Best design pattern to handle Task Results ( await / async ) - c#

I have just installed Visual Studio 2012, So I can finally test C# 5.0 features
like async/await. I was doing some testing and a doubt come to my mind.
What is the best way to handle task Results.
Given the following Snippet:
Task<List<string>> tarea = GetStringListAsync();
tarea.ContinueWith((x) =>
{
if (x.Status == TaskStatus.RanToCompletion)
{
x.Result.ForEach((y) => Console.WriteLine(y));
}
else if (x.Status == TaskStatus.Faulted)
{
Console.WriteLine(x.Exception.InnerException.Message);
}
});
private static async Task<List<string>> GetStringListAsync()
{
return await Task.Run(() =>
{
return GetStringList();
});
}
private static List<string> GetStringList()
{
//I uncomment this to get forced exception
//throw new Exception("Error Occurred");
//Add some delay
System.Threading.Thread.Sleep(12000);
return new List<string>() { "String1", "String2", "String3" };
}
I am handling the task Result in ContinueWith , but I would like to know if there is a better aproach.

Use await instead of ContinueWith or Result:
try
{
List<string> area = await GetStringListAsync();
area.ForEach((y) => Console.WriteLine(y));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
As a side note, you should usually not wrap synchronous methods (GetStringList) with a fake-asynchronous methods (e.g., using Task.Run). Let the caller decide if they want to push it to a background thread:
try
{
List<string> area = await Task.Run(() => GetStringList());
area.ForEach((y) => Console.WriteLine(y));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

Related

Using Parallel ForEach to POST in API and single Handle each Response

I would like to know if the code I produced is good practice and does not produce leaks, I have more than 7000 objects Participant which I will push individually and Handle the Response to save the "external" id in our database. First I use the Parallel ForEach on the list pPartcipant:
Parallel.ForEach(pParticipant, participant =>
{
try
{
//Create
if (participant.id == null)
{
ExecuteRequestCreate(res, participant);
}
else
{//Update
ExecuteRequestUpdate(res, participant);
}
}
catch (Exception ex)
{
LogHelper.Log("Fail Parallel ", ex);
}
});
Then I do a classic (not async request), but after I need to "handle" the response (print in the console, save in a text file in async mode, and Update in my database)
private async void ExecuteRequestCreate(Uri pRes, ParticipantDo pParticipant)
{
try
{
var request = SetRequest(pParticipant);
//lTaskAll.Add(Task.Run(() => { ExecuteAll(request, pRes, pParticipant); }));
//Task.Run(() => ExecuteAll(request, pRes, pParticipant));
var result = RestExecute(request, pRes);
await HandleResult(result, pParticipant);
//lTaskHandle.Add(Task.Run(() => { HandleResult(result, pParticipant); }));
}
catch (Exception e)
{
lTaskLog.Add(LogHelper.Log(e.Message + " " + e.InnerException));
}
}
Should I run a new task for handeling the result (as commented) ? Will it improve the performance ?
In comment you can see that I created a list of tasks so I can wait all at the end (tasklog is all my task to write in a textfile) :
int nbtask = lTaskHandle.Count;
try
{
Task.WhenAll(lTaskHandle).Wait();
Task.WhenAll(lTaskLog).Wait();
}
catch (Exception ex)
{
LogHelper.Log("Fail on API calls tasks", ex);
}
I don't have any interface it is a console program.
I would like to know if the code I produced is good practice
No; you should avoid async void and also avoid Parallel for async work.
Here's a similar top-level method that uses asynchronous concurrency instead of Parallel:
var tasks = pParticipant
.Select(participant =>
{
try
{
//Create
if (participant.id == null)
{
await ExecuteRequestCreateAsync(res, participant);
}
else
{//Update
await ExecuteRequestUpdateAsync(res, participant);
}
}
catch (Exception ex)
{
LogHelper.Log("Fail Parallel ", ex);
}
})
.ToList();
await Task.WhenAll(tasks);
And your work methods should be async Task instead of async void:
private async Task ExecuteRequestCreateAsync(Uri pRes, ParticipantDo pParticipant)
{
try
{
var request = SetRequest(pParticipant);
var result = await RestExecuteAsync(request, pRes);
await HandleResult(result, pParticipant);
}
catch (Exception e)
{
LogHelper.Log(e.Message + " " + e.InnerException);
}
}

Exception not caught in Task.Run

I'm trying to catch an exception thrown by a method (GetMoreCodes) run by a Task, but when debugging the exception is never handled and the catch block is never hit. Tried different techniques (in particular with/without await). This code is in an (async) button event handler.
try
{
// Task.Run(() => GetMoreCodes(CodeBufferMaxSize));
// await Task.Run(() => GetMoreCodes(0));
await Task.Run(() => { throw new Exception("test!"); });
}
catch (Exception ex)
{
Console.WriteLine("Exception caught: " + ex);
}
This looks to me like most examples I saw here and in blogs, in particular in this one (http://blog.stephencleary.com/ - big thanks #stephen-cleary).
For now, I only want the application not to crash and log an error if any.
Am I missing something?
await Task.Run(() => {
try
{
throw new Exception("test!");
}
catch (Exception ex)
{
Console.WriteLine("Catched");
}
});
Why not just catched in the Task.Run() ?
If you want to catch it in Console.Application with your code you can do it like this. But t.Wait() is blocking the UI Thread and waits for the Task to finish.
public static void Main(string[] args)
{
try
{
Task t = TestError();
t.Wait();
}
catch (Exception ex)
{
Console.WriteLine("Catched");
}
Console.ReadLine();
}
public static async Task TestError()
{
// Task.Run(() => GetMoreCodes(CodeBufferMaxSize));
// await Task.Run(() => GetMoreCodes(0));
await Task.Run(() => { throw new Exception("test!"); });
}

Does This Asp.Net WebAPI controller method get swapped out of app pool correctly?

I've got a Post method in my webapi 2 controller that does an insert into a database but often has to retry for many seconds before it succeeds. Basically, that causes a lot of sleeps between the retries. Effectively, it is running the code below. My question is, is this code correct so that I can have thousands of these running at the same time and not using up my iis page pool?
public async Task<HttpResponseMessage> Post()
{
try
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
Thread.Sleep(30000);
//InsertInTable(json);
}
catch (Exception ex)
{
throw ex;
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
* Added By Peter As To Try Stephens's suggestion of Await.Delay. Shows error that can not put await in catch.
public async Task<HttpResponseMessage> PostXXX()
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
bool success = false;
int retriesMax = 30;
int retries = retriesMax;
while (retries > 0)
{
try
{
// DO ADO.NET EXECUTE THAT MAY FAIL AND NEED RETRY
retries = 0;
}
catch (SqlException exception)
{
// exception is a deadlock
if (exception.Number == 1205)
{
await Task.Delay(1000);
retries--;
}
// exception is not a deadlock
else
{
throw;
}
}
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
* More Added By Peter, Trying Enterprise block, missing class (StorageTransientErrorDetectionStrategy class not found)
public async Task<HttpResponseMessage> Post()
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2));
var retryPolicy =
new RetryPolicy<StorageTransientErrorDetectionStrategy>(retryStrategy);
try
{
// Do some work that may result in a transient fault.
retryPolicy.ExecuteAction(
() =>
{
// Call a method that uses Windows Azure storage and which may
// throw a transient exception.
Thread.Sleep(10000);
});
}
catch (Exception)
{
// All the retries failed.
}
*****Code that causes SqlServer to spin out of control with open connections
try
{
await retryPolicy.ExecuteAsync(
async () =>
{
// this opens a SqlServer Connection and Transaction.
// if fails, rolls back and rethrows exception so
// if deadlock, this retry loop will handle correctly
// (caused sqlserver to spin out of control with open
// connections so replacing with simple call and
// letting sendgrid retry non 200 returns)
await InsertBatchWithTransaction(sendGridRecordList);
});
}
catch (Exception)
{
Utils.Log4NetSimple("SendGridController:POST Retries all failed");
}
and the async insert code (with some ...'s)
private static async Task
InsertBatchWithTransaction(List<SendGridRecord> sendGridRecordList)
{
using (
var sqlConnection =
new SqlConnection(...))
{
await sqlConnection.OpenAsync();
const string sqlInsert =
#"INSERT INTO SendGridEvent...
using (SqlTransaction transaction =
sqlConnection.BeginTransaction("SampleTransaction"))
{
using (var sqlCommand = new SqlCommand(sqlInsert, sqlConnection))
{
sqlCommand.Parameters.Add("EventName", SqlDbType.VarChar);
sqlCommand.Transaction = transaction;
try
{
foreach (var sendGridRecord in sendGridRecordList)
{
sqlCommand.Parameters["EventName"].Value =
GetWithMaxLen(sendGridRecord.EventName, 60);
await sqlCommand.ExecuteNonQueryAsync();
}
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
}
}
}
}
No. At the very least, you want to replace Thread.Sleep with await Task.Delay. Thread.Sleep will block a thread pool thread in that request context, doing nothing. Using await allows that thread to return to the thread pool to be used for other requests.
You might also want to consider the Transient Fault Handling Application Block.
Update: You can't use await in a catch block; this is a limitation of the C# language in VS2013 (the next version will likely allow this, as I note on my blog). So for now, you have to do something like this:
private async Task RetryAsync(Func<Task> action, int retries = 30)
{
while (retries > 0)
{
try
{
await action();
return;
}
catch (SqlException exception)
{
// exception is not a deadlock
if (exception.Number != 1205)
throw;
}
await Task.Delay(1000);
retries--;
}
throw new Exception("Retry count exceeded");
}
To use the Transient Fault Handling Application Block, first you define what errors are "transient" (should be retried). According to your example code, you only want to retry when there's a SQL deadlock exception:
private sealed class DatabaseDeadlockTransientErrorDetectionStrategy : ITransientErrorDetectionStrategy
{
public bool IsTransient(Exception ex)
{
var sqlException = ex as SqlException;
if (sqlException == null)
return false;
return sqlException.Number == 1205;
}
public static readonly DatabaseDeadlockTransientErrorDetectionStrategy Instance = new DatabaseDeadlockTransientErrorDetectionStrategy();
}
Then you can use it as such:
private static async Task RetryAsync()
{
var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2));
var retryPolicy = new RetryPolicy(DatabaseDeadlockTransientErrorDetectionStrategy.Instance, retryStrategy);
try
{
// Do some work that may result in a transient fault.
await retryPolicy.ExecuteAsync(
async () =>
{
// TODO: replace with async ADO.NET calls.
await Task.Delay(1000);
});
}
catch (Exception)
{
// All the retries failed.
}
}

How can I catch an exception that occurs in Tasks when using Task.WhenAll()?

I tried putting the try catch around the Task.WhenAll(tasks) but it did not catch anything. In one of my tasks I tried to artificially generate an exception by using Substring on an empty string. But all that happens is the app crashes and defaults to Application_UnhandledException.
private async void RunTasks()
{
tasks[0] = HttpExtensions.GetMyData("http://www....");
tasks[1] = HttpExtensions.GetMyData("http://www.....");
tasks[2] = GeoLocate.OneShotGeoLocate();
try
{
await Task.WhenAll(tasks);
}
catch (AggregateException ae)
{
App.ViewModel.ErrorMessage = ae.Message;
}
}
To get all exceptions thrown from execution of the tasks, use the following:
Task taskReturned = Task.WhenAll(taskArray);
try
{
await taskReturned;
}
catch
{
throw taskReturned.Exception;
}
This worked thanks to 'nakiya'
private async void RunTasks()
{
tasks[0] = HttpExtensions.GetMyData("http://www....");
tasks[1] = HttpExtensions.GetMyData("http://www.....");
tasks[2] = GeoLocate.OneShotGeoLocate();
try
{
await Task.WhenAll(tasks);
}
catch (Exception ae)
{
App.ViewModel.ErrorMessage = ae.Message;
}
}

Better way to show error messages in async methods

The fact that we can't use the await keyword in catch blocks makes it quite awkward to show error messages from async methods in WinRT, since the MessageDialog API is asynchronous. Ideally I would like be able to write this:
private async Task DoSomethingAsync()
{
try
{
// Some code that can throw an exception
...
}
catch (Exception ex)
{
var dialog = new MessageDialog("Something went wrong!");
await dialog.ShowAsync();
}
}
But instead I have to write it like this:
private async Task DoSomethingAsync()
{
bool error = false;
try
{
// Some code that can throw an exception
...
}
catch (Exception ex)
{
error = true;
}
if (error)
{
var dialog = new MessageDialog("Something went wrong!");
await dialog.ShowAsync();
}
}
All methods that need to do this have to follow a similar pattern, which I really don't like, because it reduces the code readability.
Is there a better way to handle this?
EDIT: I came up with this (which is similar to what svick suggested in his comments):
static class Async
{
public static async Task Try(Func<Task> asyncAction)
{
await asyncAction();
}
public static async Task Catch<TException>(this Task task, Func<TException, Task> handleExceptionAsync, bool rethrow = false)
where TException : Exception
{
TException exception = null;
try
{
await task;
}
catch (TException ex)
{
exception = ex;
}
if (exception != null)
{
await handleExceptionAsync(exception);
if (rethrow)
ExceptionDispatchInfo.Capture(exception).Throw();
}
}
}
Usage:
private async Task DoSomethingAsync()
{
await Async.Try(async () =>
{
// Some code that can throw an exception
...
})
.Catch<Exception>(async ex =>
{
var dialog = new MessageDialog("Something went wrong!");
await dialog.ShowAsync();
});
}
.Catch<...> calls can be chained to mimick multiple catch blocks.
But I'm not really happy with this solution; the syntax is even more awkward than before...
you already have that functionality in TPL
await Task.Run(async () =>
{
// Some code that can throw an exception
...
}).ContinueWith(async (a) =>
{
if (a.IsFaulted)
{
var dialog = new MessageDialog("Something went wrong!\nError: "
+ a.Exception.Message);
await dialog.ShowAsync();
}
else
{
var dialog2 = new MessageDialog("Everything is OK: " + a.Result);
await dialog2.ShowAsync();
}
}).Unwrap();
In this machine I don't have Windows 8 so I tested in Windows 7 but I think is the same.
*Edit
as stated in the comments its needed .Unwrap(); in the end for the await to work
C# 6 now supports await in catch and finally, so the code can be written the way I wanted it; a workaround is no longer needed.

Categories