¿Se pueden incluir cambios de linq a sql y actualizaciones del adaptador de tabla de conjuntos de datos ADO.NET en una sola transacción?

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

Pregunta

Estas son las tecnologías relevantes con las que estoy trabajando:

  • Dot Connect de Devart para Oracle (para facilitar Linq-to-Sql para Oracle).
  • Conjuntos de datos ADO.NET fuertemente tipados.
  • Una base de datos Oracle.

Aquí está el desafío:

  • Mi código heredado envía actualizaciones de bases de datos con conjuntos de datos y adaptadores de tablas ADO.NET.
  • Me gustaría comenzar a convertir ese código a Linq-to-Sql, pero me gustaría hacerlo poco a poco para minimizar la rotación de código y el riesgo.

Aquí está mi esquema de prueba de concepto:

Tabla principal

  • Identificación de los padres
  • Nombre del padre

Mesa infantil

  • Niño.Id.
  • Niño.Id.padre
  • Nombre de niño

Aquí está mi bloque de código de prueba de concepto:

using System;
using System.Data.Common;
using DevArtTry1.DataSet1TableAdapters;

namespace DevArtTry1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (DataContext1 dc = new DataContext1())
            {
                dc.Connection.Open();
                using (DbTransaction transaction = dc.Connection.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    dc.Transaction = transaction;

                    Parent parent = new Parent();
                    parent.Id = 1;
                    parent.Name = "Parent 1";
                    dc.Parents.InsertOnSubmit(parent);
                    dc.SubmitChanges(); // By virtue of the Parent.Id -> Child.ParentId (M:N) foreign key, this statement will impose a write lock on the child table.

                    DataSet1.CHILDDataTable dt = new DataSet1.CHILDDataTable();
                    DataSet1.CHILDRow row = dt.NewCHILDRow();
                    row.ID = 1;
                    row.PARENTID = 1;
                    row.NAME = "Child 1";
                    dt.AddCHILDRow(row);

                    CHILDTableAdapter cta = new CHILDTableAdapter();
                     // cta.Transaction = transaction;  Not allowed because you can't convert source type 'System.Data.Common.DbTransaction to target type 'System.Data.OracleClient.OracleTransaction.
                    cta.Update(dt); // The thread will encounter a deadlock here, waiting for a write lock on the Child table.
                    transaction.Commit();
                }
            }

            Console.WriteLine("Successfully inserted parent and child rows.");
            Console.ReadLine();
        }
    }
}

  • Como indican los comentarios anteriores, el hilo se detendrá indefinidamente en la llamada de actualización del adaptador de datos secundario porque esperará indefinidamente un bloqueo de escritura en la tabla secundaria.[Tenga en cuenta la relación de clave externa:Padre.Id -> Niño.ParentId (M:N)]

Aquí está mi pregunta:

  • Quiero envolver todo el bloque de código en una transacción.
  • ¿Puedo hacer esto?Teniendo en cuenta que:
    • Quiero cometer una actualización en la tabla principal con Linq a SQL's Método Enviar cambios...
    • Y quiero cometer una actualización en la tabla infantil con un conjunto de datos ADO.NET adaptador de mesa.

Aquí hay dos notas a pie de página interesantes:

  1. Todo esto obras en reversa.Es decir, si quisiera enviar cambios a la tabla principal con un adaptador de datos y cambios en la tabla de niños con Linq a SQL ...eso trabajaría.
  2. Intenté adjuntar explícitamente la transacción al adaptador de datos, pero el compilador no lo permite porque es un tipo diferente de transacción.

                    CHILDTableAdapter cta = new CHILDTableAdapter();
                cta.Transaction = transaction; // Not allowed because you can't convert source type 'System.Data.Common.DbTransaction' to target type 'System.Data.OracleClient.OracleTransaction'.
                cta.Update(dt);
                transaction.Commit();
    
¿Fue útil?

Solución

No sé nada acerca de las transacciones de Oracle ... pero en el lado dotnet que debe estar bien para controlar la operación de uno mismo. Asegúrese de que ambas tecnologías están utilizando la misma instancia de conexión.

cuando controlamos transacciones a través de la conexión en lugar de a través del ORM, utilizamos ámbito de transacción: http://msdn.microsoft.com/en-us/library/ms172152.aspx

Otros consejos

Yo tenía el mismo problema, encontrándose estos dos errores:

  • integridad restricción violación (ORA-02291)
  • "No se puede insertar entidad con la misma clave si la clave no es la base de datos generada"

El problema era que la columna identidad del objeto secundario no se ha establecido correctamente. Si dotConnect LINQ no asume una clave de identidad, entonces los objetos propiedades parecen estar establecido ad hoc, lo que resulta en cambios no secuenciales, lo que lleva a violaciónes de integridad.

Aquí está la solución:

  • LINQ necesita saber que la clave primaria del niño es una clave de entidad y auto-generado.
  • En Oracle, la configuración de una clave de auto-incrementales para el objeto secundario.
  • En primer lugar crear una secuencia:

      DROP SEQUENCE MyChild_SEQ;
      CREATE SEQUENCE MyChild_SEQ
          MINVALUE 1
          MAXVALUE 999999999999999999999999999
          START WITH 1
          INCREMENT BY 1
          CACHE 20;
    
  • A continuación, cree el gatillo OnInsert:

    CREATE OR REPLACE TRIGGER MyChild_AUTOINC 
    BEFORE INSERT
    ON MyChildObject
    FOR EACH ROW
    BEGIN
      SELECT MyChild_SEQ.nextval
      INTO :NEW.MyChild_ID
      FROM dual;
    END MyChild_AUTOINC ; 
    ALTER TRIGGER MyChild_AUTOINC ENABLE
    
  • Modificar el modelo de almacenamiento para incorporar la clave primaria nueva generada automáticamente:

    • En el EntityDeveloper para dotConnect, abra su modelo de almacenamiento (archivo .LQML) LINQ.
    • Establecer la clave del objeto hijo entidad a 'Auto valor generado', y Auto-sincronización a 'OnInsert'.
    • Guardar el modelo de almacenamiento, y en Visual Studio, limpio y reconstruir la solución.
    • Eliminar cualquier código que establece explícitamente la clave primaria del niño.
      • LINQ implícitamente reconocerá esto como auto-incrementales, y recuperar el ID de gatillo-creado.
  • En el código, después de crear el objeto hijo, adjuntarlo a los padres, como a continuación:

    ChildType newChild = new ChildType();
    DataContext.InsertOnSubmit(newChild);
    Parent.Child = newChild;
    

Aquí hay más recursos:

Saludos!

Utilice una clase TransactionScope.

Ten en cuenta que si está utilizando diferentes bases de datos (o que residen en servidores distintos) es necesario comprobar la configuración de DTC.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top