忽略TransactionScope以进行特定查询

时间:2013-08-15 16:25:26

标签: c# entity-framework transactions entity-framework-5 transactionscope

我正在寻找一种在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方法,而我更倾向于处理这个问题。一个地方。

3 个答案:

答案 0 :(得分:35)

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

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;
    }
}

答案 1 :(得分:1)

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

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

编辑在查看它时,在我看来,如果你将Logging从SaveChanges移到DoSomething()上的catch(),你的日志记录就可以了。但是,您的日志记录仍然需要自包含和原子。

答案 2 :(得分: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;
    }
}