รับ TransactionScope เพื่อทำงานกับ async / await


114

ฉันกำลังพยายามรวมasync/ awaitเข้ากับบัสบริการของเรา ผมดำเนินการSingleThreadSynchronizationContextบนพื้นฐานของตัวอย่างนี้http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx

TransactionScopeและทำงานดียกเว้นสิ่งหนึ่งที่: ฉันรอคอยสิ่งที่อยู่ข้างในTransactionScopeและมันก็ทำลายTransactionScope.

TransactionScopeไม่ได้ดูเหมือนว่าจะเล่นดีกับasync/ อย่างแน่นอนเพราะมันเก็บสิ่งในหัวข้อการใช้await ThreadStaticAttributeฉันได้รับข้อยกเว้นนี้:

"TransactionScope ซ้อนไม่ถูกต้อง"

ฉันพยายามบันทึกTransactionScopeข้อมูลก่อนจัดคิวงานและกู้คืนก่อนที่จะรัน แต่ดูเหมือนว่าจะไม่เปลี่ยนแปลงอะไรเลย และTransactionScopeโค้ดก็เป็นระเบียบดังนั้นจึงยากที่จะเข้าใจว่าเกิดอะไรขึ้น

มีวิธีทำให้ใช้งานได้หรือไม่? มีทางเลือกอื่นให้TransactionScopeหรือไม่?


นี่คือรหัสที่ง่ายมากในการสร้างข้อผิดพลาด TransactionScope pastebin.com/Eh1dxG4aยกเว้นว่าข้อยกเว้นในที่นี้คือ Transaction Aborted
Yann

คุณสามารถใช้ธุรกรรม SQL ปกติได้หรือไม่? หรือคุณใช้ทรัพยากรหลายอย่าง
Marc Gravell

ฉันกำลังขยายทรัพยากรหลายแหล่ง
ยานน

ดูเหมือนว่าคุณจะต้องส่งผ่านขอบเขตไปยังวิธี async ของคุณหรือให้วิธีดึงข้อมูลจากบริบททั่วไปบางประเภทที่ระบุไว้ในหน่วยงานของคุณ
Bertrand Le Roy

คุณจะต้องมีหัวข้อแยกกับของตัวเองสำหรับแต่ละระดับบนสุดSingleThreadSynchronizationContext TransactionScope
Stephen Cleary

คำตอบ:


161

ใน. NET Framework 4.5.1 มีชุดของตัวสร้างใหม่TransactionScopeที่ใช้TransactionScopeAsyncFlowOptionพารามิเตอร์

ตาม MSDN จะเปิดใช้งานการไหลของธุรกรรมข้ามความต่อเนื่องของเธรด

ความเข้าใจของฉันคือการอนุญาตให้คุณเขียนโค้ดเช่นนี้:

// transaction scope
using (var scope = new TransactionScope(... ,
  TransactionScopeAsyncFlowOption.Enabled))
{
  // connection
  using (var connection = new SqlConnection(_connectionString))
  {
    // open connection asynchronously
    await connection.OpenAsync();

    using (var command = connection.CreateCommand())
    {
      command.CommandText = ...;

      // run command asynchronously
      using (var dataReader = await command.ExecuteReaderAsync())
      {
        while (dataReader.Read())
        {
          ...
        }
      }
    }
  }
  scope.Complete();
}

10

ช้าไปหน่อยสำหรับคำตอบ แต่ฉันมีปัญหาเดียวกันกับ MVC4 และฉันอัปเดตโครงการจาก 4.5 เป็น 4.5.1 โดยคลิกขวาที่โครงการไปที่คุณสมบัติ เลือกแท็บแอปพลิเคชันเปลี่ยนกรอบเป้าหมายเป็น 4.5.1 และใช้ธุรกรรมดังต่อไปนี้

using (AccountServiceClient client = new AccountServiceClient())
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
}

2
สิ่งนี้แตกต่างจากคำตอบที่ยอมรับอย่างไร
Liam

6

คุณสามารถใช้DependentTransaction ที่สร้างโดยเมธอดTransaction.DependentClone () :

static void Main(string[] args)
{
  // ...

  for (int i = 0; i < 10; i++)
  {

    var dtx = Transaction.Current.DependentClone(
        DependentCloneOption.BlockCommitUntilComplete);

    tasks[i] = TestStuff(dtx);
  }

  //...
}


static async Task TestStuff(DependentTransaction dtx)
{
    using (var ts = new TransactionScope(dtx))
    {
        // do transactional stuff

        ts.Complete();
    }
    dtx.Complete();
}

การจัดการภาวะพร้อมกันด้วย DependentTransaction

http://adamprescott.net/2012/10/04/transactionscope-in-multi-threaded-applications/


2
งานเด็กตัวอย่างของ Adam Prescott ไม่ได้ถูกทำเครื่องหมายว่าไม่ตรงกัน หากคุณแทนที่ "ทำธุรกรรม" ด้วยawait Task.Delay(500)รูปแบบนี้ก็จะล้มเหลวเช่นกันTransactionScope nested incorrectlyเนื่องจาก TransactionScope ด้านนอกสุด (ไม่แสดงในตัวอย่างด้านบน) ออกจากขอบเขตก่อนที่งานย่อยจะเสร็จสมบูรณ์อย่างถูกต้อง แทนที่awaitด้วยTask.Wait()และใช้งานได้ แต่คุณได้สูญเสียประโยชน์ของasync.
mdisibio

นี่เป็นวิธีที่ยากกว่าในการแก้ปัญหา TransactionScope คือการซ่อนท่อประปาทั้งหมดนั้น
Eniola
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.