在TransactionScope

时间:2018-02-14 07:59:10

标签: c# .net async-await transactionscope fire-and-forget

我正在使用TransactionScope对象的using块中做一些事情。在某些时候,我想通过触发和忘记来调用一些异步代码(我不想等待结果,并且我对该调用期间发生的事情不感兴趣)并且我希望该代码不属于事务(使用TransactionScopeOption.Suppress选项)。

所以最初我做了类似于我在下面的代码中评论过的methodFails。它给我一个很好的“System.InvalidOperationException:'TransactionScope嵌套不正确'”。我在SO中查找了有类似问题的人,并找到了这个Question,其中ZunTzu的答案给了我method1使用TransactionScopeAsyncFlowOption.Enabled选项的想法,该工作正如我预期的那样methodFails 1}}但没有例外。

然后我想到了我放入method2的替代方案,其中包括将异步代码放入第三种方法(method3)中,而TransactionScopeOption.Suppress则通过触发而忘记选项保留在非异步method2中。在我的示例程序中,这种方法似乎与method1一样好。

所以我的问题是:哪种方法更好,method1method2,或者可能是我没想过的第三种方法?我倾向于method1,因为它听起来像“使用TransactionScope类的人将TransactionScopeAsyncFlowOption放在那里”。但是TransactionScopeAsyncFlowOption.Enabled不是TransactionScope的默认值这一事实让我觉得可能会因为启用它而导致性能下降,而且即使是点火也可能是我可以保存性能损失的特殊情况。 / p>

示例代码:

    class Program
    {
        static void Main(string[] args)
        {
            using (TransactionScope scope1 = new TransactionScope())
            {
                // Do some stuff in scope1...

                // Start calls that could execute async code
                //Task a = methodFails(); // This commented method would launch exception: System.InvalidOperationException: 'TransactionScope nested incorrectly'
                Task b = method1(); // Fire and forget
                method2();

                // Rest of stuff in scope1 ...
            }
            Console.ReadLine();
        }

        static async Task methodFails()
        {
            //Start of non-transactional section 
            using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Suppress))
            {
                //Do non-transactional work here
                Console.WriteLine("Hello World 0.1!!");
                await Task.Delay(10000);
                Console.WriteLine("Hello World 0.2!!");
            }
            //Restores ambient transaction here
            Console.WriteLine("Hello World 0.3!!");
        }

        static async Task method1()
        {
            //Start of non-transactional section 
            using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled))
            {
                //Do non-transactional work here
                Console.WriteLine("Hello World 1.1!!");
                await Task.Delay(10000);
                Console.WriteLine("Hello World 1.2!!");
            }
            //Restores ambient transaction here
            Console.WriteLine("Hello World 1.3!!");
        }

        static void method2()
        {
            //Start of non-transactional section 
            using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Suppress))
            {
                //Do non-transactional work here
                Task ignored = method3(); // Fire and forget
            }
            //Restores ambient transaction here
            Console.WriteLine("Hello World 2.2!!");
        }

        static async Task method3()
        {
            //Do non-transactional work here
            Console.WriteLine("Hello World 2.1!!");
            await Task.Delay(10000);
            Console.WriteLine("Hello World 2.3!!");
        }
    }

2 个答案:

答案 0 :(得分:4)

  

但事实上,TransactionScopeAsyncFlowOption.Enabled不是TransactionScope的默认设置,这让我觉得可能会因为启用它而导致性能下降,而且即使是点火也可能是我可以保存性能损失的特殊情况。

TransactionScopeAsyncFlowOption.Enabled是在修复错误时为了向后兼容而引入的。奇怪的是,除非你选择"选择加入"否则你不会从错误修复中受益。通过设置此标志。他们这样做是因为错误修复并没有破坏任何依赖于错误行为的现有代码。

this article中:

  

您可能不知道这一点,但.NET Framework的4.5.0版本包含有关System.Transactions.TransactionScope的严重错误以及它与async / await的行为方式。由于此错误,TransactionScope无法进入异步延续。这可能会更改事务的线程上下文,从而导致在处理事务范围时抛出异常。

     

这是一个大问题,因为它使编写涉及事务的异步代码极易出错。

     

好消息是,作为.NET Framework 4.5.1的一部分,Microsoft发布了针对该"异步延续的修复程序"错误。问题是像我们这样的开发人员现在需要明确选择加入这种新行为。我们来看看如何做到这一点。

     
      
  • 包装异步代码的TransactionScope需要在其构造函数中指定TransactionScopeAsyncFlowOption.Enabled。
  •   

答案 1 :(得分:3)

您可以在HostingEnvironment.QueueBackgroundWorkItem来电中调用异步方法。

HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
{
    await LongRunningMethodAsync();
});

QueueBackgroundWorkItem总结如下:

  

HostingEnvironment.QueueBackgroundWorkItem方法可以让你   安排小背景工作项目。 ASP.NET跟踪这些项目和   防止IIS突然终止工作进程,直到所有   后台工作项目已经完成。