C# DbContext是线程安全的吗?

C# DbContext是线程安全的吗?,c#,thread-safety,entity-framework-4.1,C#,Thread Safety,Entity Framework 4.1,我想知道DbContext类是否是线程安全的,我假设它不是,因为我目前正在并行执行访问我的应用程序中DbContext的线程,并且我得到了大量锁定异常和其他看起来可能与线程相关的东西 直到最近,我还没有收到任何错误…但直到最近,我还没有访问线程中的DbContext 如果我是对的,人们会建议什么解决方案?不,它不是线程安全的-整个EF不是线程安全的,因为EF上下文永远不应该被共享。它不是线程安全的。只需在线程中创建DbContext的新实例。编辑-下面的旧答案 我现在总是在DbContext中使

我想知道
DbContext
类是否是线程安全的,我假设它不是,因为我目前正在并行执行访问我的应用程序中
DbContext
的线程,并且我得到了大量锁定异常和其他看起来可能与线程相关的东西

直到最近,我还没有收到任何错误…但直到最近,我还没有访问线程中的
DbContext


如果我是对的,人们会建议什么解决方案?

不,它不是线程安全的-整个EF不是线程安全的,因为EF上下文永远不应该被共享。

它不是线程安全的。只需在线程中创建
DbContext
的新实例。

编辑-下面的旧答案

我现在总是在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的内部或外部调用,并且仍然以正确的方式运行,而不必有这个方法的两个版本以及我拥有的其他方便的日志记录方法或其他实用程序方法,而不是必须知道和计划每次打电话给他们或他们的呼叫者的背景。这基本上给了我以下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对象可能会很昂贵,而且这种方法可以将成本降到最低。

那么,您真的应该在每次使用DbContext时创建它吗?目前,我的repository类中只有一个dbcontext字段,我想在需要时创建它可以解决这个问题……如果单个逻辑事务使用新的repository实例,并且您的存储库是线程安全的,那么每个存储库都可以有一个上下文。是的,我想,要保存对dbcontext的更改,我猜您必须在对其进行更改的dbcontext的同一实例上调用save changes?@LadislavMrnka您如何看待锁定?在我的MVC应用程序中,每个HTTP请求我共享一个DbContext实例,我们有多个线程在一个实例上工作。由于多个线程访问一个DbContext不是线程安全的,您必须锁定多个读/写访问。我用同样的方法处理这种情况。但既然我是EF的新手,你能详细说明一下你所说的疯狂行为吗?我在一个web表单项目中使用这个dbcontext,我在Application_EndRequest事件中处理每个线程的当前dbcontext。Application_EndRequest不是处理它的正确位置,因为请求不一定从开始到结束都在该线程上处理;另一个请求可能会在同一个线程上处理,然后在此线程完成、释放DbContext和BOOM。疯狂的行为太长,无法在评论中描述,请发布一个单独的问题,我将在那里回答。奇怪的是,我认为每个请求都有自己的线程!那么,在WebForms页面生命周期中,什么是处理线程静态对象的正确位置呢?请求在ASP.Net线程池中处理,如果你在谷歌上搜索,你会发现关于该主题的文章层出不穷。基本上,许多请求都是在任何给定的线程上处理的。我不处理我的DbContext;它自己管理打开和关闭引擎盖下的连接。所以DbContext只需要关闭和处理它的连接?这到底是什么时候发生的?让ThreadStatic DbContext挂起直到GC收集它是否安全?我想我在某个地方读到ThreadStatic对象必须手动处理。。。这没有问题吗?这也是我的方法。但是,这有多安全(不是线程方面的,而是数据库方面的)?正如Chris在他的回答中所说的,我应该期待有什么奇怪的行为吗?我可以在多个线程中使用
dbContext
,然后在父线程中提交更改吗@丹尼尔
var db = LogDbContext.Current();