Relating Task exception to a Task<T> response - c#

I have just started working with tasks. We have a system setup that uses requests/responses. The service running the tasks accepts a master request that has a list of request objects and returns a master response that has a list of response objects. So it looks something like this
var MasterRequest = new MasterRequest;
MasterRequest.Requests.Add(new BlueRequest);
MasterRequest.Requests.Add(new RedRequest);
MasterRequest.Requests.Add(new YellowRequest);
The request implements a simple IRequest interface and each color is a concrete class. The service has concrete classes (request processors) set up to be able to process each request separately and simultaneously according to a concrete request object. Each concrete class on the service has a GetTask method with a signature like this:
Task<IResponse> GetTask(IRequest);
{
// some setup stuff
return Task.Factory.StartNew<IResponse>(() =>
{
// do task stuff
return response; // implements IResponse
});
}
My service takes the passed in MasterRequest and builds a list of tasks by calling the GetTask call listed above on the concrete request processors. I then use a Parallel.ForEach on the list to process the tasks.
// this is what is returned from the service.
// it has a List<IResponse> on it to hold the resposnes
MasterResposne resposne = new MasterResponse();
List<Task<IResponse>> tasks = new List<Task<IResponse>>();
foreach(IRequest req in MasterRequest.Requests)
{
// factory to get the proper request processor
RequestProcessor p = rp.GetProcessor(req);
tasks.add(p.GetTask(req));
}
Parallel.ForEach(tasks, t =>
{
t.Wait();
// check for faulted and cancelled
// this is where I need help
response.Responses.Add(t.Result);
}
This all works great. But if the task throws an exception I don't know how to tie it back to the specific concrete request that triggered it. I need to know so I can pass back a properly built response to the caller.
My first thought was to subclass Task but that brings up it's own set of issues that I don't want to deal with.
I read this SO article and it seems like I want to do something like this
Is this ok to derive from TPL Task to return more details from method?
I think Reed's second example is my solution but I still cannot see how to run the tasks simultaneously and be able to tie exceptions to the request so I can return a properly built list of responses.
Thanks in advance.

So I was able to use Reed's solution from the link I supplied. My service code to process the requests turned into this
// this is what is returned from the service.
// it has a List<IResponse> on it to hold the resposnes
MasterResposne resposne = new MasterResponse();
List<ExecutionResult> tasks = new List<ExecutionResult>();
foreach(IRequest req in MasterRequest.Requests)
{
// factory to get the proper request processor
RequestProcessor p = rp.GetProcessor(req);
tasks.add(p.GetResult(req));
}
Parallel.ForEach(tasks, t =>
{
t.task.Wait();
response.Responses.Add(t.Result);
}
Where ExecutionResult is defined like so
class ExecutionResult
{
public IResult Result;
public Task<IResponse> task;
}
That gives me access to a pre-built response object so I can pass it back to the caller.
EDIT:
So I reviewed my Parallel.ForEach and was able to redo my code and use await Task.WhenAll as suggested. New code looks more like this:
// this is what is returned from the service.
// it has a List<IResponse> on it to hold the resposnes
MasterResposne resposne = new MasterResponse();
List<ExecutionResult> tasks = new List<ExecutionResult>();
List<ExecutionResult> executionResults = new List<ExecutionResult>();
foreach(IRequest req in MasterRequest.Requests)
{
// factory to get the proper request processor
RequestProcessor p = rp.GetProcessor(req);
ExecutionResult er = engine.GetResult(req);
executionResults.Add(er);
tasks.Add(er.Task);
}
await Task.WhenAll<IResponse>(tasks);
foreach (ExecutionResult r in executionResults)
{
if (r.Task.IsCompleted)
{
response.AddResponse(r.Task.Result);
}
else
{
r.Response.Status = false;
AggregateException flat = r.Task.Exception.Flatten();
foreach (Exception e in flat.InnerExceptions)
{
Log.ErrorFormat("Reqest [{0}] threw [{1}]", r.Response.RequestId, e);
r.Response.StatusReason.AppendLine(e.Message);
}
}
}
This allows me to tie my request information to my task and get the response back that I need to return to my caller.
Thanks for the guidance.

I then use a Parallel.ForEach on the list to process the tasks.
This is actually pretty bad. It's throwing a ton of threads into the mix just to block on the tasks completing.
But if the task throws an exception I don't know how to tie it back to the specific concrete request that triggered it. I need to know so I can pass back a properly built response to the caller.
Whenever you have a "process tasks after they complete" kind of problem, usually the best solution is a higher-level asynchronous operation:
private async Task<IResponse> ProcessAsync(IRequest request)
{
try
{
return await engine.GetResult(request);
}
catch (Exception ex)
{
IResponse result = /* create error response */;
return result;
}
}
This allows a much simpler main function:
MasterResposne resposne = new MasterResponse();
var tasks = MasterRequest.Requests.Select(req => ProcessAsync(req));
response.AddRange(await Task.WhenAll(tasks));

Related

C# don't wait task to finish, but result needed

Simply I want to make an external call to external api which has SenMessageAsync(string message) method and i need to return my client Ok() without waiting it to finish. But on the backend side I need to response of SendMessageAsync() method for continue some process. I mean somehow I need to await it.
Example
try
{
//...
var response = await SendMessageAsync("test"); //dont wait the response, return Ok()
//do something with response
}
catch(MyException ex)
{
//Log
}
catch(Exception ex)
{
//Log
}
Update:
I am updating with one solution I found. Task.Run() (which called fire and forget). Maybe it helps someone. Thanks for answers.
Solution 1:
_ = Task.Run(() => SendMessageAsync(), task =>
{
//We can have exception here if SendMessageAsync() fails
var exception = task.InnerException.Exception;
//Log the exception
}, TaskContinuationOptions.OnlyOnFaulted)
You need a basic distributed architecture, as described on my blog. In summary:
A durable queue. Serialize the work to be done into this queue (e.g., { "operation" = "SendMessage", "data" = "test" }).
A separate backend processor that reads from that queue and does the actual work (e.g., SendMessageAsync(message.data)).
Your backend processor can await the call to SendMessageAsync and then do further processing.
You can use the Task.Run method to run the SendMessageAsync method asynchronously on a separate thread. You can then return Ok() to the client immediately, while the SendMessageAsync method continues to run in the background. To wait for the result of the SendMessageAsync method, you can use the await keyword within the Task.Run method.
Here's an example:
public async Task<IActionResult> MyAction()
{
// Return Ok() immediately to the client
_ = Task.Run(async () =>
{
// Run SendMessageAsync asynchronously on a separate thread
var result = await SendMessageAsync("My message");
// Continue processing using the result of SendMessageAsync
// ...
});
return Ok();
}

Is there a way to force Task with HttpClient API response?

I have a task who want to call from the constructor class but it's really slow for executing. Is there a way to force this task?
private async Task GetExchange()
{
NewsStack.IsVisible = false;
SearchStack.IsVisible = false;
ExchangeStack.IsVisible = true;
try
{
var client = new HttpClient();
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("https://coinlore-cryptocurrency.p.rapidapi.com/api/tickers/?start=0&limit=100"),
Headers =
{
{ "x-rapidapi-host", "coinlore-cryptocurrency.p.rapidapi.com" },
{ "x-rapidapi-key", "yourAPIkey" },
},
};
using (var response = await client.SendAsync(request))
{
var exchange = new Exchange();
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
var exchangeBody = JsonConvert.DeserializeObject<Exchange>(body);
exchange = exchangeBody;
this.exchangeBodyList = new List<SearchCrypto>();
foreach (var item in exchange.CryptoExchange)
{
this.exchangeBodyList.Add(new SearchCrypto()
{
Name = item.Name,
Symbol = item.Symbol
});
}
this.exchangeTest = exchange;
lstExchange.ItemsSource = exchangeBody.CryptoExchange;
}
dateTimeRefresh.Text = "Last Update: " + DateTime.Now.ToString("HH:mm:ss");
}
catch (Exception ex)
{
await DisplayAlert("Alert", "Please, check your internet connection.", "OK");
}
}
I call this task in constructor like that:
Task.Run(() => this.GetExchange()).Wait();
I'm not sure if there's a way to force it in another way.
Also I accepting tips or examples for code optimization.
In general, asynchronous work is a poor fit for constructors. Ideally, constructors should be short and fast and do almost nothing - setting some member variables, perhaps doing some argument validation, that's about it.
Instead of trying to cram I/O into a constructor, consider using a factory pattern. So you create a factory, which can then create an instance of the type you want using an asynchronous method like async Task<MyType> CreateAsync(). CreateAsync can then call GetExchange naturally (i.e., asynchronously) and pass exchangeBodyList and exchangeTest into the constructor.
What point are you trying to accomplish by forcing the API call to finish? Just like most things, the server will give a response when it's performed all it's operations, not before. The only way to force the result early is to close the connection and not wait for an answer. If you just want it to speed up and finish quicker, then you'll need to speed up the server side code and any DB calls.
Just like in any program, there's no way to force code to run faster. You can't make the computer run faster. You can force it to run a thread at a higher priority, but I'm pretty sure that's not going to make much speed difference and it's probably not the format you need the code to run in.
Speeding up code isn't really on topic here, unless you have an actual, specific error or issue you want to fix, but a general "speed up my code" doesn't work here. It might be on topic on Code Review, maybe, but not here.

Wait for another method to invoke and then continue with result

I am trying to invoke a method from another .dll file .
It is sending a message through the VPN then Return the RecievedMessage from another computer.
As you now it takes time to sending and receiving message and VpnObject just send message and I should wait for listener to invoke the RecievedMessage.
This method is like this!
public string RecievedMessage()
{
string Recieved ;
// Some VPN Code and then return the result;
return Recieved;
}
public string SendAndRecieveMessage(string MessageToSend)
{
string RecievedAnswer = string.Empty;
// Now Sending Message through the VPN
VpnObject.SendMessage(MessageToSend);
//Then want to Recieve the answer and return the answer here .
return RecievedAnswer;
}
I'm just thinking how can wait for RecievedMessage to invoke then return the result .
You know it is simple to use a variable and assign it value and check for while but it reduced the performance dramatically .
Is there anyway to continue from SendAndRecieveMessage just when RecievedMessage invoked ? (I think it is something with async and await but don't know how!)
Edit :VpnObject is just a sender and receiver through the vpn . it contains a simple socket send and a listener that invoke a method(RecievedMessage) when new message received .
Whether or not you have an alternative to polling depends on whether the library you are using provides any events or callbacks that will tell you when the request has completed.
Either way, the standard approach to exposing the deferred result of an asynchronous operation is to use a Task. Your method signature would look like this:
public Task<string> SendAndRecieveMessage(string MessageToSend)
Now, how you actually implement the method depends on what API VpnObject exposes. TaskCompletionSource is very useful for this kind of thing.
If VpnObject has an event that fires when the request completes:
public Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
VpnObject.OnMessageReceived += (s, e) => tcs.SetResult(e.Message);
...
return tcs.Task;
}
If VpnObject can accept a callback that it will invoke when the request completes:
public Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
VpnObject.OnMessageReceived(message => tcs.SetResult(message));
...
return tcs.Task;
}
If VpnObject doesn't support any of this, you can fall back to polling:
public async Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
while(!VpnObject.IsMessageReceived)
await Task.Delay(500); // Adjust to a reasonable polling interval
...
return VpnObject.Message;
}
You know it is simple to use a variable and assign it value and check for while but it reduced the performance dramatically .
A spin while loop is definitely not the way to implement this. Even with a sleep, it's clunky, and C# has multiple ways to solve this problem.
It's not entirely clear how your VPN Send and Receive method works, but the idea for solving this is to either use a callback approach, or as you noted, use C# async framework.
Without more details on the VPN Object, I'll just have to have some stub methods. The idea is to create a Task that returns the string, mark it as an async task, then await for it to complete. In your case, the task is receiving the VPN response string.
Something like this.
public Task<string> ReceivedMessage()
{
//get the response from the VPN Object.
string Received = VpnObject.GetResponse();
var ts = new TaskCompletionSource<string>();
ts.SetResult(Received);
// Some VPN Code and then return the result;
return ts.Task;
}
public async Task<string> SendAndReceiveMessageAsync(string MessageToSend)
{
string result = string.Empty;
// Now Sending Message through the VPN
VpnObject.SendMessage(MessageToSend);
result = await ReceivedMessage();
return result;
}

How do I spawn a task that will run to completion and immediately return to the client?

So I'm trying to create a proof of concept for server-side asynch using HTTP 202 codes (where the server accepts the task, immediately returns an endpoint to poll and then creates/updates the resource)
Rick Strahl has a description of how to do this in ordinary ASP.NET. That technique depends on being able to Response.End and then continue to execute code. The Response object doesn't even seem to be available in a Web API controller's context.
If the following worked as planned, it would not block returning the http 202 and still guarantee that the database task will run to completion.
//Insert or Update Asych.
public Task<HttpResponseMessage> Post(bool asynch, [FromBody]DatedValue value) //Insert Value
{
Guid key = Guid.NewGuid();
//Want this to run to completion, even if response associated with parent thread is done.
Task toWait = Task.Factory.StartNew(() =>
{
queue.Add(key, 0);
DatedValue justCreated = Insert(value);
queue[key] = justCreated.Id;
});
//Return address to resource just created.
Task<HttpResponseMessage> sender = Task.Factory.StartNew(() =>
{
HttpResponseMessage message = Request.CreateResponse(HttpStatusCode.Accepted);
message.Headers.Location = new Uri("/ValueQueue/" + key);
return message;
});
Task.WaitAll((new[] { toWait, sender }));
return sender;
}
Task.WaitAll blocks execution and response is not returned until both the tasks are completed. If you change your code something like below, you should be able to return the response while the task is being run.
public HttpResponseMessage Post(bool asynch, [FromBody]DatedValue value)
{
Guid key = Guid.NewGuid();
Task.Factory.StartNew(() =>
{
queue.Add(key, 0);
DatedValue justCreated = Insert(value);
queue[key] = justCreated.Id;
});
HttpResponseMessage message = Request.CreateResponse(HttpStatusCode.Accepted);
message.Headers.Location = new Uri("/ValueQueue/" + key);
return message;
}
However, you should be aware of a problem with an approach like this. If you are hosting Web API on IIS, your worker process can get recycled while your task is running. Since you have already returned the response, as far as ASP.NET is concerned, it has completed its work. So, if for some reason IIS decides to recycle the worker process, it will go ahead regardless of where your task is in terms of execution and hence you can end up with corrupt data.
With .NET 4.5.2, you can use QueueBackgroundWorkItem. Read this - http://blogs.msdn.com/b/webdev/archive/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-long-background-process-in-asp-net.aspx.

How to answer a request but continue processing code in WebApi

I would like to answer a request, but continue processing code.
I tried something like:
[HttpPost]
public async Task<HttpResponseMessage> SendAsync(MyRequest sms)
{
await Task.Run(() => Process(sms)); //need to run in a separate thread
var response = new MyRequest(sms) { Ack = true };
return Request.CreateResponse(HttpStatusCode.Created, response.ToString());
}
private async void Process(MyRequest sms)
{
var validationResult = new MyRequestValidation(_p2pContext, _carrierService).Validate(sms);
if (string.IsNullOrWhiteSpace(validationResult.Errors[0].PropertyName)) // Request not valid
return;
Message msg;
if (validationResult.IsValid)
{
msg = await _messageService.ProcessAsync(sms);
}
else // Create message as finished
{
msg = _messageService.MessageFromMyRequest(sms,
finished: true,
withEventSource: validationResult.Errors[0].CustomState.ToString()
);
}
// Salve in db
_p2pContext.MessageRepository.Create(msg);
_p2pContext.Save();
}
I would like to answer a request, but continue processing code.
Are you sure you want to do this in ASP.NET? This is not a situation that ASP.NET (or any web server) was designed to handle.
The classic (correct) way to do this is to queue the work to a persistent queue and have a separate backend process do the actual processing of that work.
There are all kinds of dangers with doing processing inside of ASP.NET outside of a request context. In general, you can't assume that the work will ever actually be done. If you're OK with that (or just like to live dangerously), then you can use HostingEnvironment.QueueBackgroundWorkItem.
I have a blog post that goes into more detail.

Categories

Resources