У меня есть вопрос о цепочке задач с TPL. Он должен работать с Entity Framework Core, поэтому мы не поддерживаем одновременные операции в одном и том же контексте базы данных.
Я хочу быть максимально асинхронным. Ниже приведен код без асинхронности:
Context.Set<T>().Add(entity);
Context.SaveChanges();
return entity;
Я хочу сделать эту асинхронную цепочку методов AddAsync
, SaveChangesAsync
. Я должен убедиться, что AddAsync
завершен, прежде чем вызывать SaveChangesAsync
.
Моя первая попытка была следующей:
public Task<T> AddAsync(T entity)
{
return _dbContext.Set<T>()
.AddAsync(entity)
.ContinueWith(addTask =>
{
_dbContext.SaveChangesAsync();
return addTask.Result.Entity;
});
}
Но я не думаю, что это правильно. SaveChangesAsync()
не содержится в возвращаемой задаче. Если я позвоню: await AddAsync(myEntity);
, я уверен, что SaveChangesAsync()
будет готово? Я так не думаю.
Итак, я пытался решить эту проблему, но в итоге получил вложенные задачи, которые не очень удобны для пользователя. Возможное решение будет примерно таким (псевдокод, не компилируется):
return Context.Set<T>()
.AddAsync(entity)
.ContinueWith(addTask =>
{
return Context.SaveChangesAsync()
.ContinueWith(saveTask =>
{
return addTask.GetAwaiter().GetResult().Entity;
});
});
Есть ли способ добиться такого поведения с помощью TPL? И если да: я просто переписываю await
? Это API, который следует использовать во многих проектах. Я хочу сделать API как можно более производительным (по крайней мере, в теории. Я использую его для обучения). Пользователи должны использовать await
вне API. Чего я хочу избежать, так это создания узких мест внутри API из-за ожидания. Я не уверен, не является ли ContinueWith
просто другой реализацией await
. Еще одна вещь, которую я избегаю при использовании ContinueWith
вместо await
, — это помечать мои методы как async
.
SaveChangesAsync()
не содержится в возвращенной задаче. Это.ContinueWith
возвращает задачу, отличную от исходной, которая включает в себя продолжение. Но вкратце,async/await
специально разработан для обработки продолжений естественным (и менее подверженным ошибкам) способом. Пометка вашего методаasync
или нет не влияет на вызывающие программы.await
и ожидание — разные вещи. Мой совет - забудьте сырой TPL и используйтеasync/await
при реализации таких методов. - person Ivan Stoev   schedule 14.07.2018await
вернул задачу из этойContinueWith
, методSaveChangesAsync()
не обязательно будет завершен, потому что он выполняется внутри другой задачи. - person El Mac   schedule 14.07.2018async/await
:) Потому что да, вы заново изобретаетеawait
. Другими словами, код послеawait
эквивалентенContinueWith
, но в более естественном и оптимизированном для компилятора виде. - person Ivan Stoev   schedule 14.07.2018await
не ждать. это означает продолжить, когда это будет сделано. Вы можете найти много полезных объяснений того, какasync/await
работает в SO и блогах. - person Ivan Stoev   schedule 14.07.2018