【问题标题】:Writing to log table after transaction rollback in EF在 EF 中事务回滚后写入日志表
【发布时间】:2018-02-15 00:47:55
【问题描述】:

我需要在 EF 中事务回滚后写入日志表。 当前代码:

using (MyDataBase db = new DataBase())
{
    using (var dbContextTransaction = db.Database.BeginTransaction())
    {
        try
        {
            //Inserting several records to different tables
            var newEntity = new Entity();
            ...
            db.Entity.Add(newEntity);

            db.SaveChanges();
            db.up_StoredProcCall;
            dbContextTransaction.Commit();
        }
        catch (Exception ex)
        {
            dbContextTransaction.Rollback();

            var logEntry = new LogEntry();
            ...
            db.LogEntry.Add(logEntry);
            db.SaveChanges();
        }
    }
}

它不起作用,因为 EF 会在内部缓存所有插入并将它们全部与单个事务中的日志记录一起刷新:

db.LogEntry.Add(logEntry);
db.SaveChanges();

实现目标的最佳方式是什么:

  1. 创建单独的 DbContext 以插入日志记录?
  2. 如here 或here 或here 所述,通过ChangeTracker 放弃更改?我个人认为这不是一个好主意,因为那时我根本不需要使用交易。
  3. 非常感谢任何其他选项

【问题讨论】:

  • 在 SQL 服务器端,如果一个事务回滚,它通常是注定的(不是比喻,这是它的技术术语),这意味着不能执行更多的数据修改语句,包括任何写入到日志表。因此,您必须至少需要一个单独的事务来记录故障。
  • 我不熟悉 EF 如何处理事务,确切地说。如果它对dbContextTransaction 仍然存在感到不安,请将异常存储在来自catch 的局部变量中,然后检查它并在using 之外登录。
  • EF 中的事务仅更改 SQL 端更改的事务上下文。通常,它会将在 SaveChanges 调用之前所做的所有更改包装在一个事务中。事务上下文对此进行了更改,例如将存储过程的调用包含在事务中。但它对 ChangeTracker 没有任何作用。因此,将日志表插入远离事务范围将无济于事,因为事务中所做的所有更改都将保留在 EF 的缓存 (ChangeTracker) 中,并将转到 SaveChanges 上的数据库。只有将日志表插入移动到另一个 DbContext 才会有所帮助。但可能有更方便的解决方案?
  • “在 SQL Server 端,如果事务回滚......意味着不能执行更多的数据修改语句” - 这是不正确的。回滚只会丢弃事务中所做的所有更改。但回滚后,您可以写入数据库。
  • 哎呀。你是对的,我把它与 errors 混淆了,它使事务 before 回滚。并非所有错误都会导致事务终止(也不是所有错误都会导致事务终止)。在明确的ROLLBACK 之后,事务不再存在,所以忘记这一切。

标签: c# .net sql-server entity-framework transactions


【解决方案1】:

使用失败的 SaveChanges() 关闭 DbContext 并从新条目写入日志条目。 AFAIK 无法放弃 DbContext 中的未决更改。

例如

using (MyDataBase db = new DataBase())
{
    using (var dbContextTransaction = db.Database.BeginTransaction())
    {
        try
        {
            //Inserting several records to different tables
            var newEntity = new Entity();
            ...
            db.Entity.Add(newEntity);

            db.SaveChanges();
            db.up_StoredProcCall;
            dbContextTransaction.Commit();
        }
        catch (Exception ex)
        {
            dbContextTransaction.Rollback();
            db.Dispose();

            using (MyDataBase dbLog = new DataBase())
            {
              var logEntry = new LogEntry();
              ...
              dbLog.LogEntry.Add(logEntry);
              dbLog.SaveChanges();

            }

        }
    }
}

【讨论】:

  • 谢谢大卫!但是是否需要在创建新的 DbContext 之前显式处理第一个 DbContext ?我想新的 DbContext 在创建时应该没有待处理的更改,并且在新的 DbContext 上调用 SaveChanges 不应受到第一个上下文中所做更改的影响。离开 catch 块后,它将由 .NET 处理。同时打开两个上下文是否有任何风险或缺点?
  • 不需要。但是,如果您在打开新事务之前回滚事务并 Dispose DbContext,您可能会重用相同的池连接。
  • 顺便问一下,事务的创建对Entity.Add、Entity.Remove和update等方法有影响吗?或者它只改变 SaveChanges 方法的行为?换句话说,我可以在调用 SaveCahnges 之前创建事务吗?像这样:创建上下文、进行数据更改、创建事务、SaveChanges、调用 SP、提交/回滚?
  • 您可以在第一个 SaveChanges() 之前开始事务,但提前开始事务并无害。
  • 我知道早点启动它并没有什么坏处。问题只是为了让我了解它到底做了什么。我有两个选择:它可以在 ChangeTracker 中以某种方式标记对象,或者它只是改变 SaveChanges 方法的行为。现在很明显,后者是正确的。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2010-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多