We're in the process of refactoring all, or a large portion of our .NET 4.6 MVC WebAPI controller methods to async
methods.
This seems like it will work well for methods that have a lower level invocation of a method that is awaitable such as SQL command execution; however we are utilizing an in-memory distributed caching framework by Alachisoft called NCache (4.6 SP2 to be exact), which does not offer any truly asynchronous methods.
Would it be worth creating an async
helper method that would expose a awaitable Task<object>
return type?
Traditionally using the NCache API, you Get
an object from cache by a Key
in the following usage;
NCacheObject.Get(string);
The proposal is to create a helper method of the following;
protected static async Task<Object> GetAsync(string key, Cache nCache)
{
Task<Object> getTask = new Task<Object>(() => nCache.Get(key));
getTask.Start();
return await getTask.ConfigureAwait(false);
}
So that it would then allow full waterfall of async
methods up to the entry controller method as such;
public static async Task<Tuple<List<SomeModel>, bool, string>> GetSomeModelList(string apiKey)
{
return newTuple<List<SomeModel>, bool, string>(await GetAsync("GetSomeModelKey", CacheInstance).ConfigureAwait(false), true, "Success message");
}
And finally the controller method;
[HttpGet, Route("Route/Method")]
public async Task<ResponseOutputModel<List<SomeModel>>> GetSomeModelList()
{
ResponseOutputModel<List<SomeModel>> resp = new ResponseOutputModel<List<SomeModel>>();
try
{
Tuple<List<SomeModel>, Boolean, String> asyncResp = await CacheProcessing.GetSomeModelList(GetApiKey()).ConfigureAwait(false);
resp.Response = asyncResp.Item1;
resp.Success = asyncResp.Item2;
resp.Message = asyncResp.Item3;
}
catch (Exception ex)
{
LoggingHelper.Write(ex);
resp.StatusCode = Enumerations.ApiResponseStatusCodes.ProcessingError;
resp.Success = false;
resp.Message = ex.Message;
}
return resp;
}
This would complicate the refactoring, as the original methods actually had output parameters for bool success
and string message
; but it seems this can be accomplished in a decent and quick manner using a Tuple<>
; otherwise we could just create a return type model.
To do this properly, there would be many hundreds of methods to refactor; quite an undertaking.
Would it be worth creating an async helper method that would expose a awaitable Task return type?
Nope.
The proposal is to create a helper method of the following
This just queues in-memory work to the thread pool.
(Side note: The task constructor should never be used. Ever. If you need to queue work to the thread pool, use Task.Run
instead of the task constructor with Start
)
Would this work as expected, and be the best solution to accomplish the objective?
Is it likely worth all of the effort required, with the end goal of increasing scalability and subsequently "performance" of the web servers?
These are the same question. The objective is scalability; asynchrony is just one means to help you accomplish it.
Asynchrony assists scalability on ASP.NET because it frees up a thread that can process other requests. However, if you make methods that are asynchronous by using another thread, then that doesn't assist you at all. I call this "fake asynchrony" - these kinds of methods look asynchronous but they're really just synchronously running on the thread pool.
In contrast to true asynchrony, fake asynchrony will actually hurt your scalability.