实体框架DbContext和线程安全

本文关键字:线程 安全 DbContext 框架 实体 | 更新日期: 2023-09-27 18:11:14

我需要在一个事务中更新我的DB中的几个表,我读到使用DbContext.SaveChanges应该是这样做的方式。

然而,我也读到DbContext的生命周期应该尽可能短,因为它随着时间的推移而增长,因为它加载了更多的实体。

我也读到,为了使它线程安全,每个动作都应该有自己的DbContext

我是否应该为我想要更改的每个表设置一个DbContext,并在每个DbContext上调用SaveChanges ?最后一次SaveChanges调用不会覆盖之前调用的更改吗?

最好的方法是什么?(我需要这个网站)

实体框架DbContext和线程安全

实体框架不是线程安全的。每个请求实例化一个MVC控制器。因此,如果您每个请求使用一个DbContext,只要您不手动在控制器操作中生成线程(无论如何都不应该这样做),您就是安全的。

现在,如果您的应用程序中有并发性,例如预订系统,其中多个用户外出访问可能耗尽的相同稀缺资源(如门票),则必须自己实现相关逻辑。无论如何,没有线程安全可以帮助你。

这就是为什么你被要求在注释中写代码,因为一般来说解释线程安全的方式太宽泛了,可能不适用于你的情况。

简单的方法是,每个请求有一个DbContext, ASP。asp.net MVC做所有的线程安全,每个控制器实例在ASP。NET MVC对每个请求都是隔离的,你不必担心竞争条件。只要您不创建线程,而只是简单地使用单个DbContext在action方法中进行数据转换,就不会有任何问题。

基本上DbContext什么都不做,它只是将SQL查询队列到目标数据库,它是处理多线程,竞争条件的数据库。为了保护您的数据,您应该在数据库中使用事务并添加验证,以确保它们被正确保存

public abstract class DbContextController : Controller{
    public AppDbContext  DB { get; private set;}
    public DbContextController(){
        DB = new AppDbContext();
    }
    protected override void OnDisposing(bool disposing){
        DB.Dispose();
    }
}

如果您从DbContextController继承任何类,并在控制器的整个生命周期中使用DB,您将不会有任何问题。

public ActionResult ProcessProducts(){
    foreach(var p in DB.Products){
       p.Processed = true;
       foreach(var order in p.Orders){
          order.Processed = true;
       }
    }
    DB.SaveChanges();
}

但是,如果像下面的例子那样使用任何线程,

public ActionResult ProcessProducts(){
    Parallel.ForEach(DB.Products, p=>{
         p.Processed = true;
         // this fails, as p.Orders query is fired
         // from same DbContext in multiple threads
         foreach(var order in p.Orders){
             order.Processed = true;
         }
    });
    DB.SaveChanges(); 
}