Ответ 1
Это не безопасный поток. Просто создайте новый экземпляр DbContext
в вашем потоке.
Мне было интересно, является ли класс DbContext
потокобезопасным, я предполагаю, что это не так, поскольку в настоящее время я выполняю потоки paralell, которые обращаются к DbContext
в моем приложении, и я получаю множество блокирующих исключений и других вещей похоже, что они могут быть связаны с потоком.
До недавнего времени у меня не было никаких ошибок... но до недавнего времени я не обращался к DbContext
в потоках.
Если я прав, что бы люди предложили в качестве решения?
Это не безопасный поток. Просто создайте новый экземпляр DbContext
в вашем потоке.
Нет, он не является потокобезопасным - весь EF не является потокобезопасным, потому что контекст EF никогда не должен быть общим.
Отредактированный - старый ответ ниже.
Теперь я всегда использую этот шаблон с DbContext:
using(var db = new LogDbContext())
{
// Perform work then get rid of the thing
}
Мой подход к одному запросу в запросе означал, что кешированные объекты в DbContext будут стоять и устаревать, даже если другие экземпляры DbContext записывают новые значения в фактическую базу данных позади нее. Это создало бы некоторые странные проблемы, например, один запрос, выполняющий вставку, и следующий запрос списка, входящего в другой поток, который имел кэшированный, устаревший список данных для этого запроса.
Существуют подходы, которые делают работу ниже и даже улучшают производительность приложений с множеством чтений/нескольких записей, но они требуют больше дизайна и стратегии, чем более простой шаблон выше.
Обновление
Я также использую полезный вспомогательный метод для библиотечных методов, таких как вызовы журналов. Здесь вспомогательный метод:
public static async Task Using(Db db, Func<Db, Task> action)
{
if (db == null)
{
using (db = new Db())
{
await action(db);
}
}
else
{
await action(db);
}
}
С этим я могу легко написать код, который использует необязательный существующий DbContext, или создает экземпляр внутри контекста использования, в зависимости от того, как он вызван.
Например, при работе с DbContext я могу загрузить некоторые данные, зарегистрировать некоторую информацию, а затем сохранить эти данные - лучше всего сделать это с одним и тем же DbContext с точки зрения производительности. С другой стороны, я мог бы также записать что-то в ответ на простое действие и не загружать и не записывать какие-либо другие данные. Используя вышеописанный метод, я могу использовать только один метод ведения журнала, который работает независимо от того, хотите ли вы работать внутри существующего DbContext или нет:
public async Task WriteLine(string line, Db _db = null)
{
await Db.Using(_db, db => {
db.LogLines.Add(new LogLine(line));
await db.SaveChangesAsync();
});
}
Теперь этот вызов метода можно вызвать внутри или за пределами существующего DbContext и по-прежнему вести себя правильно, вместо того, чтобы иметь 2 версии этого и каждого другого метода ведения журнала или другого метода полезности, которые у меня есть, и вместо того, чтобы знать и планировать контекст каждого звонка, который когда-либо будет сделан им или их абонентам. Это в основном возвращает мне одно из преимуществ приведенной ниже стратегии threadstatic, где мне не нужно было беспокоиться о том, когда именно db открылся в служебных вызовах, которые должны беспокоиться об этом.
Я обычно обрабатываю безопасность потоков с помощью EF DbContext следующим образом:
public class LogDbContext : DbContext
{
. . .
[ThreadStatic]
protected static LogDbContext current;
public static LogDbContext Current()
{
if (current == null)
current = new LogDbContext();
return current;
}
. . .
}
С этим на месте я могу получить DbContext для этого потока, например:
var db = LogDbContext.Current();
Важно заметить, что, поскольку каждый DbContext хранит свой собственный локальный кеш, каждый поток теперь будет иметь свой отдельный кеш объектов сущности, что может привести к некоторому сумасшедшему поведению, если вы не подготовлены к нему. Однако создание новых объектов DbContext может быть дорогостоящим, и этот подход минимизирует эту стоимость.