Отредактировано - старый ответ ниже.
Теперь я всегда использую этот шаблон с 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 версии этого и любого другого удобного метода регистрации или другого вспомогательного метода, который у меня есть, и вместо того, чтобы знать и планировать контекст каждого вызова, который им когда-либо будет сделанили их абоненты.Это в основном возвращает мне одно из преимуществ приведенной ниже потоковой стратегии, когда мне не нужно было беспокоиться о том, когда именно БД открывается в вызовах утилит, о которых следует беспокоиться.
Старый ответ
Я обычно работаю с безопасностью потоков с помощью 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 может быть дорогостоящим, и этот подход минимизирует эту стоимость.