使用 EF 的 DbContext.ExecuteSqlCommand() 的事务期间出现异常

Posted

技术标签:

【中文标题】使用 EF 的 DbContext.ExecuteSqlCommand() 的事务期间出现异常【英文标题】:Exception during transactions using EF's DbContext.ExecuteSqlCommand() 【发布时间】:2014-09-02 12:07:48 【问题描述】:

使用 try-catch 结构,我试图弄清楚如果在事务的任何点捕获到异常该怎么办。下面是一个代码示例:

try

   DbContext.ExecuteSqlCommand("BEGIN TRANSACTION");        //Line 1
   DBContext.ExecuteSqlCommand("Some Insertion/Deletion Goes Here"); //Line 2
   DbContext.ExecuteSqlCommand("COMMIT");                   //Line 3

catch(Exception)

   

如果在执行“第 1 行”时捕获了预期,则除了警告错误外,无需执行任何操作。如果它在执行第二行时被捕获,我不知道我是否需要尝试回滚已成功打开的事务,如果第三行出现问题,也会发生同样的情况。

我是否应该只发送回滚?还是在一个方法调用中将所有命令直接发送到银行?

在 try-catch 内部有一个循环执行许多事务,例如示例中的事务(我需要大量小事务而不是大事务,因此我可以正确重用 SQL 的“_log”文件并避免它增长不必要的)。

如果任何事务出错,我只需将它们全部删除并告知发生了什么,但我不能将其变成一个大事务而只使用回滚,否则它会使日志文件变大到 40GB。

【问题讨论】:

【参考方案1】:

认为这会有所帮助:

using (var ctx = new MyDbContext())

    // begin a transaction in EF – note: this returns a DbContextTransaction object
    // and will open the underlying database connection if necessary
    using (var dbCtxTxn = ctx.Database.BeginTransaction())
    
       try
       
            // use DbContext as normal - query, update, call SaveChanges() etc. E.g.:
           ctx.Database.ExecuteSqlCommand(
               @"UPDATE MyEntity SET Processed = ‘Done’ "
               + "WHERE LastUpdated < ‘2013-03-05T16:43:00’");

           var myNewEntity = new MyEntity()  Text = @"My New Entity" ;
           ctx.MyEntities.Add(myNewEntity);
           ctx.SaveChanges();

           dbCtxTxn.Commit();
       
       catch (Exception e)
       
           dbCtxTxn.Rollback();
       
     // if DbContextTransaction opened the connection then it will close it here

取自:https://entityframework.codeplex.com/wikipage?title=Improved%20Transaction%20Support

基本上,它的想法是您的事务成为 using 块的一部分,并且在其中您可以使用实际的 sql 进行 try/catch。如果在 try/catch 中有任何失败,它将被回滚

【讨论】:

值得注意的是,这仅适用于 EF 6。对于旧版本的 EF,您应该改用 TransactionScope(请参阅 ***.com/questions/22382892/…) 如果我没记错术语,这是一种事务范围,但是当我根本不需要使用实体时这有用吗?您将整个事务编写为单个查询“开始事务/n 插入内容/n 提交”的答案有什么区别? 从示例中可以看出,他不是在处理实体,而是在处理内联 sql。这样做的好处是它取消了您的问题 - 您不必担心捕获发生的方式/位置,无论如何它都会被回滚。 如果交易无法打开怎么办?会跳过 using 块吗?【参考方案2】:

从 Entity Framework 6 开始,ExecuteSqlCommand 使用自己的事务进行包装,如下所述:http://msdn.microsoft.com/en-gb/data/dn456843.aspx

除非您明确需要将多个 sql 命令滚动到单个事务中,否则无需明确开始新的事务范围。

关于事务日志增长,假设您的目标是 Sql Server,那么将事务日志操作设置为 simple 将确保日志在检查点之间得到回收。

显然,如果在整个导入过程中未维护事务日志历史记录,则没有隐式机制可以在失败时回滚所有数据。为简单起见,如果我需要删除所有行以防出错,我可能会在表中添加一个“已创建”日期时间字段,并根据筛选器从表中删除到已创建字段。

【讨论】:

我说要“正确”使用它哈哈哈,我之所以这么说是因为当我尝试使用单个查询来完成任务时,日志文件会增长到接近 40Gb 6gb,所以我正在创建小批量,当前一个操作被提交时将重用日志文件,因此不会超出需要扩展日志文件。恢复模型已经设置为简单但这个巨大的查询让我发疯了。无论如何感谢您的回答=]

以上是关于使用 EF 的 DbContext.ExecuteSqlCommand() 的事务期间出现异常的主要内容,如果未能解决你的问题,请参考以下文章

[EF]关于EF使用Contains关键字的问题

EF学习和使用综合

ps -ef grep 怎么使用?

ef code first db first 哪种好

MVC3+EF4.1学习系列-----EF4.1其他的一些技巧的使用

是否有使用 Fluent API 的 C# EF6 DbContext 生成器?