2010-07-06 21 views
15

Tengo un objeto principal (parte de un DAL) que contiene, entre otras cosas, una colección (List<t>) de objetos secundarios.TransactionScope: evitar transacciones distribuidas

Cuando guardo el objeto en la base de datos, ingreso/actualizo el elemento primario y luego recorro cada elemento secundario. Para la mantenibilidad, he puesto todo el código para el niño en un método privado separado.

Iba a utilizar transacciones ADO estándar, pero en mis viajes tropecé con el objeto TransactionScope, que creo que me permitirá ajustar todas las interacciones DB en el método padre (junto con todas las interacciones en el método secundario) en una transacción

Hasta ahora todo bien ..?

Así que la siguiente pregunta es cómo crear y usar conexiones dentro de este TransactionScope. He oído que el uso de conexiones múltiples, incluso si están en la misma base de datos, puede obligar a TransactionScope a pensar que se trata de una transacción distribuida (que implica un costoso trabajo de DTC).

¿Es el caso? ¿O es que, como parece estar leyendo en otro lado, un caso que usar la misma cadena de conexión (que se prestará a la agrupación de conexiones) estará bien?

Más prácticamente hablando, puedo ...

  1. crear conexiones separadas en el niño los padres & (aunque con la misma cadena de conexión)
  2. crear un enlace en la matriz a través de un pase de como parámetro (me parece torpe)
  3. ¿Hace algo más ...?

ACTUALIZACIÓN:

Aunque parece que iba a estar bien usando mi .NET3.5 habitual + y SQL Server 2008+, otra parte de este proyecto va a utilizar Oracle (10g) por lo que también podría practicar una técnica que pueda usarse de manera consistente en todos los proyectos.

Así que simplemente pasaré la conexión a través de los métodos secundarios. 1 Código


opción de la muestra:

using (TransactionScope ts = new TransactionScope()) 
      { 
       using (SqlConnection conn = new SqlConnection(connString)) 
       { 
        using (SqlCommand cmd = new SqlCommand()) 
        { 
         cmd.Connection = conn; 
         cmd.Connection.Open(); 
         cmd.CommandType = CommandType.StoredProcedure; 

         try 
         { 
          //create & add parameters to command 

          //save parent object to DB 
          cmd.ExecuteNonQuery(); 

          if ((int)cmd.Parameters["@Result"].Value != 0) 
          { 
           //not ok 
           //rollback transaction 
           ts.Dispose(); 
           return false; 
          } 
          else //enquiry saved OK 
          { 
           if (update) 
           { 
            enquiryID = (int)cmd.Parameters["@EnquiryID"].Value; 
           } 

           //Save Vehicles (child objects) 
           if (SaveVehiclesToEPE()) 
           { 
            ts.Complete(); 
            return true; 
           } 
           else 
           { 
            ts.Dispose(); 
            return false; 
           } 
          } 
         } 
         catch (Exception ex) 
         { 
          //log error 
          ts.Dispose(); 
          throw; 
         } 
        } 
       } 
      } 
+3

Ver [TransactionScope escalando automáticamente a MSDTC en algunas máquinas?] (Http://stackoverflow.com/questions/1690892/transactionscope-automatically-escalating-to-msdtc-on-some-machines/1693795#1693795). Hay varias buenas respuestas, pero la que he vinculado es la más sucinta (y relevante para su pregunta). El resultado es que, si está utilizando .NET 2.0 y SQL Server 2005, escalará aún utilizando dos conexiones con la misma cadena de conexión. Esto no es un problema con .NET 3.5 y SQL Server 2008. –

+0

I * estoy * usando .NET 3.5/4 y SQL 2008 en general, pero de vez en cuando podría usar SQL2005/2000, así que vale la pena recordarlo de todos modos. Gracias – CJM

+0

¿alguien puede darme algún conocimiento sobre lo que se distribuye la transacción. explicar con el ejemplo. – Thomas

Respuesta

24

Muchos proveedores de bases de datos ADO (como Oracle ODP.NET) comienzan transacciones distribuidas cuando usa TransactionScope para realizar transacciones a través de varias conexiones, incluso cuando comparten la misma cadena de conexión.

Algunos proveedores (como SQL2008 en .NET 3.5+) reconocen cuándo se crea una nueva conexión en un ámbito de transacción que hace referencia a la misma cadena de conexión y no da como resultado el trabajo de DTC. Pero cualquier variación en la cadena de conexión (como los parámetros de ajuste) puede impedir que esto ocurra, y el comportamiento volverá a usar una transacción distribuida.

Desafortunadamente, la única manera confiable de asegurar que sus transacciones funcionen juntas sin crear una transacción distribuida es pasar el objeto de conexión (o el IDbTransaction) a métodos que necesitan "continuar" en la misma transacción.

A veces ayuda elevar la conexión a un miembro de la clase en la que está haciendo el trabajo, pero esto puede crear situaciones incómodas y complica el control de la vida útil y la eliminación del objeto de conexión (ya que generalmente excluye el uso de la declaración using).

+0

Tenía la impresión de que la conexión debe crearse dentro de TransactionScope para que se cubra. Supongo que simplemente pasar la conexión es más simple y ordenado que encontrar alguna otra solución. – CJM

+0

¿Sabe si esto se cambia con Oracle 12c? Lo veo marcado como "Disponible en ODAC 12c o posterior". aquí: https://apex.oracle.com/pls/apex/f?p = 18357: 39: 1473540763666 :: NO :: P39_ID: 27121 – pauloya

1

En su ejemplo la TransactionScope se encuentra todavía en el contexto de un método, usted podría simplemente crear un SqlTransaction con múltiples comandos debajo de eso. Use TransactionScope si desea mover la transacción fuera de un método, para decir, la persona que llama de ese método, o si accede a múltiples bases de datos.

Actualización: no importa Acabo de ver la llamada de un niño. En esta situación, podría pasar el objeto de conexión a clases secundarias. Además, no es necesario que deseche manualmente el TransactionScope, ya que los bloques actúan como bloques try-finally y ejecutarán el desecho incluso con excepciones.

Actualización 2: mejor aún, pase el IDbTransaction a la clase secundaria. La conexión se puede recuperar de eso.

+0

Sí, entendí que la eliminación no era explícitamente necesaria, pero cuando adapté TransactionScope a mi código, claramente me olvidé y continué convirtiendo mi antiguo trm. Declaraciones de devolución para ts.Disfrutar sin pensar. ¡Buen lugar! – CJM

+0

Re ** Actualización 2 ** -Puede simplemente pasar el 'IDbConnection' a la clase secundaria. Si usa 'IbConnection.BeginTransaction()', todos los comandos creados con 'IDbConnection.CreateCommand()' tendrán automáticamente la transacción asociada. Esto es mejor porque es un parámetro menos para pasar su jerarquía, y reduce el acoplamiento ya que las clases secundarias no tendrán que preocuparse de si se están ejecutando dentro de una transacción o no. –

2

Empíricamente, he determinado que (por el proveedor de SQL Server) si el proceso puede tomar ventaja de la agrupación de conexiones para compartir la conexión (y la transacción) entre los procesos padre e hijo, el DTC no necesariamente va a convertirse involucrado.

Este es un gran "si", sin embargo, según su ejemplo, la conexión creada por el proceso principal no puede ser compartida por los procesos secundarios (no cierra/libera la conexión antes de invocar los procesos secundarios). Esto dará lugar a una transacción que abarca dos conexiones reales, lo que dará lugar a que la transacción se promueva a una transacción distribuida.

Parece que sería fácil refactorizar su código para evitar este escenario: simplemente cierre la conexión creada por el proceso principal antes de invocar los procesos secundarios.

+0

¿Está diciendo que si paso por el objeto de conexión, tengo que cerrar en el elemento primario y volver a abrir el elemento secundario? – CJM

+0

No. Asumí que estaba intentando aprovechar la ventaja de TransactionScope sin hacer que la transacción se promoviera a una transacción distribuida. Parece que esto no es posible cuando se apunta a bases de datos Oracle, pero es posible atacar las bases de datos de SQL Server ... siempre que las conexiones se puedan agrupar y solo haya una conexión abierta a la vez. –

Cuestiones relacionadas