0

My MVC application consumes a library and some methods of this library call WCF service internally. All methods exposed from this DLL are sync (none of them return Task or Task) and since we don't own that assembly, it is not possible to convert them into Async API.

However, because these methods call WCF service, they are network bound (so ideally they should be async).

I want to use async controller actions in my MVC application to make it more scalable. My question is how to make the entire method pipeline await able when one method is sync in nature.

Async action --> await async method --> await async method 2 --> sync method from library?

Should I use TaskCompletionSource or Task.FromResult to wrap the library method call?

Also, if I use above approach, will my code more scalable than sync version?

1
  • 2
    Wrapping sync methods in async does nothing for scalability. It just moves execution onto another thread. This may increase responsiveness if that thread has important things to do (like the UI thread). See Steven Toub's post: blogs.msdn.com/b/pfxteam/archive/2012/03/24/10287244.aspx Commented Feb 1, 2014 at 8:49

2 Answers 2

5

My question is how to make the entire method pipeline await able when one method is sync in nature.

You can't. The only solution is to rewrite the dll.

Should I use TaskCompletionSource or Task.FromResult to wrap the library method call?

Neither.

Also, if I use above approach, will my code more scalable than sync version?

No. It will be slightly less scalable.

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks Stephen. However I was reading one of your blog and you mention that 'there is a way to partially convert your method stack in async' and in my opinion I am in that situation. Knowing that the sync API makes network bound call, I can't resist myself to call it in async way, but since it's a server application Task.Run is no-no. It seems like .NET should have a better solution for this kind of scenario which is preventing me to take the benefits of async just because one method in the stack is sync and I don't have control over it. :(
To add to this, based on my knowledge (that I learnt by reading most of your blogs :) ), regardless of how many await able methods I have before this sync method call, which is wrapped using TCS , eventually the request thread will end up executing the sync method and will be blocked. Which diminish the purpose of creating the entire call stack await able starting from controller action.
It seems like .NET should have a better solution for this kind of scenario... The question is: how would it work?
In ASP.NET, your best option is to just call the synchronous method directly. There's no point in using Task.FromResult - you're just causing more overhead. You could wrap the synchronous method in Task.Run to do multiple things concurrently (making individual requests faster); however, that can easily tank the scalability of your server as a whole since each request uses multiple threads.
Yes, Task.Run is an acceptable workaround in UI apps because the goal is different: to prevent the UI thread from blocking. In your ASP.NET solution, if you have an async operation to do in addition to the blocking one, you can start it (var task = OpAsync();), then do your blocking part (Blocking();), and then await the async part (await task;). This allows some benefit from async at least. This does cause an unusual situation though: your calling code has an async signature but does blocking - so I recommend clear documentation whenever you have to do this.
|
0

TaskCompletionSource<T> is a way to create a puppet Task, which can complete at any point you like, and can make it fault at any point you like. This means, this would ideal in your case since you have no control over the API method, which you are trying to consume. Following example will give you a head start.

public class HomeController : Controller
 {
    public async Task<ActionResult> Index()
    {
    ViewBag.Message = await ProcessRequest();

    return View();
    }


    //TResult -> can be of any built-in or custom type that you should decide.
    Task<TResult> ProcessRequest()
    {

    // Make a TaskCompletionSource so we can return a puppet Task
    TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();

    // Call your sync API method
    SyncAPI syncApi = new SyncAPI();

    // Call api method and set the result or exception based on the output from the API //method.
    tcs.SetResult(TResult);

    // Return the puppet Task, which isn't completed yet
    return tcs.Task;
    }
}

1 Comment

Thanks Nair. That's what I have implemented currently. However if you notice how async calls are executed, you will realize that eventually the puppet method that wraps the sync API call will be executed on the request thread and will not free it to take more user requests. So regardless of how many await able methods you have above it, your request thread will eventually be blocked on sync API call. Correct me if I am mistaken. You might want to Response.Write the current threads managed Id in your code as a proof of concept.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.