Могу ли я получить ссылку на ожидающую транзакцию из объекта SqlConnection? [c#]


Answers

Если кто-то интересуется кодом отражения, чтобы выполнить это, вот оно:

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

Заметки:

  • Типы являются внутренними и частными, поэтому вы не можете использовать динамические
  • внутренние типы также не позволяют вам объявлять промежуточные типы, как это было в первом ConnectionInfo. Должен использовать GetType для объектов
Question

Предположим, что кто-то (кроме меня) записывает следующий код и компилирует его в сборку:

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 требует, чтобы команда имела транзакцию, когда соединение, назначенное команде, находится в ожидающей локальной транзакции. Свойство Transaction команды не было инициализировано».

Могу ли я каким-либо образом включить мой SqlCommand cmd в ожидающую транзакцию? Могу ли я получить ссылку на ожидающую транзакцию из объекта IDbConnection (я был бы рад использовать отражение, если это необходимо)?




Командному объекту может быть назначен только объект транзакции с использованием одного из его конструкторов. Вы можете использовать подход .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.




В случае, если кто-либо столкнулся с этой проблемой на .Net 4.5, вы можете использовать Transaction.Current в System.Transactions .