SqlBulkCopy de una list <>

¿Cómo puedo hacer una gran inserción con SqlBulkCopy desde una Lista <> de object simple?

¿Implemento mi IDataReader personalizado?

Simplemente cree una DataTable desde su list de objects y llame a SqlBulkCopy.WriteToServer , pasando la tabla de datos.

Puede encontrar lo siguiente útil:

  • Agregar columnas a una DataTable . Agregue una columna para cada propiedad / campo que desea escribir.
  • Agregar filas a una DataTable . Agregue una fila para cada object en su list.

Para get el máximo performance con SqlBulkCopy, debe establecer un BatchSize apropiado. 10.000 parece funcionar bien, pero el perfil de sus datos.

También puede observar mejores resultados al usar SqlBulkCopyOptions.TableLock .

Puede encontrar un análisis interesante e informativo del performance de SqlBulkCopy aquí .

Con FastMember , puede hacer esto sin tener que pasar por DataTable (que, en mis testings, más que duplica el performance):

 using(var bcp = new SqlBulkCopy(connection)) using(var reader = ObjectReader.Create(data, "Id", "Name", "Description")) { bcp.DestinationTableName = "SomeTable"; bcp.WriteToServer(reader); } 

Tenga en count que ObjectReader también puede funcionar con orígenes no generics, y no es necesario especificar los nombres de los miembros por adelantado (aunque es probable que desee utilizar el aspecto SqlBulkCopy de SqlBulkCopy si no los especifica en el ObjectReader ).

Tarde en la fiesta, pero si agrega esta class EntityDataReader de Microsoft, existe un método de extensión AsDataReader() que hace exactamente eso: https://github.com/matthewschrager/Repository/blob/master/Repository.EntityFramework/EntityDataReader.cs

(Ejemplo [List].AsDataReader() implementation 🙂

 var connStr = ""; using (var connection = new SqlConnection(connStr)) { var startTime = DateTime.Now; connection.Open(); var transaction = connection.BeginTransaction(); try { //var connStr = connection.ConnectionString; using (var sbCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, transaction)) { sbCopy.BulkCopyTimeout = 0; sbCopy.BatchSize = 10000; sbCopy.DestinationTableName = "Foobars"; var reader = Foobars.AsDataReader(); sbCopy.WriteToServer(reader); } transaction.Commit(); } catch (Exception ex) { Console.WriteLine(ex.Message); transaction.Rollback(); } finally { transaction.Dispose(); connection.Close(); var endTime = DateTime.Now; Console.WriteLine("Upload time elapsed: {0} seconds", (endTime - startTime).TotalSeconds); } } 

Dependiendo de lo que intente lograr llamando a SqlBulkCopy en primer lugar, podría tener más sentido utilizar un parámetro de valor de tabla (TVP). Usar un TVP haría que sea trivial enviar una colección de cualquier tipo personalizado. Los datos se pueden transmitir para que pueda evitar el DataTable (muy parecido a la respuesta de @Marc Gravell) y también puede evitar SqlBulkCopy . Los TVP permiten una flexibilidad total de cómo manejar los datos una vez que llegan a SQL Server cuando usted llama a un Procedimiento almacenado para pasar los datos de TVP y aparece como una Variable de tabla con la que puede hacer cualquier cosa, no solo INSERT (que es caso con SqlBulkCopy ). También puede recuperar datos a través de un SqlDataReader , datos tales como valores de IDENTITY recién creados. Agregué un ejemplo y algunas notas adicionales sobre esta respuesta: ¿Cómo puedo insert 10 millones de loggings en el menor time posible? . Y hace varios años escribí un artículo sobre SQL Server Central (se requiere logging gratuito), Transmisión de datos en SQL Server 2008 desde una aplicación , que también se menciona en esa respuesta vinculada, proporcionando un ejemplo práctico de pasar en una list genérica de una costumbre escriba, transmitido desde un file de text de 3 millones de filas.