2012-10-04 15 views
36

मैं एक डीबी के साथ असीमित रूप से काम करने के लिए नई async/प्रतीक्षा सुविधा का उपयोग करने की कोशिश कर रहा हूं। चूंकि कुछ अनुरोध लंबे समय तक हो सकते हैं, मैं उन्हें रद्द करने में सक्षम होना चाहता हूं। जिस मुद्दे में मैं चल रहा हूं वह यह है कि TransactionScope स्पष्ट रूप से एक थ्रेड एफ़िनिटी है, और ऐसा लगता है कि कार्य को रद्द करते समय, Dispose() गलत थ्रेड पर चलाया जाता है।रद्द करने योग्य async/प्रतीक्षा में TransactionScope का निपटान कैसे करें?

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(); 
      // ... 
      //} 
     } 
    } 
} 

UPDATED: बाहर टिप्पणी की भाग वहाँ कुछ दिखाने के लिए है

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

कोड यह रहा:

विशेष रूप से, जब .TestTx() बुला मैं निम्नलिखित AggregateException युक्त InvalidOperationExceptiontask.Wait() पर मिलता है निष्पादित - असीमित रूप से - एक बार यह खुला होने के बाद कनेक्शन के साथ, लेकिन उस कोड को पुन: पेश करने के लिए उस कोड की आवश्यकता नहीं है।

उत्तर

4

समस्या तथ्य यह है कि मैं एक सांत्वना आवेदन में कोड है, जो मैं प्रश्न में प्रतिबिंबित नहीं किया प्रोटोटाइप था से उपजी है।

रास्ता async/इंतजार के बाद awaitSynchronizationContext.Current, और कंसोल आवेदन की उपस्थिति पर निर्भर है डिफ़ॉल्ट रूप से एक है, जो निरंतरता वर्तमान TaskScheduler है, जो एक ThreadPool है का उपयोग कर निष्पादित हो जाता है इसका मतलब है की जरूरत नहीं है कोड निष्पादित करने के लिए जारी , तो यह (संभावित रूप से?) एक अलग धागे पर निष्पादित करता है।

इस प्रकार किसी को केवल SynchronizationContext होना चाहिए जो सुनिश्चित करेगा कि TransactionScope उसी थ्रेड पर डिस्प्ले किया गया है जो इसे बनाया गया था। WinForms और WPF अनुप्रयोगों में यह डिफ़ॉल्ट रूप से होगा, जबकि कंसोल अनुप्रयोग या तो कस्टम एक का उपयोग कर सकते हैं, या WPF से DispatcherSynchronizationContext उधार ले सकते हैं।

यहाँ दो महान ब्लॉग पोस्ट को विस्तार से यांत्रिकी को स्पष्ट कर रहे हैं:
Await, SynchronizationContext, and Console Apps
Await, SynchronizationContext, and Console Apps: Part 2

0

हाँ, आपको एक ही थ्रेड पर लेनदेन को रखना होगा। चूंकि आप एसिंक एक्शन से पहले लेन-देन बना रहे हैं, और इसे एसिंक एक्शन में इस्तेमाल करते हैं, इसलिए लेन-देन का उपयोग एक थ्रेड में नहीं किया जाता है। TransactionScope इस तरह इस्तेमाल करने के लिए डिज़ाइन नहीं किया गया था।

एक साधारण समाधान मुझे लगता है कि ट्रांज़ेक्शनस्कोप ऑब्जेक्ट और कनेक्शन ऑब्जेक्ट के निर्माण को एसिंक एक्शन में स्थानांतरित करना होगा।

अद्यतन

के बाद से async कार्रवाई SqlConnection ऑब्जेक्ट के अंदर है, हम चाहते हैं कि परिवर्तन नहीं कर सकते। हम क्या कर सकते हैं, enlist the connection in the transaction scope है। मैं एक एसिंक फैशन में कनेक्शन ऑब्जेक्ट बनाउंगा, और उसके बाद लेनदेन का दायरा बनाउंगा, और लेनदेन को सूचीबद्ध करूँगा।

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

क्या आप अपने दूसरे बिंदु पर विस्तार कर सकते हैं? सृजन को कहाँ ले जाएं? – chase

+0

क्या आप फिर कनेक्शन को असीमित रूप से खोलने का सुझाव दे रहे हैं, और वास्तविक वर्कलोड के लिए ब्लॉकिंग कॉल का उपयोग करते हैं? या मैं फिर से कुछ याद कर रहा हूँ? – chase

+0

इस समय आपके प्रश्न में आप केवल कनेक्शन को असीमित रूप से प्राप्त करते हैं। मैंने आपके उदाहरण का पालन किया है, लेकिन यदि आप कुछ वास्तविक वर्कलोड के साथ अपना प्रश्न अपडेट करते हैं, तो मैं अपना जवाब भी अपडेट कर सकता हूं। – Maarten

76

.नेट फ्रेमवर्क 4.5.1 में, वहाँ new constructors for TransactionScope कि एक TransactionScopeAsyncFlowOption पैरामीटर लेने का एक सेट है।

एमएसडीएन के मुताबिक, यह थ्रेड निरंतरता में लेनदेन प्रवाह को सक्षम बनाता है।

मेरे समझ है कि यह आप इस तरह कोड लिखने की अनुमति देने के लिए किया जाता है:

// 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

कोशिश की और यह काम करता है। शीर्ष पर ईएफ 6 और लेनदेन के दायरे का उपयोग करना। –

+0

बिल्कुल सही, धन्यवाद! –

+0

क्या होगा यदि आप 4.5.1 पर अपग्रेड नहीं कर सकते हैं? तब समाधान क्या है? – JobaDiniz

संबंधित मुद्दे