Ответ 1
Здесь у нас есть тупиковая ситуация. AspNetSynchronizationContext
, который отвечает за модель потоковой обработки среды выполнения ASP.NET Web API, не гарантирует, что асинхронное продолжение после await
произойдет в одном потоке. Вся идея заключается в том, чтобы сделать приложения ASP.NET более масштабируемыми, поэтому меньше потоков из ThreadPool
блокируются ожидающими синхронными операциями.
Однако DataContext
не является потокобезопасным, поэтому его не следует использовать, если потенциальный коммутатор потоков может возникать в вызовах API DataContext
. Отдельная конструкция using
для асинхронного вызова будет иметь не, либо:
var something;
using (var dataContext = new DataContext())
{
something = await dataContext.someEntities.FirstOrDefaultAsync(e => e.Id == 1);
}
Это потому, что DataContext.Dispose
может выполняться в другом потоке с тем, из которого был первоначально создан объект, и это не то, что ожидало бы DataContext
.
Если вам нравится придерживаться API DataContext
, вызов его синхронно представляется единственным возможным вариантом. Я не уверен, что это утверждение должно быть распространено на весь API EF, но я полагаю, что любые дочерние объекты, созданные с помощью DataContext
API, также не являются потокобезопасными. Таким образом, в ASP.NET их область using
должна быть ограничена областью между двумя соседними вызовами await
.
Может возникнуть соблазн выгрузить кучу синхронных вызовов DataContext
в отдельный поток с помощью await Task.Run(() => { /* do DataContext stuff here */ })
. Тем не менее, это будет известный анти-шаблон, особенно в контексте ASP.NET, где это может только повредить производительность и масштабируемость, поскольку это не уменьшит количество потоков, необходимых для выполнения запроса.
К сожалению, хотя асинхронная архитектура ASP.NET велика, она по-прежнему несовместима с некоторыми установленными API и шаблонами (например, здесь аналогичный случай).
Это особенно печально, потому что мы не имеем дело с одновременным доступом к API здесь, то есть не более одного потока пытается получить доступ к объекту DataContext
в то же время.
Надеюсь, Microsoft рассмотрит это в будущих версиях Framework.
[UPDATE] Однако в больших масштабах может быть возможно разгрузить логику EF отдельному процессу (выполняемому как служба WCF), который предоставил бы безопасный асинхронный API для потоков Логика клиента ASP.NET. Такой процесс может быть организован с помощью настраиваемого контекста синхронизации как машины событий, аналогичной Node.js. Он может даже запускать пул квартир Node.js-like, каждая квартира поддерживает сходство потоков для объектов EF. Это позволит по-прежнему извлекать выгоду из API асинхронного EF.
[ОБНОВЛЕНИЕ] Ниже приведена некоторая попытка найти решение этой проблемы.