Страницы

Поиск по вопросам

вторник, 10 декабря 2019 г.

Где правильно использовать async/await в WebAPI

#c_sharp #архитектура #aspnet_web_api #async_await


Мы никогда не использовали async/await (или что-то подобное) в нашем небольшом проекте.
Но сейчас проект растет, и мы решили добавить асинхронность.

Проект имеет следующую архитектуру:


Domain (репозиторий, EF)
UnitTests 
Service (новый, тут планируется перенести бизнес логику с контроллеров)
WebUI


Мой вопрос, в каком слое мы должны реализовать асинхронность? Или мы должны сделать
это на нескольких слоях? Пожалуйста, подскажите мне, где реализация будет наиболее
подходящей?

Например у меня есть метод в контроллере

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);
}


Как мне сделать его асинхронным?
    


Ответы

Ответ 1



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

Ответ 2



Асинхронность распространяется на все слои. От нижнего к верхнему. В вашем случае, если Domain асинхронный то и WebUI будет асинхронным. Не забывайте, что асинхронность нужна только для операций с периферийными устройствами (диск, сеть...) для того, что бы при ожидании ответа не простаивал рабочий поток. class Repository { public Task GetAllAsync(CancellationToken cancellationToken) { using (var db = new DataContext()) { return db.EntitySet.ToListAsync(); } } } public virtual async Task 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); } Обратите внимание http status code 204 - No content (это не ошибочный статус код) Для справки: https://ru.wikipedia.org/wiki/Список_кодов_состояния_HTTP И вообще вам прежде нужно почитать про Task и async await. Task.Run используется для cpu bound operations (Вычислительных операций процессора) async await для ожидания результата этих операций в вызывающем коде или ожидания операций I/O bound operations. То что касается entity framework то в 6 версии есть асинхронные методы их названия заканчиваются на Async, например .ToListAsync().

Комментариев нет:

Отправить комментарий