Reputation: 33
I have a service I need to connect to that passes data back through an action like this:
public Guid UpdateEntities<T>(Action<EntitiesChangedResponse<T>> onResponse, IEnumerable<T> entities)
{
//Get some data
onResponse.Invoke(response);
{
Existing code would call the service as follows:
Guid requestId = _productService.UpdateEntities<Product>(x => OnEntitiesUpdated(x), new List<Product> { updateProduct1, updateProduct2 });
And the callback would do something with the result at some point in the future:
private void OnEntitiesUpdated<T>(EntitiesChangedResponse<T> response)
{
//Do something with the result
}
I'm try to integrate it with a task based signal R hub so need to return the operation as a typed task, but I can't for the life of me figure out how to achieve this (I'm quite new to tasks so please tell me if this is daft).
It would look something like this:
public Task<EntitiesChangedResponse<Product>> UpdateProducts(List<Product> products)
{
//Somehow wrap this in a task
Task<EntitiesChangedResponse<Product>> result = New Task<EntitiesChangedResponse<Product>>( call the product service );
return result;
}
Any help appreciated. It is hurting my head.
Upvotes: 2
Views: 656
Reputation: 2285
Let's have a look at the following method:
public Task<EntitiesChangedResponse<T>> UpdateEntities<T>(IEnumerable<T> entities)
{
var updateTask = Task.Run(()=>
{
//return data from this lambda expression
});
return updateTask;
}
This starts and returns a task which does your work.
(If you really need the GUID you can return a Tuple or any DTO containing both).
You now have a couple of options to consume this task, here's one:
private Task async UpdateEntitiesAsync<T>(IEnumerable<T> entities)
{
try
{
EntitiesChangedResponse<T> response = await UpdateEntities(entities);
//asynchronous callback implementation code can go here
}
catch(AggregateException ex)
{
//handle a thrown exception
}
}
Do note:
1)The execution continues past the 'await' statement once the task completes and will execute the following LOC.
2) if the task faults (throws an exception) the LOC past the await block will not execute and the catch clause will execute instead.
3) Calling the UpdateEntitiesAsync method does not block the calling thread. the execution immediately returns to the caller once the await statement is hit.
4) You can await the task returned from the async method as well since it (implicitly) returns a task which finishes once this method executes in full, making this method awaitable in itself, if such a need arises.
Alternatively, you can have this method return void, if you do not really care about when it completes.
5) If you do not catch the exception in this method, know that the task returned from the async method will fault, and must be handled somewhere up the invocation chain.
You can do this by either awaiting the task, accessing the Exception property, or invoking the Wait method of the task.
6) The Async postfix in the method's name is merely a naming convention, applied so that client code is aware of the method's asynchronous nature.
Upvotes: 1
Reputation: 37938
To make a bridge between "callback" API and task based API you can use TaskCompletionSource
public Task<EntitiesChangedResponse<Product>> UpdateProducts(List<Product> products)
{
var tcs = new TaskCompletionSource<EntitiesChangedResponse<Product>>();
_productService.UpdateEntities<Product>(response => tcs.SetResult(response), new List<Product> { updateProduct1, updateProduct2 });
return tcs.Task;
}
Upvotes: 2