Является ли DbContext потокобезопасным? - PullRequest
52 голосов
/ 25 мая 2011

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

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

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

Ответы [ 3 ]

58 голосов
/ 25 мая 2011

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

27 голосов
/ 25 мая 2011

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

12 голосов
/ 03 августа 2012

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...