We never used async / await (or something like that) in our small project. But now the project is growing, and we decided to add asynchrony.

The project has the following architecture:

  • Domain (repository, EF)
  • UnitTests
  • Service (new, it is planned to transfer business logic from controllers)
  • WebUI

My question is, in which layer should we implement asynchrony? Or should we do it on several layers? Please tell me where the implementation will be most appropriate?

For example, I have a method in the controller

public virtual HttpResponseMessage Get() { var entity = Repository.GetAll(); if (entity != null && entity.Any()) { return Request.CreateResponse(HttpStatusCode.OK, entity); } var message = $"{GenericTypeName}: No content"; return ErrorMsg(HttpStatusCode.NoContent, message); } 

How do i make it asynchronous?

  • one
    You have one operation Repository.GetAll (); you have essentially nothing to do asynchronous. - cpp_user
  • @cpp_user oh whether? And if 1000 users at the same time come? - andreycha
  • @andreycha: The number of users is already a problem of the web server level and asynchrony is a weak helper here - more successful solutions are webshakes or distributed requests to the clouds. - cpp_user

2 answers 2

 class Repository { public Task<entity> GetAllAsync(CancellationToken cancellationToken) { return Task.Run<entity>(() => { // тут что-то ресурсоёмкое вычисляем return entity; // возвращаем результат }); } } 

The second example, the same with async await

 class Repository { public async Task<entity> GetAllAsync(CancellationToken cancellationToken) { // вернёт управление в вызывающий код await Task.Yield(); // тут что-то ресурсоёмкое вычисляем return entity; // возвращаем результат } } 

The third example, if you need to calculate something in parallel before returning the data

 class Repository { public async Task<entity> GetAllAsync(CancellationToken cancellationToken) { var tasks = new List<Task>(); tasks.Add(Task.Run<entity>(() => { // тут что-то ресурсоёмкое вычисляем return entity; // возвращаем результат })); tasks.Add(Task.Run<entity>(() => { // тут что-то ресурсоёмкое вычисляем return entity; // возвращаем результат })); tasks.Add(Task.Run<entity>(() => { // тут что-то ресурсоёмкое вычисляем return entity; // возвращаем результат })); // дожидаемся окончания всех задач await Task.WhenAll(tasks); // дальше пробегаемся по списку задач, получаем результат работы каждой и что-то с ним делаем. И возвращаем уже вычисленный нами результат. } } 

Of course, in a real combat application, the code will be slightly different and depend on a specific task, but the principle will be the same.

I note that async/await is a syntax wrapper over Task.Run with some differences. And the differences in exception handling in the process of performing the task.

But before writing an asynchronous code, be sure to read about async/await And the task otherwise there will be a lot of errors. Especially carefully you need to look at the exception handling.

  • one
    Be careful - await Task.Yield(); although it returns control to the calling code — it still saves the request context! Sometimes this is useful, but it is not at all the same as performing a task in the background thread. - Pavel Mayorov

Asynchrony applies to all layers. From the bottom to the top. In your case, if Domain is asynchronous then WebUI will be asynchronous. Do not forget that asynchrony is needed only for operations with peripheral devices (disk, network ...) so that, while waiting for a response, the workflow is not idle.

 class Repository { public Task<entity> GetAllAsync(CancellationToken cancellationToken) { using (var db = new DataContext()) { return db.EntitySet.ToListAsync(); } } } public virtual async Task<IHttpActionResult> Get(CancellationToken cancellationToken) { var entity = await Repository.GetAllAsync(cancellationToken); if (!entity.Any()) { var message = $"{GenericTypeName}: No content"; return Content(HttpStatusCode.NoContent, message); } return Ok(entity); } 

Please note http status code 204 - No content (this is not an error code status)

For reference: https://ru.wikipedia.org/wiki/HTTP_stat_list_HTTP_List

And in general, you first need to read about Task and async await. Task.Run is used for cpu bound operations (Computational processor operations) async await to wait for the result of these operations in the calling code or to wait for I / O bound operations.

As for the entity framework, in version 6 there are asynchronous methods; their names end with Async, for example .ToListAsync ().

  • 3
    "Asynchrony is needed only for operations with peripheral devices" - and when processing large amounts of data. - Stack
  • 2
    And with massive calculations. In general, it makes sense to add asynchrony wherever tangible “simple” is possible, awaiting the results of a method call. But be careful with asynchrony. For firstly, it makes the program behavior more non-linear. This can lead to very specific bugs. And secondly, not every method that can be made asynchronous needs to be made asynchronous. Often there is no gain from asynchronous. - Alexey
  • Thank you very much for the answer. And how do I implement GetAllAsync() ? via Task.Run? - Roman Marusyk
  • @Evgeniy And how do I implement GetAllAsync ()? via Task.Run? - Roman Marusyk
  • one
    @MegaTron If your underlying stack is not asynchronous below, you do not need to fence something with Task.Run, but if you really want it, then the answer added below. - Eugene