30

我正在寻找一种在 TransactionScope 处于活动状态时执行查询的方法,并忽略 TransactionScope - 基本上,无论如何我都想执行这个特定的查询。

我使用 EF 代码优先,以及应用程序的设计方式,在一次调用中多次打开一个新的数据上下文,每个都有自己的更改,所有这些都包含在一个 TransactionScope 中,它Complete()调用了最后假设没有失败。在我们覆盖的上下文中SaveChanges,如果发生任何异常base.SaveChanges(),我们可以在回滚事务之前捕获它并记录到数据库。

由于SaveChanges发生在事务内部,因此显然不会发生日志记录,因为它与原始调用属于同一事务。我试图完全忽略 TransactionScope 只是为了记录代码。

这是一些精简的代码:

// From the context
public override int SaveChanges() {
    try {
        return base.SaveChanges();
    } catch (Exception ex) {

        // Writes to the log table - I want this to run no matter what
        LogRepo.Log(/*stuff to log from the context*/);

        throw;
    }
}

// Inside the business logic
public void DoSomething() {
    try {
        using (var scope = new TransactionScope()) {

            using (var context = new FooContext()) {
                // Do something
                context.SaveChanges();
            }
            using (var context = new FooContext()) {
                // Do something else
                context.SaveChanges();
            }

            scope.Complete();
        }
    } catch (Exception ex) {
        // scope.Complete is never called, so the transaction is rolled back
    }
}

我尝试使用常规 ADO.NET 而不是 EF 进行日志记录,但结果仍然相同 - 它也会回滚。

我需要在 内部进行错误处理SaveChanges,因为我正在记录的是正在保存的实体的状态 - 所以我不能轻易地将日志记录移动到其他地方。我可以在内部构建消息SaveChanges catch,然后将其抛出并DoSomething catch记录它,但是有许多DoSomething方法,我宁愿只在一个地方处理这个问题。

4

3 回答 3

45

如果您在启用了抑制选项的情况下将日志调用包装在另一个事务范围内,则不会使用事务范围。

public override int SaveChanges() {
    try {
        return base.SaveChanges();
    } catch (Exception ex) {
        using (var scope = new TransactionScope(TransactionScopeOption.Suppress)) {
            LogRepo.Log(message); // stuff to log from the context
        }

        throw;
    }
}
于 2013-10-25T16:12:41.150 回答
1

只是我最初的想法,但是您需要将 LogRepo 放在它自己的 DataContext (DC2) 上,以便周围的 TransactionScope (使用 DC1) 在未提交时不会回滚。

基本上,您需要使您的日志记录自包含且原子化。

编辑再看一遍,在我看来,如果您将 Logging 从 SaveChanges 移到 DoSomething() 上的 catch() 中,则您的日志记录将起作用。但是,您的日志记录仍然需要是独立的和原子的。

于 2013-08-15T17:06:54.980 回答
1

我找到了一种我不太满意的解决方案,但似乎可行。TransactionScope 显然只影响当前线程,因此使用新线程进行日志记录似乎可以正常工作。

public override int SaveChanges() {
    try {
        return base.SaveChanges();
    } catch (Exception ex) {

        string message = /*stuff to log from the context*/;
        new Thread(msg => {    

            LogRepo.Log(msg);

        }).Start(message);

        throw;
    }
}
于 2013-08-15T17:52:55.917 回答