实体框架DbContext和线程安全
本文关键字:线程 安全 DbContext 框架 实体 | 更新日期: 2023-09-27 18:11:14
我需要在一个事务中更新我的DB中的几个表,我读到使用DbContext.SaveChanges
应该是这样做的方式。
然而,我也读到DbContext
的生命周期应该尽可能短,因为它随着时间的推移而增长,因为它加载了更多的实体。
我也读到,为了使它线程安全,每个动作都应该有自己的DbContext
。
我是否应该为我想要更改的每个表设置一个DbContext
,并在每个DbContext
上调用SaveChanges
?最后一次SaveChanges
调用不会覆盖之前调用的更改吗?
最好的方法是什么?(我需要这个网站)
实体框架不是线程安全的。每个请求实例化一个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();
}