Executing synchronous methods asynchronously

We can achieve certain advantages by writing asynchronous methods. It's pretty simple to write them in C# code: we just need to use async and await keywords properly. Grammar of these keywords can be found in detail in MSDN. In this post I will try to focus on writing few code to invoke a regular synchronous method asynchronously.

Let's assume a scenario where we need to perform an expensive database operation when a particular information is changed from the front end.

For example: I have an application to manage employee information and I've changed the basic salary for a particular grade using my application. Now I need to update salary of all the employees under that grade. The later operation will be slower one and I don't want this to block me from doing other stuffs.

Here is a piece of synchronous code to update salary information:
public bool UpdateEmployeeSalary(string grade)
{
    try
    {
        //logic for an expensive operation
        using (var newConn = new SqlConnection(GetConnectionStringFromConfig()))
        {
            newConn.Open();
            using (var sqlCommand = newConn.CreateCommand())
            {
                sqlCommand.Connection = newConn;
                sqlCommand.CommandText = "spUpdateEmployeeSalary";
                sqlCommand.CommandType = CommandType.StoredProcedure;
                sqlCommand.Parameters.AddWithValue("Grade"grade); 
                sqlCommand.CommandTimeout = 0; // operation might take longer
                sqlCommand.ExecuteNonQuery();
            }
        }
    }
    catch (Exception ex)
    {
        //log error
    }
    return true;
}

Now I will be invoking this synchronous operation within an asynchronous method so that this operation is executed asynchronously i.e. without blocking the user:
public virtual async Task<bool> SynchronizeSalaryInformationAsync(string grade)
{
    var service = ServiceFactory.Get<IEmployeeService>();
    return await Task.Run(() => service.UpdateEmployeeSalary(grade));
}

Notice that, SynchronizeSalaryInformationAsync will wait for salary data synchronization, but the control flow will immediately return to the end of the function after invoking the Task.

Now the final task is to call this  asynchronous method after regular update of grade information. Here is how we can do that:
public void SaveGrade(Grade model)
{
    // regular update operation
    UpdateGradeInformation(model);
    // asynchronous update operation
    SynchronizeSalaryInformationAsync(model.Title);
 
    // other stuffs to do...
}

So, basically SaveGrade() method will be executing in the following sequence:
  1. UpdateGradeInformation() will be executed and completed.
  2. SynchronizeSalaryInformationAsync() will start asynchronously.
  3. The rest of the code will execute regardless of completion of the async method.

But executing the last piece of code is likely to throw NullReference exception probably with the following stack trace:
System.Web.dll!System.Web.ThreadContext.AssociateWithCurrentThread(bool setImpersonationContext = {unknown})    C#
System.Web.dll!System.Web.HttpApplication.OnThreadEnterPrivate(bool setImpersonationContext = {unknown})    C#
System.Web.dll!System.Web.HttpApplication.OnThreadEnter()   C#
System.Web.dll!System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter()  C#
System.Web.dll!System.Web.Util.SynchronizationHelper.SafeWrapCallback(System.Action action = {unknown}) C#
System.Web.dll!<>c__DisplayClass9.AnonymousMethod(System.Threading.Tasks.Task _ = {unknown})    C#
mscorlib.dll!System.Threading.Tasks.ContinuationTaskFromTask.InnerInvoke()  C#
mscorlib.dll!System.Threading.Tasks.Task.Execute()  C#
mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj = {unknown})   C#
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext = {unknown}, System.Threading.ContextCallback callback = {unknown}, object state = {unknown}, bool preserveSyncCtx = {unknown})   C#
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext = {unknown}, System.Threading.ContextCallback callback = {unknown}, object state = {unknown}, bool preserveSyncCtx = {unknown})   C#
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot = {unknown})    C#
mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution = {unknown}) C#
mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() C#
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()    C#
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() C#

This is because, when an async method is executed, it attempts to return back to the parent thread in its calling context which might have switched by another user action. To avoid this we can slightly modify the code as in below:
public void SaveGrade(Grade model)
{
    // regular update operation
    UpdateGradeInformation(model);

    // asynchronous update operation
    Task.Run(async () =>
    {
        await SynchronizeSalaryInformationAsync(model.Level);
    });
}

Now we are assuring that the async method will return to its orphan parent thread and thereby will not throw any NullReference exception.

Comments

  1. Pleasant Tips..Thanks for Sharing….We keep up hands on approach at work and in the workplace, keeping our business pragmatic, which recommends we can help you with your tree clearing and pruning in an invaluable and fit way.
    python Training in Pune
    python Training in Chennai
    python Training in Bangalore

    ReplyDelete
  2. Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging.
    python Training in Pune
    python Training in Chennai
    python Training in Bangalore

    ReplyDelete
  3. I appreciate that you produced this wonderful article to help us get more knowledge about this topic. I know, it is not an easy task to write such a big article in one day, I've tried that and I've failed. But, here you are, trying the big task and finishing it off and getting good comments and ratings. That is one hell of a job done!
    Data Science course in kalyan nagar
    Data Science course in OMR
    Data Science course in chennai
    Data science course in velachery
    Data science course in jaya nagar
    Data Science interview questions and answers
    Data science course in bangalore

    ReplyDelete

Post a Comment

Popular posts from this blog

Adding security headers to prevent XSS and MiTM attacks in .Net Core

Creating transformations for custom config files

Microsoft.IdentityModel.Protocols.OpenIdConnectProtocolInvalidNonceException