किन परिस्थितियों में एक SqlConnection स्वचालित रूप से एक परिवेश TransactionScope लेनदेन में सूचीबद्ध है?


201

SqlConnection के लिए लेनदेन में "सूचीबद्ध" होने का क्या मतलब है? क्या इसका सीधा मतलब यह है कि कनेक्शन पर मैं जो कमांड निष्पादित करता हूं वह लेनदेन में भाग लेगा?

यदि हां, तो किन परिस्थितियों में एक SqlConnection स्वचालित रूप से एक परिवेश TransactionScope लेनदेन में सूचीबद्ध है?

कोड टिप्पणियों में प्रश्न देखें। प्रत्येक प्रश्न के उत्तर के लिए मेरा अनुमान कोष्ठक में प्रत्येक प्रश्न का अनुसरण करता है।

परिदृश्य 1: कनेक्शन खोलना एक लेन-देन गुंजाइश है

using (TransactionScope scope = new TransactionScope())
using (SqlConnection conn = ConnectToDB())
{   
    // Q1: Is connection automatically enlisted in transaction? (Yes?)
    //
    // Q2: If I open (and run commands on) a second connection now,
    // with an identical connection string,
    // what, if any, is the relationship of this second connection to the first?
    //
    // Q3: Will this second connection's automatic enlistment
    // in the current transaction scope cause the transaction to be
    // escalated to a distributed transaction? (Yes?)
}

परिदृश्य 2: कनेक्शन का उपयोग करना एक लेन-देन गुंजाइश है जो इसके बारे में खोला गया था

//Assume no ambient transaction active now
SqlConnection new_or_existing_connection = ConnectToDB(); //or passed in as method parameter
using (TransactionScope scope = new TransactionScope())
{
    // Connection was opened before transaction scope was created
    // Q4: If I start executing commands on the connection now,
    // will it automatically become enlisted in the current transaction scope? (No?)
    //
    // Q5: If not enlisted, will commands I execute on the connection now
    // participate in the ambient transaction? (No?)
    //
    // Q6: If commands on this connection are
    // not participating in the current transaction, will they be committed
    // even if rollback the current transaction scope? (Yes?)
    //
    // If my thoughts are correct, all of the above is disturbing,
    // because it would look like I'm executing commands
    // in a transaction scope, when in fact I'm not at all, 
    // until I do the following...
    //
    // Now enlisting existing connection in current transaction
    conn.EnlistTransaction( Transaction.Current );
    //
    // Q7: Does the above method explicitly enlist the pre-existing connection
    // in the current ambient transaction, so that commands I
    // execute on the connection now participate in the
    // ambient transaction? (Yes?)
    //
    // Q8: If the existing connection was already enlisted in a transaction
    // when I called the above method, what would happen?  Might an error be thrown? (Probably?)
    //
    // Q9: If the existing connection was already enlisted in a transaction
    // and I did NOT call the above method to enlist it, would any commands
    // I execute on it participate in it's existing transaction rather than
    // the current transaction scope. (Yes?)
}

जवाबों:


188

मैंने इस प्रश्न को पूछने के बाद से कुछ परीक्षण किए हैं और सबसे अधिक पाया गया यदि सभी उत्तर अपने आप नहीं हैं, क्योंकि किसी और ने उत्तर नहीं दिया है। कृपया मुझे बताएं कि क्या मैंने कुछ भी याद किया है।

Q1। हां, जब तक कनेक्शन स्ट्रिंग में "एनलिस्ट = गलत" निर्दिष्ट नहीं किया जाता है। कनेक्शन पूल एक उपयोगी कनेक्शन पाता है। एक प्रयोग करने योग्य कनेक्शन वह है जिसे किसी लेन-देन में सूचीबद्ध नहीं किया गया है या उसी लेनदेन में सूचीबद्ध किया गया है।

Q2। दूसरा कनेक्शन एक स्वतंत्र कनेक्शन है, जो उसी लेनदेन में भाग लेता है। मैं इन दो कनेक्शनों पर कमांडों की बातचीत के बारे में निश्चित नहीं हूं, क्योंकि वे एक ही डेटाबेस के खिलाफ चल रहे हैं, लेकिन मुझे लगता है कि यदि एक ही समय में दोनों पर आदेश जारी किए जाते हैं तो त्रुटियां हो सकती हैं: "उपयोग में लेनदेन का संदर्भ" जैसी त्रुटियां एक और सत्र "

Q3। हां, यह एक वितरित लेनदेन में बढ़ जाता है, इसलिए एक ही कनेक्शन स्ट्रिंग के साथ, एक से अधिक कनेक्शन की सूची बनाकर, यह एक वितरित लेनदेन बनने का कारण बनता है, जिसे Transaction.Current.ransactionIn पर एक गैर-शून्य GUID के लिए जाँच कर पुष्टि की जा सकती है। .DistributedIdentifier। * अद्यतन: मैंने कहीं पढ़ा है कि यह SQL Server 2008 में तय किया गया है, ताकि MSDTC का उपयोग तब न किया जाए जब दोनों कनेक्शन के लिए एक ही कनेक्शन स्ट्रिंग का उपयोग किया जाता है (जब तक कि दोनों कनेक्शन एक ही समय में खुले नहीं हों)। यह आपको एक कनेक्शन खोलने और एक लेन-देन के भीतर कई बार इसे बंद करने की अनुमति देता है, जो कि कनेक्शन को देर से खोलना और जितनी जल्दी हो सके उन्हें बंद करके कनेक्शन पूल का बेहतर उपयोग कर सकता है।

Q4। नहीं लेन-देन का दायरा सक्रिय होने पर खोला गया कनेक्शन, नए बनाए गए लेनदेन दायरे में स्वचालित रूप से सूचीबद्ध नहीं होगा।

क्यू 5। जब तक आप लेन-देन के दायरे में एक कनेक्शन नहीं खोलते हैं, या दायरे में मौजूदा कनेक्शन को सूचीबद्ध नहीं करते हैं, तो मूल रूप से कोई लेनदेन नहीं होता है। लेन-देन में भाग लेने के लिए आपका कनेक्शन स्वचालित रूप से या मैन्युअल रूप से लेन-देन के दायरे में सूचीबद्ध होना चाहिए।

Q6। हां, लेन-देन में भाग नहीं लेने वाले कनेक्शन पर आदेश जारी किए गए हैं, भले ही कोड एक लेनदेन स्कोप ब्लॉक में निष्पादित होने के लिए होता है जो वापस लुढ़क गया। यदि कनेक्शन को वर्तमान लेन-देन के दायरे में सूचीबद्ध नहीं किया गया है, तो वह लेन-देन में भाग नहीं ले रहा है, इसलिए लेनदेन को कमिट या रोल करने से लेन-देन के दायरे में सूचीबद्ध नहीं किए गए कनेक्शन पर जारी किए गए आदेशों पर कोई प्रभाव नहीं पड़ेगा ... जैसा कि इस लड़के को पता चला है । यही कारण है कि एक बहुत ही मुश्किल से एक को पहचानना यदि आप स्वत: भर्ती प्रक्रिया को समझने के लिए: यह तब होता है जब केवल एक कनेक्शन खोला जाता है के अंदर एक सक्रिय लेन-देन गुंजाइश।

क्यू 7। हाँ। मौजूदा लेन-देन को मौजूदा लेन-देन दायरे में EnlistTransaction (Transaction.Current) कहकर स्पष्ट रूप से सूचीबद्ध किया जा सकता है। आप डिपेंडेंटट्रांसलेशन का उपयोग करके लेन-देन में एक अलग थ्रेड पर एक कनेक्शन भी संलग्न कर सकते हैं, लेकिन पहले की तरह, मुझे यकीन नहीं है कि एक ही डेटाबेस के खिलाफ एक ही लेनदेन में शामिल दो कनेक्शन कैसे बातचीत कर सकते हैं ... और त्रुटियां हो सकती हैं, और निश्चित रूप से दूसरा सूचीबद्ध कनेक्शन लेन-देन को वितरित लेनदेन में वृद्धि का कारण बनता है।

प्रश्न 8। कोई त्रुटि डाली जा सकती है। यदि TransactionScopeOption.Required का उपयोग किया गया था, और कनेक्शन पहले से ही लेनदेन गुंजाइश लेनदेन में सूचीबद्ध किया गया था, तो कोई त्रुटि नहीं है; वास्तव में, गुंजाइश के लिए कोई नया लेनदेन नहीं बनाया गया है, और लेनदेन की गणना (@@ ट्रांसकाउंट) नहीं बढ़ती है। यदि, हालांकि, आप TransactionScopeOption.RequiresNew का उपयोग करते हैं, तो आपको नए लेनदेन गुंजाइश लेनदेन में कनेक्शन को लागू करने के प्रयास में एक उपयोगी त्रुटि संदेश मिलता है: "वर्तमान में कनेक्शन में लेन-देन सूचीबद्ध है। वर्तमान लेन-देन समाप्त करें और पुनः प्रयास करें।" और हां, यदि आप लेन-देन को पूरा करते हैं, तो कनेक्शन को सूचीबद्ध किया जाता है, तो आप नए लेनदेन में कनेक्शन को सुरक्षित रूप से सूचीबद्ध कर सकते हैं। अद्यतन: यदि आपको पहले कनेक्शन पर बिग्रेडट्रांसशन कहा जाता है, तो एक नया लेनदेन गुंजाइश लेनदेन में सूचीबद्ध करने की कोशिश करने पर थोड़ी अलग त्रुटि होती है: "लेन-देन में सूचीबद्ध नहीं कर सकते क्योंकि कनेक्शन पर एक स्थानीय लेनदेन प्रगति पर है। स्थानीय लेनदेन समाप्त करें। पुनः प्रयास करें। " दूसरी ओर, आप सुरक्षित रूप से SqlConnection पर BeginTransaction को कॉल कर सकते हैं, जबकि इसके लेन-देन की गुंजाइश लेनदेन में सूचीबद्ध है, और यह वास्तव में एक नेस्टेड ट्रांजेक्शन स्कोप के आवश्यक विकल्प का उपयोग करने के विपरीत, @@ trancount को बढ़ाएगा, जिसके कारण यह नहीं होता है। बढ़ना। दिलचस्प बात यह है कि यदि आप आवश्यक विकल्प के साथ एक और नेस्टेड ट्रांजैक्शन स्कोप बनाने के लिए जाते हैं, तो आपको त्रुटि नहीं मिलेगी,

प्रश्न 9। हाँ। सीमेंस # कोड में सक्रिय लेन-देन की गुंजाइश क्या है, इसकी परवाह किए बिना, जो भी लेन-देन में सूचीबद्ध है, उसमें कमांड भाग लेते हैं।


11
Q8 का उत्तर लिखने के बाद, मुझे एहसास हुआ कि यह सामान जादू के नियमों के रूप में जटिल लग रहा है: सभा! सिवाय इसके और भी बुरा है, क्योंकि TransactionScope प्रलेखन इस बारे में कोई व्याख्या नहीं करता है।
ट्रायंको

Q3 के लिए, क्या आप एक ही कनेक्शन स्ट्रिंग का उपयोग करके एक ही समय में दो कनेक्शन खोल रहे हैं? यदि ऐसा है, तो वह एक डिस्ट्रिब्यूटेड ट्रांजैक्शन होगा (SQL सर्वर 2008 के साथ भी)
रैंडी मोनिका का समर्थन करता है

2
नहीं। मैं पोस्ट को स्पष्ट करने के लिए संपादन कर रहा हूं। मेरी समझ यह है कि एक ही समय में दो कनेक्शन खुले होने से SQL सर्वर संस्करण की परवाह किए बिना हमेशा वितरित लेनदेन का कारण होगा। SQL 2008 से पहले, एक बार में केवल एक कनेक्शन खोलने से, एक ही कनेक्शन स्ट्रिंग के कारण अभी भी एक DT होगा, लेकिन SQL 2008 के साथ, एक ही कनेक्शन स्ट्रिंग के साथ समय पर एक कनेक्शन खोलने (कभी एक साथ दो खुले नहीं होने) के कारण नहीं होगा डीटी
त्रिवेंको

1
Q2 के लिए आपके उत्तर को स्पष्ट करने के लिए, यदि वे एक ही धागे पर क्रमिक रूप से किए जाते हैं, तो दोनों कमांड को ठीक चलना चाहिए।
जारेड मूर

2
SQL 2008 में समान कनेक्शन स्ट्रिंग्स के लिए Q3 पदोन्नति समस्या पर, यहाँ MSDN उद्धरण है: msdn.microsoft.com/en-us/library/ms172070(v=vs.90).aspx
pseudocodor

19

अच्छा काम ट्रायंको, आपके जवाब सभी काफी सटीक लगते हैं और मुझे पूरा करते हैं। कुछ अन्य बातें जो मैं बताना चाहूंगा:

(1) मैनुअल एनलिस्टमेंट

ऊपर दिए गए अपने कोड में, आप (सही ढंग से) इस तरह से मैनुअल एनलिस्टमेंट दिखाते हैं:

using (SqlConnection conn = new SqlConnection(connStr))
{
    conn.Open();
    using (TransactionScope ts = new TransactionScope())
    {
        conn.EnlistTransaction(Transaction.Current);
    }
}

हालाँकि, कनेक्शन स्ट्रिंग में Enlist = false का उपयोग करके इसे इस तरह करना भी संभव है।

string connStr = "...; Enlist = false";
using (TransactionScope ts = new TransactionScope())
{
    using (SqlConnection conn1 = new SqlConnection(connStr))
    {
        conn1.Open();
        conn1.EnlistTransaction(Transaction.Current);
    }

    using (SqlConnection conn2 = new SqlConnection(connStr))
    {
        conn2.Open();
        conn2.EnlistTransaction(Transaction.Current);
    }
}

यहां एक और बात ध्यान देने वाली है। जब con2 खोला जाता है, तो कनेक्शन पूल कोड यह नहीं जानता है कि आप बाद में इसे उसी लेन-देन में en1 के रूप में सूचीबद्ध करना चाहते हैं, जिसका अर्थ है कि con2 को conn1 की तुलना में एक अलग आंतरिक कनेक्शन दिया गया है। तब जब con2 को सूचीबद्ध किया जाता है, तो अब 2 कनेक्शन सूचीबद्ध हैं, इसलिए लेन-देन को MSDTC को बढ़ावा देना चाहिए। इस पदोन्नति को केवल स्वचालित सूचीकरण का उपयोग करके टाला जा सकता है।

(2) .net 4.0 से पहले, मैं अत्यधिक कनेक्शन स्ट्रिंग में "ट्रांज़ेक्शन बाइंडिंग = स्पष्ट अनबिंड" सेट करने की सलाह देता हूं । यह मुद्दा .net 4.0 में तय किया गया है, जिससे स्पष्ट अनबिंड पूरी तरह अनावश्यक हो गया है।

(३) अपना स्वयं का रोल करना CommittableTransactionऔर उसको सेट Transaction.Currentकरना अनिवार्य रूप से वही है जो TransactionScopeवह करता है। यह शायद ही वास्तव में उपयोगी है, सिर्फ FYI करें।

(4) Transaction.Current थ्रेड-स्टैटिक है। इसका मतलब है कि Transaction.Currentकेवल उस धागे पर सेट किया गया है जिसने बनाया है TransactionScope। एक ही TransactionScope(संभवतः उपयोग Task) को निष्पादित करने वाले कई धागे संभव नहीं हैं।


मैंने अभी इस परिदृश्य का परीक्षण किया है, और जैसा कि आप वर्णन करते हैं, यह काम करता है। इसके अतिरिक्त, भले ही आप स्वत: नामांकन का उपयोग करते हों, यदि आप दूसरा कनेक्शन खोलने से पहले "SqlConnection.ClearAllPools ()" कहते हैं, तो यह वितरित लेनदेन में बढ़ जाता है।
त्रिवेंको

यदि यह सच है, तो लेनदेन में केवल एक एकल "वास्तविक" कनेक्शन हो सकता है। किसी लेन-देन को आगे बढ़ाए बिना किसी लेन-देन में लेन-देन किए बिना लेन-देन को खोलने, बंद करने और फिर से खोलने की क्षमता है, फिर वास्तव में कनेक्शन पूल द्वारा बनाया गया एक भ्रम है , जो सामान्य रूप से निपटाए गए कनेक्शन को छोड़ देगा, और उसी सटीक कनेक्शन को वापस करेगा यदि पुनः -ऑटोमेटिक एनलिस्टमेंट के लिए लिया गया।
त्रिनको

तो आप वास्तव में क्या कह रहे हैं कि यदि आप स्वचालित सूची प्रक्रिया को रोकते हैं, तो जब आप कनेक्शन कनेक्शन के बजाय लेन-देन की गुंजाइश लेनदेन (TST) के अंदर एक नया कनेक्शन फिर से खोलने के लिए जाते हैं, तो सही कनेक्शन को हथियाने के लिए (मूल रूप से एक) TST में सूचीबद्ध), यह काफी उचित रूप से एक पूरी तरह से नए कनेक्शन को पकड़ता है, जिसे जब मैन्युअल रूप से सूचीबद्ध किया जाता है, तो TST के बढ़ने का कारण बनता है।
ट्राइंको

वैसे भी, यह वही है जो मैं अपने उत्तर में Q1 में संकेत कर रहा था जब मैंने उल्लेख किया कि यह तब तक सूचीबद्ध है जब तक कि कनेक्शन स्ट्रिंग में "एनलिस्ट = गलत" निर्दिष्ट नहीं किया जाता है, तब इस बारे में बात की गई कि पूल एक उपयुक्त कनेक्शन कैसे ढूंढता है।
ट्राइंको

जहां तक ​​मल्टी-थ्रेडिंग जाता है, अगर आप Q2 के मेरे उत्तर में लिंक पर जाते हैं, तो आप देखेंगे कि Transaction.Current प्रत्येक थ्रेड के लिए अद्वितीय है, आप आसानी से एक थ्रेड में संदर्भ प्राप्त कर सकते हैं और इसे किसी अन्य थ्रेड को पास कर सकते हैं; हालाँकि, दो अलग-अलग थ्रेड्स से एक TST तक पहुँचने से एक बहुत ही विशिष्ट त्रुटि होती है "दूसरे सत्र द्वारा उपयोग में लेन-देन का संदर्भ"। एक TST को मल्टी-थ्रेड करने के लिए, आपको एक डिपेंडेंटट्रांसलेशन बनाना होगा, लेकिन उस बिंदु पर यह एक वितरित लेनदेन होना चाहिए, क्योंकि आपको वास्तव में एक साथ कमांड और MSDTC चलाने के लिए दूसरे को जोड़ने के लिए एक दूसरे स्वतंत्र कनेक्शन की आवश्यकता है।
त्रियुन्को

1

एक अन्य विचित्र स्थिति जो हमने देखी है, वह यह है कि यदि आप इसका निर्माण करते हैं तो EntityConnectionStringBuilderयह TransactionScope.Currentलेन-देन में शामिल हो जाएगा (और हमें लगता है)। हम डिबगर, जहां में इस 'पाया है TransactionScope.Current' s current.TransactionInformation.internalTransactionशो enlistmentCount == 1का निर्माण, और इससे पहले कि enlistmentCount == 2बाद में।

इससे बचने के लिए इसके अंदर निर्माण करें

using (new TransactionScope(TransactionScopeOption.Suppress))

और संभवतः आपके ऑपरेशन के दायरे से बाहर (हम इसका निर्माण हर बार कर रहे थे जब हमें कनेक्शन की आवश्यकता थी)।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.