2010-10-12 19 views
21

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

¿Implemento mi IDataReader personalizado?

+0

tarde a la fiesta , pero si agrega esta clase 'EntityDataReader', hay un método de extensión' AsDataReader() 'que hace exactamente eso: https: // github.com/matthewschrager/Repository/blob/master/Repository.EntityFramework/EntityDataReader.cs – RJB

+0

(consulte la nueva respuesta a continuación para ver la implementación completa) – RJB

Respuesta

19

Simplemente create a DataTable de su lista de objetos y llame al SqlBulkCopy.WriteToServer, pasando la tabla de datos.

Puede que le resulte útil la siguiente:

Para obtener el máximo rendimiento 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.

Se puede encontrar un análisis interesante e informativo del rendimiento de SqlBulkCopy here.

40

Con FastMember, usted puede hacer esto sin tener que tener que ir a través de DataTable (que, en mis pruebas, más de lo duplica el rendimiento):

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

Tenga en cuenta que ObjectReader también pueden trabajar con no fuentes genéricas, y no es necesario especificar los nombres de los miembros de antemano (aunque es probable que desee utilizar el aspecto ColumnMappings de si no los especifica en el propio ObjectReader).

+0

¡Excelente biblioteca! Lo probé en este momento y funciona genial. – alex

+0

Sé que esto fue hace unos meses, pero estoy teniendo un problema similar. Tarda demasiado tiempo en cargar 'DataTable' primero, así que quería usar este método. Sin embargo, ¿las cadenas enumeradas en la variable params son los nombres reales de las variables que se usan en orden desde el objeto sobre el que se está iterando la estructura de datos subyacente? – JNYRanger

+0

Scratch that-- lo resolvió y la respuesta fue sí, esos son los nombres de las propiedades dentro del objeto. – JNYRanger

1

En primer lugar, dependiendo de lo que esté intentando lograr llamando al SqlBulkCopy, podría tener más sentido utilizar un parámetro con valores 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 SqlDataReader, datos como los valores IDENTITY recién creados. Agregué un ejemplo y algunas notas adicionales sobre esta respuesta: How can I insert 10 million records in the shortest time possible?. Y hace varios años escribí un artículo sobre SQL Server Central (se requiere registro gratuito), Streaming Data Into SQL Server 2008 From an Application, que también se menciona en esa respuesta vinculada, proporcionando un ejemplo práctico de pasar en una lista genérica de un tipo personalizado, transmitido desde 3 millones archivo de texto de fila.

5

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

(ejemplo [List].AsDataReader() aplicación :)

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); 
    } 
} 
Cuestiones relacionadas