Страницы

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

понедельник, 9 декабря 2019 г.

Постоянно возвращать Task или при необходимости использовать Task.Run()?

#c_sharp #многопоточность #async_await #асинхронность


Есть вот такой класс из слоя бизнес-логики: 

public class ProductService
{
    private readonly IGenericRepository _productRepository;

    public ProductService(IGenericRepository productRepository)
    {
        _productRepository = productRepository;
    }

    public Task Update(int id, ProductModel model)
    {
        return Task.Run(() =>
        {
            if (model == null)
            {
                throw new ArgumentNullException(nameof(model));
            }

            var product = _productRepository.GetById(id);
            if (product == null)
            {
                throw new ArgumentException(nameof(id));
            }
            // еще код... //

            _productRepository.Update(product);
        });
    }
}


Я поймал себя на том, что у меня все методы, обращающиеся к базе данных (даже если
они обращаются через репозитории), возвращают Task, так как знаю, что выше у меня Web
API контроллер, action которого я хочу вызывать асинхронно.

Я думаю, что я делаю неправильно, потому что, проектируя класс ProductService я не
должен заботиться о клиентском коде. Сейчас да, у меня клиентом является самый верхний
слой (Web API), но так ведь может быть не всегда и для другого клиента возможно тип
Task метода Update может быть излишним. Я прав?



Согласно своим рассуждениям я переписал методProductService.Update() и класс-клиент:

public void Update(int id, ProductModel model)
{
    if (model == null)
    {
        throw new ArgumentNullException(nameof(model));
    }

    var product = _productRepository.GetById(id);
    if (product == null)
    {
        throw new ArgumentException(nameof(id));
    }
    // еще код... //

    _productRepository.Update(product);
}


[RoutePrefix("api/products")]
public class ProductsController : ApiController
{
    private readonly IProductService _productService;

    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }

    [HttpPut]
    [Route("{id:int}")]
    public async Task Put([FromBody] productModel)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var productId = (int)RequestContext.RouteData.Values["id"];

        await Task.Run(() =>
        {
            _productService.Update(productId, productModel);
        });

        return Ok();
    }

    


Ответы

Ответ 1



В том что вы делаете, нет ни малейшего смысла - методы контроллеров уже исполняются в пуле потоков, и выносить их снова в пул потоков через Task.Run - бесполезно. Если вам хочется сделать полностью асинхронный код - надо отказаться от Task.Run в пользу действительно асинхронных способов. Так, если вы используете EntityFramework, то вытащить запись из БД можно через FirstOrDefaultAsync, а сохранить изменения - через SaveChangesAsync

Ответ 2



Я думаю, что я делаю неправильно, потому что, проектируя класс ProductService я не должен заботиться о клиентском коде. Совершенно верно. Предоставьте возможность клиенту самому решать, когда ему запускать метод синхронно, а когда -- асинхронно. Создание асинхронных оберток для синхронных методов мы уже обсуждали.

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

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