2009-01-06 6 views
34

मान लीजिए किसी (मुझे छोड़कर) निम्नलिखित कोड लिखते हैं और एक विधानसभा में संकलित:क्या मुझे एसक्यूएलकनेक्शन ऑब्जेक्ट से लंबित लेनदेन का संदर्भ मिल सकता है?

using (SqlConnection conn = new SqlConnection(connString)) 
{ 
    conn.Open(); 
    using (var transaction = conn.BeginTransaction()) 
    { 
     /* Update something in the database */ 
     /* Then call any registered OnUpdate handlers */ 
     InvokeOnUpdate(conn); 

     transaction.Commit(); 
    } 
} 

InvokeOnUpdate (IDbConnection Conn) करने के लिए कॉल एक ईवेंट हैंडलर है कि मैं लागू करने और रजिस्टर कर सकते हैं के लिए बाहर कॉल। इस प्रकार, इस हैंडलर में मुझे आईडीबीकनेक्शन ऑब्जेक्ट का संदर्भ होगा, लेकिन मेरे पास लंबित लेनदेन का संदर्भ नहीं होगा। क्या कोई तरीका है जिसमें मैं लेनदेन को पकड़ सकता हूं? मेरी OnUpdate हैंडलर में मैं निम्न जैसा कुछ पर अमल करना चाहते हैं:

private void MyOnUpdateHandler(IDbConnection conn) 
{ 
    var cmd = conn.CreateCommand(); 
    cmd.CommandText = someSQLString; 
    cmd.CommandType = CommandType.Text; 

    cmd.ExecuteNonQuery(); 
} 

हालांकि, cmd.ExecuteNonQuery() करने के लिए कॉल एक InvalidOperationException शिकायत की कि

"ExecuteNonQuery आदेश की आवश्यकता के लिए फेंकता एक लेनदेन जब कमांड को असाइन किया गया कनेक्शन लंबित स्थानीय लेनदेन में है। कमांड की लेनदेन संपत्ति प्रारंभ नहीं की गई है "।

क्या मैं किसी भी तरह से लंबित लेनदेन के साथ अपने SQLCommand cmd को सूचीबद्ध कर सकता हूं? क्या मैं आईडीबीकनेक्शन ऑब्जेक्ट से लंबित लेनदेन का संदर्भ पुनर्प्राप्त कर सकता हूं (यदि आवश्यक हो तो मुझे प्रतिबिंब का उपयोग करने में खुशी होगी)?

उत्तर

7

वाह मैंने पहले इसे विश्वास नहीं किया था। मुझे हैरान है कि CreateCommand() स्थानीय SQL सर्वर लेनदेन का उपयोग करते समय यह लेनदेन का आदेश नहीं देता है, और लेनदेन SqlConnection ऑब्जेक्ट पर प्रकट नहीं होता है। दरअसल जब एसक्यूएलकनेक्शन पर प्रतिबिंबित होता है तो वर्तमान लेनदेन उस वस्तु में भी संग्रहीत नहीं होता है। संपादन में, मैंने आपको कुछ आंतरिक संकेतों के माध्यम से ऑब्जेक्ट को ट्रैक करने के लिए कुछ संकेत दिए।

मुझे पता है कि आप विधि को संशोधित नहीं कर सकते हैं, लेकिन क्या आप विधि पट्टी के चारों ओर एक ट्रांज़ेक्शनस्कोप का उपयोग कर सकते हैं? तो अगर आप हैं:

public static void CallingFooBar() 
{ 
    using (var ts=new TransactionScope()) 
    { 
     var foo=new Foo(); 
     foo.Bar(); 
     ts.Complete(); 
    } 
} 

यह काम करेंगे, मैं तुम्हारा के लिए simillar कोड का उपयोग कर परीक्षण किया है और एक बार मैं आवरण जोड़ने सब ठीक काम करता है अगर आप निश्चित रूप से ऐसा कर सकते हैं। जैसा कि इंगित किया गया है कि यदि लेनदेनस्कोप के भीतर एक कनेक्शन खोला गया है तो आपको एक वितरित लेनदेन में आगे बढ़ाया जाएगा, जब तक कि आपके सिस्टम को उनके लिए कॉन्फ़िगर नहीं किया जाता है, आपको एक त्रुटि मिल जाएगी।

डीटीसी के साथ जुड़ना भी स्थानीय लेनदेन के बाद कई बार धीमा है।

संपादित

यदि आप वास्तव में कोशिश करते हैं और उपयोग प्रतिबिंब करना चाहते हैं, SqlConnection एक SqlInternalConnection है इस बारी में AvailableInternalTransaction की एक संपत्ति है जो एक SqlInternalTransaction देता है, यह माता पिता की संपत्ति जो SqlTransaction आप आवश्यकता होगी रिटर्न किया है ।

3

कमांड ऑब्जेक्ट को केवल अपने रचनाकारों में से एक का उपयोग करके एक लेनदेन वस्तु असाइन की जा सकती है। आप .NET 2.0 दृष्टिकोण के लिए जा सकते हैं और एक TransactionScope ऑब्जेक्ट का उपयोग कर सकते हैं जिसे System.Transactions नेमस्पेस में परिभाषित किया गया है (एक समर्पित असेंबली है)।

using System.Transactions; 

    class Foo 
    { 
     void Bar() 
     { 
      using (TransactionScope scope = new TransactionScope()) 
      { 
       // Data access 
       // ... 
       scope.Complete() 
      } 
     } 
    } 

System.Transactions दृष्टिकोण SQL सर्वर 2005 एक हल्के लेन-देन समन्वयक (LTM) के साथ संयोजन के रूप में उपयोग करता है। अपने लेन-देन के दायरे में एकाधिक कनेक्शन ऑब्जेक्ट्स का उपयोग न करने के लिए सावधान रहें या लेनदेन को प्रचारित किया जाएगा क्योंकि इसे वितरित के रूप में देखा जाता है। लेनदेन के इस संसाधन-गहन संस्करण को फिर डीटीसी द्वारा संभाला जाएगा।

0

मैं सरल का एक बड़ा समर्थक हूं, इसलिए लेनदेन का खुलासा करने वाले आईडीबीएन कनेक्शन (डिलीजेट पैटर्न) पर एक रैपर लिखने के बारे में। इस तरह (VB.NET कोड के लिए क्षमा करें, मैं इस VB.NET में लिख रहा हूँ अभी) कुछ:

Public class MyConnection 
     Implements IDbConnection 

     Private itsConnection as IDbConnection 
     Private itsTransaction as IDbTransaction 

     Public Sub New(ByVal conn as IDbConnection) 
     itsConnection = conn 
     End Sub 

     //... 'All the implementations would look like 
     Public Sub Dispose() Implements IDbConnection.Dispose 
     itsConnection.Dispose() 
     End Sub 
     //... 

     //  'Except BeginTransaction which would look like 
     Public Overridable Function BeginTransaction() As IDbTransaction Implements IDbConnection.BeginTransaction 
     itsTransaction = itsConnection.BeginTransaction() 
     Return itsTransaction 
     End Function 


     // 'Now you can create a property and use it everywhere without any hacks 
     Public ReadOnly Property Transaction 
      Get 
       return itsTransaction 
      End Get 
     End Property 

    End Class 

तो आप के रूप में इस का दृष्टांत होगा:

Dim myConn as new MyConnection(new SqlConnection(...)) 

और फिर आप प्राप्त कर सकते हैं लेन-देन का उपयोग करके किसी भी समय:

myConn.Transaction 
+0

मुझे गलत हो सकता है, लेकिन मुझे लगता है कि आपके सुझाव की आवश्यकता है कि मेरे पास तीसरे पक्ष की लाइब्रेरी हो सकती है जिसे मैं एक सादे SqlConnection के बजाय एक MyConnection को तत्काल उपयोग कर रहा हूं। दुर्भाग्य से, मेरे पास स्रोत कोड नहीं है और यह किसी भी आकार या रूप में निर्भरता इंजेक्शन का समर्थन नहीं करता है। – Rune

+0

सुनिश्चित नहीं है कि आपका क्या मतलब है। आपके मामले में आपकी "MyConnection" कक्षा SqlConnection, OleConnection या आप एक तर्क के रूप में क्या ले जाएगा। कोई इंजेक्शन जरूरी नहीं है। इस पर एक नज़र डालें कि "प्रतिनिधि डिजाइन पैटर्न" यहां कैसे काम करता है: http://en.wikipedia.org/wiki/Delegation_pattern – Denis

+0

मुझे लगता है कि आपका क्या मतलब है। आपका मामला मुश्किल है क्योंकि आप तीसरे कनेक्शन को अपना कनेक्शन बना रहे हैं, मेरे मामले में मैं कई कनेक्शन बना रहा हूं लेकिन वे मेरे ऐप में ग्लोबल हैं इसलिए मैं लगातार लेनदेन खोल रहा हूं कि कौन सा लेनदेन खोल रहा है और मैं लेनदेन रखना चाहता हूं मेरे कनेक्शन। आपके मामले में मुझे लगता है कि आपके तीसरे पक्ष के निर्माण कनेक्शन को MyConnection में पारित करने के बाद लेनदेन ट्रैक किया जाएगा। आपको पता लगाना होगा कि क्या तीसरे पक्ष ने आपको कॉन पकड़ने से पहले लेनदेन बनाया है, तो शुरुआती लेनदेन को खोजने के लिए प्रतिबिंब का उपयोग करने के लिए आपके पास थोड़ा विकल्प नहीं होगा। – Denis

4

किसी के लिए जो डेकोरेटर वर्ग कि डेनिस VB.NET में किए गए की सी # संस्करण में रुचि रखता है, यहाँ यह है:

0,123,
using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Data; 

namespace DataAccessLayer 
{ 
    /// <summary> 
    /// Decorator for the connection class, exposing additional info like it's transaction. 
    /// </summary> 
    public class ConnectionWithExtraInfo : IDbConnection 
    { 
     private IDbConnection connection = null; 
     private IDbTransaction transaction = null; 

     public IDbConnection Connection 
     { 
      get { return connection; } 
     } 

     public IDbTransaction Transaction 
     { 
      get { return transaction; } 
     } 

     public ConnectionWithExtraInfo(IDbConnection connection) 
     { 
      this.connection = connection; 
     } 

     #region IDbConnection Members 

     public IDbTransaction BeginTransaction(IsolationLevel il) 
     { 
      transaction = connection.BeginTransaction(il); 
      return transaction; 
     } 

     public IDbTransaction BeginTransaction() 
     { 
      transaction = connection.BeginTransaction(); 
      return transaction; 
     } 

     public void ChangeDatabase(string databaseName) 
     { 
      connection.ChangeDatabase(databaseName); 
     } 

     public void Close() 
     { 
      connection.Close(); 
     } 

     public string ConnectionString 
     { 
      get 
      { 
       return connection.ConnectionString; 
      } 
      set 
      { 
       connection.ConnectionString = value; 
      } 
     } 

     public int ConnectionTimeout 
     { 
      get { return connection.ConnectionTimeout; } 
     } 

     public IDbCommand CreateCommand() 
     { 
      return connection.CreateCommand(); 
     } 

     public string Database 
     { 
      get { return connection.Database; } 
     } 

     public void Open() 
     { 
      connection.Open(); 
     } 

     public ConnectionState State 
     { 
      get { return connection.State; } 
     } 

     #endregion 

     #region IDisposable Members 

     public void Dispose() 
     { 
      connection.Dispose(); 
     } 

     #endregion 
    } 
} 
+0

के बजाय MyConnection इंजेक्ट करने की अनुमति देते हैं, मैं इसका उपयोग नहीं करता। कनेक्शन पर सक्रिय लेनदेन होने के बाद आप इनमें से एक को तुरंत चालू कर सकते हैं, और उस स्थिति में यह काम नहीं करेगा। – AyCabron

+0

आप सही हैं, यह केवल तभी काम करता है जब आप एक ऐसे कनेक्शन को पास करते हैं जिसमें सक्रिय लेनदेन नहीं है ... आपके मामले के लिए कनेक्शन से पहले कनेक्शन के रूप में कनेक्शन कनेक्शन के बजाय कनेक्शन कनेक्शन में नया कनेक्शन बनाना बेहतर समाधान है। एक पैरामीटर सबसे अच्छी बात यह है कि कनेक्शन ऑब्जेक्ट से लेनदेन तक सीधी पहुंच होगी, और इस सजावटी वर्ग की आवश्यकता नहीं होगी ... – victorvartan

16

मामले में किसी को भी प्रतिबिंब कोड में रुचि रखता है यह पूरा करने के लिए, यहाँ यह जाता है:

private static readonly PropertyInfo ConnectionInfo = typeof(SqlConnection).GetProperty("InnerConnection", BindingFlags.NonPublic | BindingFlags.Instance); 
    private static SqlTransaction GetTransaction(IDbConnection conn) { 
     var internalConn = ConnectionInfo.GetValue(conn, null); 
     var currentTransactionProperty = internalConn.GetType().GetProperty("CurrentTransaction", BindingFlags.NonPublic | BindingFlags.Instance); 
     var currentTransaction = currentTransactionProperty.GetValue(internalConn, null); 
     var realTransactionProperty = currentTransaction.GetType().GetProperty("Parent", BindingFlags.NonPublic | BindingFlags.Instance); 
     var realTransaction = realTransactionProperty.GetValue(currentTransaction, null); 
     return (SqlTransaction) realTransaction; 
    } 

नोट्स:

  • प्रकार आंतरिक रहे हैं और गुण निजी इसलिए आप नहीं कर सकते गतिशील
  • आंतरिक प्रकारों का उपयोग आप मध्यवर्ती प्रकारों को घोषित करने से रोकते हैं जैसा मैंने पहले कनेक्शन इंफो के साथ किया था। होगा वस्तुओं
+2

आगे बढ़ने से पहले वर्तमान ट्रांजैक्शन की जांच करना एक अच्छा विचार हो सकता है। अगर (वर्तमान ट्रान्सएक्शन == नल) वापस शून्य; – kerem

-2

पर GetType का उपयोग मामले में जहां किसी को भी नेट 4.5 पर इस समस्या का सामना करना पड़ा आप System.Transactions में Transaction.Current उपयोग कर सकते हैं।

+0

जो वर्तमान ट्रांज़ेक्शनस्कोप को पुनर्प्राप्त करता है, एसक्यूएलट्रैक्शन नहीं। समान, लेकिन अलग तकनीक। –

+0

आप इस सवाल में कहां देखते हैं कि उसे एसक्लट्रांसक्शन की आवश्यकता है और लेनदेनस्कोप नहीं है? – A77

+0

उनके प्रश्न की पहली 5 पंक्तियां। "मान लीजिए किसी (मुझे छोड़कर) निम्नलिखित कोड लिखते हैं और एक विधानसभा में संकलित: का उपयोग कर (SqlConnection Conn = नए SqlConnection (connString)) { conn.Open(); का उपयोग कर (वर लेनदेन = ** Conn .BeginTransaction()) ** " –

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