2012-10-04 15 views
36

Yeni asenkron/bekletme özelliğini bir DB ile eşzamansız olarak çalışmak için kullanıyorum. Bazı talepler uzun olabileceğinden, bunları iptal edebilmek istiyorum. İçinde bulunduğum sorun şu ki, TransactionScope görünen bir iş parçacığı afinitesine sahip ve görevin iptal edilmesi durumunda, onun Dispose() yanlış bir iş parçacığı üzerinde çalıştığı görünüyor. GÜNCELLEMETransactionScope canceble async/standında nasıl kullanılır?

public void TestTx() { 
    var cancellation = new CancellationTokenSource(); 
    var task = TestTxAsync (cancellation.Token); 
    cancellation.Cancel(); 
    task.Wait(); 
} 

private async Task TestTxAsync (CancellationToken cancellationToken) { 
    using (var scope = new TransactionScope()) { 
     using (var connection = new SqlConnection (m_ConnectionString)) { 
      await connection.OpenAsync (cancellationToken); 
      //using (var command = new SqlCommand (... , connection)) { 
      // await command.ExecuteReaderAsync(); 
      // ... 
      //} 
     } 
    } 
} 

: dışarı yorumladı parçası olmak bir şey var göstermektir İşte kod

"A TransactionScope must be disposed on the same thread that it was created." 

: .TestTx() çağrılırken

Özellikle, InvalidOperationExceptiontask.Wait() üzerinde AggregateException içeren aşağıdaki olsun - eşzamansız olarak - açık olduğunda bağlantı ile yapılır, ancak bu kodu, sorunu yeniden üretmek için gerekli değildir.

cevap

4

sorun ben söz konusu yansıtmayan bir konsol uygulamasında kodu, prototip olduğu gerçeğinden kaynaklanır.

yönlü zaman uyumsuz/bekliyor await bir ThreadPool olan cari TaskScheduler kullanılarak işletilirse devamı demektir varsayılan olarak biri yok SynchronizationContext.Current ve konsol uygulaması varlığına bağlıdır sonra kod çalıştırmaya devam eder Yani (potansiyel olarak?) farklı bir iş parçacığı üzerinde yürütülür. Bu nedenle, TransactionScope'un oluşturulduğu iş parçacığı üzerine yerleştirilmesini sağlayacak bir SynchronizationContext olması yeterlidir. Konsol uygulamaları varsayılan olarak WinForms ve WPF uygulamalarına sahip olurken, konsol uygulamaları ya özel bir kullanabilir ya da WPF'den DispatcherSynchronizationContext ödünç alabilir.

Burada ayrıntılı olarak mekaniği açıklamak iki büyük blog yayınları şunlardır: .NET Framework 4.5.1 yılında
Await, SynchronizationContext, and Console Apps
Await, SynchronizationContext, and Console Apps: Part 2

0

Evet, tek bir iş parçacığı üzerinde işlem tutmak zorundasınız. Async eyleminden önce işlemciyi oluşturduğunuzdan ve async eyleminde kullandığınızdan beri, işlemler tek bir iş parçacığında kullanılmaz. TransactionScope bunun gibi kullanılmak üzere tasarlanmamıştı.

Basit bir çözüm, TransactionScope nesnesinin ve Connection nesnesinin nesnesinin oluşturulmasının, async eyleminin içine taşınması olacağını düşünüyorum.

GÜNCELLEME

zaman uyumsuz işlem SqlConnection nesnesi içindeki olduğundan, bunu değiştirmek mümkün değil. Yapabileceklerimiz enlist the connection in the transaction scope. Bağlantı nesnesini eşzamansız olarak oluşturabilir, sonra da işlem kapsamını oluşturabilir ve işlemi kaydeder.

SqlConnection connection = null; 
// TODO: Get the connection object in an async fashion 
using (var scope = new TransactionScope()) { 
    connection.EnlistTransaction(Transaction.Current); 
    // ... 
    // Do something with the connection/transaction. 
    // Do not use async since the transactionscope cannot be used/disposed outside the 
    // thread where it was created. 
    // ... 
} 
+0

İkinci noktanızı detaylandırır mısınız? Yaratılışı nereye taşıyalım? – chase

+0

Bağlantıyı yalnızca senkronize olmayan bir şekilde açmayı ve gerçek iş yükü için çağrıları engellemeyi mi önerirsiniz? Yoksa yine bir şeyi özlüyorum mu? – chase

+0

Sorunuzdaki anda, bağlantıyı yalnızca senkronize olmayan bir şekilde edinebilirsiniz. Örneğinizi takip ettim, ancak sorunuzu gerçek bir iş yüküyle güncellerseniz, cevabımı da güncelleyebilirim. – Maarten

76

, bir TransactionScopeAsyncFlowOption parametresi alması new constructors for TransactionScope bir dizi vardır.

MSDN'e göre, iş parçacığı devamları boyunca işlem akışını sağlar.

Benim anlayış böyle kod yazmak için izin içindir ki:

Ben henüz denemedim, bu yüzden işe yarayacak mı bilmiyorum
// 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(); 
} 

.

+2

Denenmiş ve çalışır. EF6 ve işlem kapsamını en üste kullanma. –

+0

Mükemmel, teşekkürler! –

+0

4.5.1'e yükseltemezseniz ne olur? O zaman çözüm nedir? – JobaDiniz

İlgili konular