C# 什么';使用单个和多个SaveChanges()调用的Entity Framework(6)事务之间的区别

C# 什么';使用单个和多个SaveChanges()调用的Entity Framework(6)事务之间的区别,c#,transactions,entity-framework-6,C#,Transactions,Entity Framework 6,我想知道这三种方式在同一数据库上下文中执行事务的实际区别: 1) 使用单个SaveChanges()执行多个操作,而不显式使用sql事务 using (TestDbContext db = new TestDbContext()) { // first operation // second operation db.SaveChanges(); } using (TestDbContext db = new TestDbContext()) using (DbCont

我想知道这三种方式在同一数据库上下文中执行事务的实际区别:

1) 使用单个
SaveChanges()
执行多个操作,而不显式使用sql事务

using (TestDbContext db = new TestDbContext())
{
    // first operation
    // second operation
    db.SaveChanges();
}
using (TestDbContext db = new TestDbContext())
using (DbContextTransaction trans = db.Database.BeginTransaction())
{
     // operation 1
     // operation 2
     db.SaveChanges();    
     trans.commit();
}
using (TestDbContext db = new TestDbContext())
using (DbContextTransaction trans = db.BeginTransaction())
{
     // operation 1
     db.SaveChanges();    
     // operation 2
     db.SaveChanges();

     trans.commit();
}
2) 使用sql事务通过单个
SaveChanges()
执行多个操作

using (TestDbContext db = new TestDbContext())
{
    // first operation
    // second operation
    db.SaveChanges();
}
using (TestDbContext db = new TestDbContext())
using (DbContextTransaction trans = db.Database.BeginTransaction())
{
     // operation 1
     // operation 2
     db.SaveChanges();    
     trans.commit();
}
using (TestDbContext db = new TestDbContext())
using (DbContextTransaction trans = db.BeginTransaction())
{
     // operation 1
     db.SaveChanges();    
     // operation 2
     db.SaveChanges();

     trans.commit();
}
3) 使用sql事务执行具有多个
SaveChanges()
的多个操作

using (TestDbContext db = new TestDbContext())
{
    // first operation
    // second operation
    db.SaveChanges();
}
using (TestDbContext db = new TestDbContext())
using (DbContextTransaction trans = db.Database.BeginTransaction())
{
     // operation 1
     // operation 2
     db.SaveChanges();    
     trans.commit();
}
using (TestDbContext db = new TestDbContext())
using (DbContextTransaction trans = db.BeginTransaction())
{
     // operation 1
     db.SaveChanges();    
     // operation 2
     db.SaveChanges();

     trans.commit();
}
在(2)和(3)中,如果
commit()
实际上应该执行对数据库的请求sql查询,那么它真的不同吗,比如说,保存每个操作的更改,还是一次保存所有操作的更改

如果(1)还允许在同一数据库上下文中安全地执行多个操作,那么手动启动事务的主要用途是什么?我想说,如果发生错误,我们可以手动提供try/catch块来回滚事务,但是AFAIK,SaveChanges()也会自动覆盖它,至少在SQLServer中是这样


**更新:另一件事是:我应该将db上下文和事务变量设置为类级别,还是这些变量应该是仅包含方法的本地变量?

如果不启动事务,它是隐式的。也就是说,调用后数据库中会立即显示您执行的所有
SaveChanges()

如果启动事务,
SaveChanges()
仍会执行更新,但在调用
commit
之前,数据对其他连接不可用

您可以通过设置断点、创建新对象、将它们添加到上下文中以及执行
SaveChanges()
来测试这一点。您将看到ID属性在该调用之后将有一个值,但是在您对事务执行提交之前,数据库中将没有相应的行

至于第二个问题,它实际上取决于并发需求、类正在做什么以及处理多少数据。这与其说是一个范围问题,不如说是一个代码执行问题

上下文不是线程安全的,所以只要应用程序中只有一个线程访问上下文,就可以在更大的范围内访问上下文。但是,如果应用程序的其他实例正在访问数据,则必须确保将数据刷新到最新模型。你还应该考虑,你加载到内存中的模型越多,越慢的保存时间就会越长。
我倾向于创建尽可能接近将要执行的操作的上下文,并在不久之后处理它们。

您的问题似乎根本不是关于实体框架,而是关于sql事务。sql事务是单个“原子”更改。也就是说,要么提交所有更改,要么不提交任何更改

您没有一个真正涵盖该场景的示例,但如果您添加了另一个示例,如:

using (TestDbContext db = new TestDbContext())
{
     // operation 1
     db.SaveChanges();    
     // operation 2
     db.SaveChanges();
}
…在此示例中,如果第一个操作成功保存,但第二个操作失败,则可能会出现第一步提交的数据可能无效的情况


这就是为什么要使用sql事务,将
SaveChanges
打包为一个操作,这意味着提交所有数据,或者提交无数据。

这是显而易见的,但我的意思是,如果事务实际执行,会有什么区别。当然,在调用commit()之前,案例(2)和(3)中的事务不会执行。我不确定我是否理解您试图理解的内容。但如果不指定事务,则仍然存在一个事务。它基本上围绕着EF在服务器上创建的每个sql语句,确切地说,你有一个要点“如果你不指定一个事务,那么一个仍然存在”。然而,正如我所看到的,事务的隐式或显式触发不会带来任何实际的差异,我的意思是,任何影响开发灵活性、系统性能或其他方面的东西。例如,如果我自己启动事务并提交更改(日志记录、基准测试等),我能做什么,而调用SaveChanges()自动执行所有操作时我做不到什么?默认行为是“一刀切”,因此无论您的情况如何,都可能是真的。但在某些情况下,您需要事务(对于可能执行或可能不执行更新的模型上的行锁定,或者出于EF关系管理以外的其他原因,您需要在提交所有行之前提前知道ID)。或者,您可能正在通过公共事务将EF操作与非EF操作混合。再加上保罗下面的回答说明了一个很好的观点,它们几乎是一样的。在示例1中,隐式事务在
SaveChanges()
处创建。EF不会为查询创建事务,因此在此之前的任何读取操作都不会包含事务。在示例2中,您是在操作开始时创建它的,因此根据隔离级别设置,通过读取行可以更早地锁定行。您好,确切地说,问题是关于在EF中使用事务。我以为DbContext和DbContextTransaction只来自EF,不是吗?我给出的代码片段可能代表了一种典型的情况,其中对几个db表进行了一些更改(比如创建一个新订单涉及,(1)插入一个订单记录和(2)插入一个OrderDetail记录。正如您所说,创建订单的事务在(1)和(2)之间完成我清楚地看到了您的示例和我的示例之间的区别,并且知道事务在这里的重要性,是的,原子性。但是我不清楚手动操作事务的意义,而不是在发生sh*t时显式触发回滚(使用try/catch)。Acc