Правильное использование Async/Await при работе с БД через Entity Framework Core
Назрел следующий вопрос.
Есть задача - все запросы к БД должны происходить в асинхронном режиме. Я примерно представляю(и реализовал) такую возможность, но мне кажется, что я где-то сделал ошибку.
Инициатором самого обращения к БД служит обработка события нажатия на кнопку
private async void MenuItem_Click_3(object sender, RoutedEventArgs e)
{
try
{
ShowFrameInBrowser((await _videos.GetAsync((int)VideoNames.RAMWork)).URL);
ActionsToMakeVisibleBrowser();
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
}
далее мы приходим вот сюда
public async Task<Video> GetAsync(int id)
{
using (_db = new VideosContext(_options))
{
if (_db.Videos.AsNoTracking().Any(v => v.Id == id))
{
return await _db.Videos.AsNoTracking().FirstAsync(v => v.Id == id);
}
else
{
throw new Exception("По указанному индексу не нашлось видео");
}
}
}
Работает-то оно все как надо(по моим меркам), но что я не до конца понимаю - в каком месте должен стоять await в 2-х случаях:
- Когда метод возвращает void
- Когда метод возвращает Task
Насколько я понимаю, после того как написано await весь код ниже будет выполняться и так в другом потоке.
И у меня 2 вопроса:
- Первое обращение к БД в методе выше происходит в этой строчке
_db.Videos.AsNoTracking().Any(v => v.Id == id)и я хотел именно здесь применить конструкцию await, но, поскольку я должен вернуть Task, я этого не делаю. Правильно ли я поступаю? - Если метод возвращает void, то я должен ставить await при первом обращении к БД как в примере ниже?
public async void CreateAsync(Video item)
{
using (_db = new VideosContext(_options))
{
if(!await _db.Videos.AnyAsync(v => v.Name == item.Name))
{
_db.Add(item);
_db.SaveChanges();
}
else
{
throw new Exception("Попытка добавить существующее видео")
}
}
}
Ответы (1 шт):
Благодаря советам от aepot, VladD и Grundy получилось то, что я хотел сделать. Спасибо.
Что изменилось в коде.
Если вы не хотите,
чтобы вас закидали гнилыми помидорами
избегайте использования async void. Потому что асинхронные методы этого типа нельзя ожидать и, как в моем примере, выбрасывать исключение. Поэтому вид метода, где возвращаемым результатом является void, можно изменить на следующий:
public async Task<bool> TryCreateAsync(Video item)
{
using (_db = new VideosContext(_options))
{
if(!_db.Videos.Any(v => v.Name == item.Name))
{
_db.Add(item);
await _db.SaveChangesAsync();
return true;
}
else
{
return false;
}
}
}
Также я понял, что await не создает отдельный поток для выполнения, а
ключевое слово await делает две вещи: указывает точку возможного прерывания/возобновления метода; возможного -- потому что если таск уже завершен, то метод продолжит выполнение и прерываться не будет извлекает результат или исключение из таска, который возвращается ожидаемым методом Т.е. никакого отношения к потокам эти два слова не имеют как указано в одном из ответов этого вопроса.