2010-03-02 36 views
5

Estoy tratando de averiguar si es posible realizar un comando de tipo "insertar en ... seleccionar" con LINQ to SQL. Un poco de código LINQ a SQL que me permitiría enviar un único comando SQL a la base de datos que insertaría varias filas en una tabla determinada.Realización de un INSERT INTO ... SELECT con LINQ to SQL

Por ejemplo, ¿cómo hacer LINQ to SQL para enviar la siguiente instrucción T-SQL a una base de datos SQL Server?

INSERT INTO Table1 
SELECT Table2.column1 + 1 AS column1, Table2.column2 + 2 AS column2 
WHERE Table2.column3 > 100 

que podría, por supuesto, lograr esto mediante el uso de la función DataContext.ExecuteCommand pero esto sería ejecutado inmediatamente sin tomar ventaja de la operación automática de manipulación que se obtiene con DataContext.SubmitChanges. Tengo una serie de actualizaciones además de ésta y me gustaría que todas sean resueltas en caso de error.

¿Alguna idea?

ACTUALIZACIÓN: Aquí está el código real:

 var bs_prep = 
      from b in dc.T_EDR_FILEBODies 
      join 
      unpaid in dc.V_UNPAIDs 
      on 
       b.NUM_ADC.Substring(1, 9) equals unpaid.NOCONT 
      join 
      acordo in dc.T_ACORDOS_RECOM_APREs 
      on 
       Convert.ToInt32(b.NUM_ADC.Substring(1, 9)) equals acordo.ID_Contrato 
      where 
       b.ID_EDR == id_edr 
       && 
       (
        unpaid.NUM_INCUMPRIMENTOS <= max_unpaid_consec 
        && 
        unpaid.TOTAL_NUM_INCUPRIMENTOS <= max_unpaid_nonconsec 
       ) 
       || 
       (
        acordo.Activo == true 
        && 
        acordo.Data_Recomeco <= now 
       ) 
      select new 
       { 
        ID_EDR = id_edr_filt, 
        NUM_LINHA = b.NUM_LINHA, 
        CODREJ = b.CODREJ, 
        HDT = b.HDT, 
        IMPORT = b.IMPORT, 
        NIB_DEV = b.NIB_DEV, 
        NUM_ADC = b.NUM_ADC, 
        REF_DD_BC = b.REF_DD_BC, 
        REF_MOV = b.REF_MOV 
       } 
      ; 


     dc.T_EDR_FILEBODies.InsertAllOnSubmit(
      bs_prep.Select(
       b => new T_EDR_FILEBODY{ 
        CODREJ = b.CODREJ, 
        HDT = b.HDT, 
        ID_EDR = b.ID_EDR, 
        IMPORT = b.IMPORT, 
        NIB_DEV = b.NIB_DEV, 
        NUM_ADC = b.NUM_ADC, 
        NUM_LINHA = b.NUM_LINHA, 
        REF_DD_BC = b.REF_DD_BC, 
        REF_MOV = b.REF_MOV 
       } 
      ) 
     ); 

explicación rápida: La entidad T_EDR_FILEBODies mapea a una tabla de base de datos que básicamente almacena el contenido de algunos archivos de texto que importamos. Un registro corresponde a una línea en el archivo de texto.

Lo que intento hacer es crear una versión filtrada del contenido del archivo copiando los registros de un archivo, dándoles un nuevo ID de archivo (ID_EDR=id_edr_filt) pero filtrando algunas de las líneas. Las entidades LINQ to SQL son asignaciones directas a las tablas de la base de datos. No he agregado ningún código a mi datacontext hasta el momento. Tienen claves primarias, o de lo contrario no podría hacer inserciones en ellas (leí en algún lugar que podría deshacerme de esa excepción si me deshace de las claves principales, pero, como puedes ver, eso no trabajo en mi caso).

Cuando lo ejecuto me sale el siguiente excepción lanzada por InsertAllOnSubmit:

construcción explícita del tipo de entidad 'T_EDR_FILEBODY' en la consulta no está permitido.

Supongo que entiendo que construir explícitamente una entidad dentro de una consulta sería problemático. Las entidades devueltas por consultas tienen un seguimiento de cambios, los cambios se traducen a la base de datos cuando se llama a submitchanges. Pero, ¿cómo podría traducir, en la base de datos, los cambios en una entidad creada en el lado del cliente? Pero, ¿esto realmente significa que no se puede realizar un comando INSERT INTO ... SELECT type utilizando LINQ to SQL?

+1

¿Puede usted hacer un "Uso _tx como Nueva TransactionScope()" y envolver el ExecuteCommand y todo lo que en ella? – StingyJack

+0

Bueno, supongo que puedo, y podría ser exigente, pero me resulta difícil aceptar que no puedas hacer una inserción en ... seleccionar instrucción usando linq. Sería un gran handycap. –

Respuesta

0

Puede utilizar

ctx.Table1.InsertAllOnSubmit(
    mySelectEnumeration.Select(x => new Table1DT { ... }) 
); 
  • InsertAllOnSubmit inserta un número de entradas en una tabla de SQL LINQ to.
  • mySelectEnumeration es la consulta que selecciona los elementos que se insertarán.
  • Select(new Table1DT { ... }) es la transformación necesaria para convertir el tipo de datos de la consulta seleccionada en el tipo de datos de la tabla.

Alternativamente, puede utilizar el método ExecuteCommand y administrar la transacción manualmente.

using (var ctx = new DataClasses1DataContext()) { 
    ctx.Connection.Open(); 
    using (ctx.Transaction = ctx.Connection.BeginTransaction()) { 
     ctx.ExecuteCommand("sqlcommand"); 
     ctx.Transaction.Commit(); 
    } 
} 

O usando el alcance de la transacción:

using (var ctx = new DataClasses1DataContext()) { 
    using (var scope = new TransactionScope()) { 
     ctx.ExecuteCommand("sqlcommand"); 
     scope.Complete(); 
    } 
} 
+0

No estoy seguro de haber entendido su sugerencia. No veo cómo inicializar la instancia de Table1DT en el contructor en línea basado en mySelectEnumeration. ¿Cómo hago referencia a mySelectEnumeration? Alternativamente, traté de encapsular la primera consulta (que devuelve IQueryable ) en otra consulta con un constructor en línea Table1DT en la cláusula Select y así devuelve un IQueryable . Pero obtengo el mismo error que obtengo al poner el constructor en línea en la primera consulta: No está permitida la construcción explícita del tipo de entidad 'Table1DT' en la consulta. –

+1

Como no conozco su clase concreta DataContext (ctx en mi ejemplo), utilicé marcadores de posición. 'Table1' es el marcador de posición para la tabla en los elementos que se deben insertar. 'mySelectEnumeration' es el resultado de la consulta que realiza para seleccionar los elementos que desea insertar. 'Table1DT' es el tipo de datos de su' Table1'. Y, el '...' indica dónde tendrá que establecer las propiedades de "Tabla1", p. 'ID = x.identificador'. No puedo ser más específico sin conocer tu DataContext. – AxelEckenberger

+0

Bueno, mi error, creo que no leí tu respuesta tan bien como debería (me perdí la "x =>"). Ok lo intenté, y todavía obtuve la misma excepción en la llamada InsertAllOnSubmit: no está permitida la construcción explícita del tipo de entidad 'Table1DT' en la consulta. (Estoy usando la misma notación que usaste). He leído sobre esto y tengo la idea de que por alguna razón no puedes construir entidades dentro de las consultas. Aunque no entendí bien por qué. –