هل يمكنني الحصول على إشارة إلى معاملة معلقة من كائن SQLConnection؟

StackOverflow https://stackoverflow.com/questions/417024

سؤال

لنفترض أن شخصًا ما (بخلاف ME) يكتب الرمز التالي ويقوم بتجميعه في مجموعة:

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) إلى معالج حدث يمكنني تنفيذه والتسجيل. وبالتالي ، في هذا المعالج ، سيكون لدي إشارة إلى كائن IDBConnection ، لكن لن يكون لدي إشارة إلى المعاملة المعلقة. هل هناك أي طريقة يمكنني من خلالها الحصول على المعاملة؟ في معالج 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 مع المعاملة المعلقة؟ هل يمكنني استرداد إشارة إلى المعاملة المعلقة من كائن IDBConnection (سأكون سعيدًا لاستخدام التفكير إذا لزم الأمر)؟

هل كانت مفيدة؟

المحلول

واو لم أصدق هذا في البداية. أنا مندهش من ذلك CreateCommand() لا يعطي الأمر معاملة ذلك عند استخدام معاملات خادم SQL المحلية ، وأن المعاملة لا تتعرض على SqlConnection هدف. في الواقع عند التفكير في SqlConnection المعاملة الحالية لا يتم تخزينها في هذا الكائن. في تحرير الصوت ، أعطيتك بعض التلميحات لتعقب الكائن عبر بعض فصولها الداخلية.

أعلم أنه لا يمكنك تعديل الطريقة ولكن هل يمكنك استخدام TransactionsCope حول شريط الطريقة؟ لذلك إذا كان لديك:

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

سيعمل هذا ، لقد اختبرت باستخدام رمز مماثل لك ، وبمجرد إضافة Wrapper ، يعمل كل شيء بشكل جيد إذا كنت تستطيع القيام بذلك بالطبع. كما هو موضح احترس ما إذا كان يتم فتح اتصال واحد داخل TransactionScope سيتم تصعيدك إلى معاملة موزعة والتي ما لم يتم تكوين نظامك لهم ، فستحصل على خطأ.

يعد التجنيد مع DTC أبطأ عدة مرات ثم معاملة محلية.

يحرر

إذا كنت ترغب حقًا في محاولة استخدام التأمل ، فإن SQLConnection لديه sqlinternalconnection ، وهذا بدوره لديه خاصية من متاح stransaction الذي يعيد sqlinternaltransaction ، وهذا يحتوي على خاصية من الوالد الذي يعيد sqltransaction التي ستحتاجها.

نصائح أخرى

في حال كان أي شخص مهتمًا برمز الانعكاس لإنجاز هذا ، فهذا يذهب:

    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;
    }

ملحوظات:

  • الأنواع داخلية وخصائص خاصة بحيث لا يمكنك استخدام الديناميكية
  • تمنعك الأنواع الداخلية أيضًا من إعلان الأنواع المتوسطة كما فعلت مع الاتصال الأول. يجب استخدام Gettype على الكائنات

لأي شخص مهتم بإصدار C# من فئة الديكور التي صنعها دينيس في VB.NET ، ها هو:

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

لا يمكن تعيين كائن الأمر إلا كائن معاملة باستخدام أحد مُنشئاته. يمكنك الذهاب إلى نهج .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 Server 2005 منسق معاملات خفيفة الوزن (LTM). احرص على عدم استخدام كائنات اتصال متعددة في نطاق المعاملة الخاص بك أو سيتم تعزيز المعاملة كما هو موزع. سيتم بعد ذلك معالجة هذه النسخة الأكثر كثافة في الموارد من المعاملة بواسطة DTC.

أنا مؤيد كبير للبسيط ، ماذا عن كتابة غلاف عبر IDBConnection (نمط مندوب) يكشف المعاملة. (آسف لرمز 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

في حالة واجه أي شخص هذه المشكلة على .NET 4.5 يمكنك استخدامه Transaction.Currentفي System.Transactions.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top