2012-04-13 8 views
9

Estoy tratando de encontrar la diferencia entre usar SqlBulkCopy con la opción de copia SqlBulkCopyOptions.UseInternalTransaction y sin ella, pero en mi aplicación de prueba no detecté ninguna diferencia. Si BatchSize es por ejemplo 0 y agrego 100 registros (en un DataTable) donde el registro número 50 causa un error al agregarlo a la tabla de la base de datos, obtengo 0 registros en la tabla. Si BatchSize se establece en 10, por ejemplo, obtengo 40 registros (4 lotes de 10 registros, el quinto incluye el registro defectuoso y hace que la copia masiva se cancele). No importa si S qlBulkCopyOptions.UseInternalTransaction está configurado o no, siempre obtengo el mismo resultado. Parece que los lotes siempre se copian en una transacción interna.SqlBulkCopy: ¿Cuál es la diferencia entre pasar SqlBulkCopyOptions.UseInternalTransaction y no pasarlo?

Si usted está interesado en mi aplicación de prueba, aquí está: SqlBulkCopy-Error-and-Transaction-Test.zip

Mis preguntas son:

  1. Es SqlBulkCopyOptions.UseInternalTransaction obsoleta porque SqlBulkCopy siempre utiliza las transacciones internas?
  2. En caso negativo: ¿Cuál es el significado real de esta opción? ¿En qué casos haría la diferencia?

espero que alguien pueda aclarar

Editar: De acuerdo con la respuesta y los comentarios que supongo que mi problema no ist suficientemente claro. Sé la documentación. Dice que "de forma predeterminada, una operación de copia masiva es su propia transacción". y que cada lote usa su propia transacción al pasar UseInternalTransaction. Pero si eso significa que, de forma predeterminada, la operación de copia masiva usa solo una transacción para toda la copia masiva (y no una para cada lote) no obtendría registros en la base de datos si configuro BatchSize a un determinado tamaño y un lote que se encuentra después el primero causa un error. Si solo se utilizara una transacción, se revertirían todos los registros agregados al registro de transacciones. Pero obtengo los registros de los lotes que se encuentran antes del lote que incluye el registro defectuoso. Según esto, parece que, por defecto, cada lote se ejecuta en su propia transacción. Eso significa que no importa si paso UseInternalTransaction o no. Si estoy en el camino equivocado aquí, realmente agradecería que alguien pudiera aclarar.

Un hecho podría ser importante: uso SQL Server 2012. Tal vez el SQL Server 2008 se comporte de manera diferente. Lo comprobaré.

Editar: Gracias a la respuesta de usr Creo que he encontrado la respuesta: Me depurado y perfilado un poco y descubrí que la _internalTransaction ámbito privado está realmente no establece si UseInternalTransaction no está definido. SqlBulkCopy no usa una transacción propia (interna). Pero los perfiles indicaron que SqlBulkCopy usa TDS (flujo de datos tabulares) para copiar los datos (sin importar qué BatchSize sea). No encontré mucha información sobre TDS especialmente para SQL Server, pero supongo que SQL Server ejecuta las operaciones de copia masiva de TDS en una transacción interna. Por lo tanto, UseInternalTransaction parece ser algo redundante para SQL Server, pero para estar seguro lo configuré.

+0

http://msdn.microsoft.com/en-us/library/tchktcdk%28v=vs.80%29.aspx –

+0

Gracias Tim. Sí, la documentación dice "Por defecto, una operación de copia masiva es su propia transacción". Un poco más tarde dice "Puede especificar explícitamente la opción UseInternalTransaction en el constructor de la clase SqlBulkCopy para provocar explícitamente que se ejecute una operación de copia masiva en su propia transacción, haciendo que cada lote de la operación de copia masiva se ejecute dentro de una transacción separada". Esto es confuso. ¡Y mi aplicación de prueba no muestra una sola diferencia! Ver mi comentario a la respuesta de Arion. Solo si paso una transacción propia, obtengo 0 registros agregados en caso de error. Prueba mi aplicación de prueba ... –

+0

Tim Schmelter: El enlace es irrelevante. No aclara la cuestión de cómo escribir ** un lote ** en el servidor sin una transacción. Si entiendo la pregunta de OP, no se trata de cómo hacer que toda la operación de copia masiva sea una transacción. Confirmo el hallazgo de Jurgen. UseInternalTransaction no es efectivo en este caso. – JohnC

Respuesta

2

Si se establece esta opción entonces la clase SqlBulkCopy añadirá un

_internalTransaction = _connection.BeginTransaction(); 

alrededor de cada lote.

Pero esta opción no tiene ninguna diferencia práctica con SQL Server ya que las transacciones se ejecutan de manera predeterminada en el modo de confirmación automática.

La única diferencia observable es que realiza la validación que no ha intentado pasar en una transacción externa.

La siguiente tendrá éxito y deshacer todos los lotes

var transaction = sourceConnection.BeginTransaction();    
using (SqlBulkCopy bulkCopy = 
    new SqlBulkCopy(sourceConnection, SqlBulkCopyOptions.Default, transaction)) 

{ 
    bulkCopy.BatchSize = 50; 

    bulkCopy.DestinationTableName = "dbo.foobar"; 
     bulkCopy.WriteToServer(dt); 
} 

transaction.Rollback(); 

Pasando SqlBulkCopyOptions.UseInternalTransaction falla con un error

no debe especificar SqlBulkCopyOption.UseInternalTransaction y aprobar un Transacción externo al mismo tiempo.

me preguntaba si podría hacer una diferencia si un SET IMPLICIT_TRANSACTIONS ON; habían sido previamente ejecutar en la conexión para desactivar el modo automático pero la sobrecarga del constructor SqlBulkCopy que toma un objeto de conexión devuelve un cometen "transacción existente inesperado." error en ambos casos de todos modos, y la sobrecarga que toma una cadena de conexión solo crea una nueva conexión.

Cuestiones relacionadas