2

以下是使用事務範圍的異步緩存和數據庫更新。我無法使用v 4.5.1中引入的TransactionScopeAsyncFlowOption.Enabled,因爲我正在使用的Apache Ignite.Net緩存不支持它。我試圖通過捕捉當前Synchronization Context,然後明確使用Synchronization Context Send的方法來完成交易找到一個解決辦法,但我仍然得到一個錯誤Transaction scope must be disposed on same thread it was created啓用異步TransactionScope不使用TransactionScopeAsyncFlowOption.Enabled

任何建議如何去實現這Async Update不起作用。阿帕奇點燃支持的建議之一是使用類似:

Task.WhenAll(cacheUpdate, databaseUpdate).Wait(),但是這將使異步代碼同步,因此不是最好的選擇

public async Task Update() 
{ 
    // Capture Current Synchronization Context 
    var sc = SynchronizationContext.Current; 

    TransactionOptions tranOptions = new TransactionOptions(); 
    tranOptions.IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead; 


    using (var ts = new TransactionScope()) 
    { 
     // Do Cache Update Operation as Async 
     Task cacheUpdate = // Update Cache Async 

     // Do Database Update Operation as Async 
     Task databaseUpdate = // Update Database Async 

     await Task.WhenAll(cacheUpdate, databaseUpdate); 

       sc.Send(new SendOrPostCallback(
       o => 
       { 
        ts.Complete(); 
       }), sc);   
    } 
} 
+3

運行異步方法我不太明白你爲什麼不能使用TransactionScopeAsyncFlowOption.Enabled。你必須在.NET 4.0上運行? – Evk

+0

這將使用由Apache Ignite.Net公開的緩存更新異步方法,這些方法與Java進程通信,並按照它們不支持此選項。我不知道爲什麼 –

+2

的所有內部細節不知道此功能需要任何第三方組件的明確支持。 – Evk

回答

1

整個博客相當數量的搜索之後的一個文章中,我找到了Stephen Toub的以下博客,它幫助我們在完全相同的線程上實現Continuation of Async方法,從而避免了事務範圍問題。現在,我不需要TransactionScopeAsyncFlowOption.Enabled得到的TransactionScope

https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/

void Main() 
{ 
    // Modified Async Scheduler for Continuations to work on Exactly same thread 
    // Required in the case same Thread is required for Task Continuation post await 
    Run(async() => await DemoAsync()); 

    "Main Complete".Dump(); 
} 

static async Task DemoAsync() 
{ 
    // Transcation Scope test (shall dispose 
    using (var ts = new TransactionScope()) 
    {    
     await Cache + Database Async update 
     ts.Complete(); 
     "Transaction Scope Complete".Dump(); 
    } 
} 

// Run Method to utilize the Single Thread Synchronization context, thus ensuring we can 
// Control the threads/Synchronization context post await, cotinuation run of specific set of threads 

public static void Run(Func<Task> func) 
{ 
    // Fetch Current Synchronization context 
    var prevCtx = SynchronizationContext.Current; 

    try 
    { 
     // Create SingleThreadSynchronizationContext 
     var syncCtx = new SingleThreadSynchronizationContext(); 

     // Set SingleThreadSynchronizationContext 
     SynchronizationContext.SetSynchronizationContext(syncCtx); 

     // Execute Func<Task> to fetch the task to be executed 
     var t = func(); 

     // On Continuation complete the SingleThreadSynchronizationContext 
     t.ContinueWith(
      delegate { syncCtx.Complete(); }, TaskScheduler.Default); 

     // Ensure that SingleThreadSynchronizationContext run on a single thread 
     // Execute a Task and its continuation on same thread 
     syncCtx.RunOnCurrentThread(); 

     // Fetch Result if any 
     t.GetAwaiter().GetResult(); 
    } 
    // Reset the Previous Synchronization Context 
    finally { SynchronizationContext.SetSynchronizationContext(prevCtx); } 
} 

// Overriden Synchronization context, using Blocking Collection Consumer/Producer model 
// Ensure that same Synchronization context/Thread/set of threads are maintained 
// In this case we main a single thread for continuation post await 

private sealed class SingleThreadSynchronizationContext : SynchronizationContext 
{ 
    // BlockingCollection Consumer Producer Model 
    private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> 
     m_queue = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); 

    // Override Post, which is called during Async continuation 
    // Send is for Synchronous continuation 
    public override void Post(SendOrPostCallback d, object state) 
    { 
     m_queue.Add(
      new KeyValuePair<SendOrPostCallback, object>(d, state)); 
    } 

    // RunOnCurrentThread, does the job if fetching object from BlockingCollection and execute it 
    public void RunOnCurrentThread() 
    { 
     KeyValuePair<SendOrPostCallback, object> workItem; 
     while (m_queue.TryTake(out workItem, Timeout.Infinite)) 
      workItem.Key(workItem.Value); 
    } 

    // Compete the SynchronizationContext 
    public void Complete() { m_queue.CompleteAdding(); } 
}