Подтвердить что ты не робот

Безопасен ли поток DbContext?

Мне было интересно, является ли класс DbContext потокобезопасным, я предполагаю, что это не так, поскольку в настоящее время я выполняю потоки paralell, которые обращаются к DbContext в моем приложении, и я получаю множество блокирующих исключений и других вещей похоже, что они могут быть связаны с потоком.

До недавнего времени у меня не было никаких ошибок... но до недавнего времени я не обращался к DbContext в потоках.

Если я прав, что бы люди предложили в качестве решения?

4b9b3361

Ответ 1

Это не безопасный поток. Просто создайте новый экземпляр DbContext в вашем потоке.

Ответ 2

Нет, он не является потокобезопасным - весь EF не является потокобезопасным, потому что контекст EF никогда не должен быть общим.

Ответ 3

Отредактированный - старый ответ ниже.

Теперь я всегда использую этот шаблон с 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 может быть дорогостоящим, и этот подход минимизирует эту стоимость.