web-dev-qa-db-fra.com

SqlBulkCopy à partir d'une liste <>

Comment puis-je faire une grande insertion avec SqlBulkCopy à partir d'une liste <> d'objets simples?

Dois-je implémenter mon IDataReader personnalisé?

28
Patrice Pezillier

Simplement créez un DataTable à partir de votre liste d'objets et appelez SqlBulkCopy.WriteToServer, en passant le tableau de données.

Vous pourriez trouver les informations suivantes utiles:

Pour des performances maximales avec SqlBulkCopy, vous devez définir un BatchSize approprié. 10000 semble bien fonctionner - mais le profil de vos données.

Vous pouvez également observer de meilleurs résultats lors de l'utilisation de SqlBulkCopyOptions.TableLock .

Une analyse intéressante et informative des performances de SqlBulkCopy peut être trouvée ici .

20
Winston Smith

Avec FastMember , vous pouvez le faire sans avoir à passer par DataTable (ce qui, dans mes tests, fait plus que doubler les performances):

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

Notez que ObjectReader peut également fonctionner avec des sources non génériques, et il n'est pas nécessaire de spécifier les noms de membres à l'avance (bien que vous souhaitiez probablement utiliser l'aspect ColumnMappings de SqlBulkCopy si vous ne les spécifiez pas dans le ObjectReader lui-même).

52
Marc Gravell

Tard dans la soirée, mais si vous ajoutez cette classe EntityDataReader de Microsoft, il existe une méthode d'extension AsDataReader() qui fait exactement cela: https://github.com/matthewschrager/ Référentiel/blob/master/Repository.EntityFramework/EntityDataReader.cs

(exemple [List].AsDataReader() implémentation :)

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);
    }
}
9
RJB

Selon ce que vous essayez d'accomplir en appelant SqlBulkCopy en premier lieu, il peut être plus judicieux d'utiliser un paramètre table (TVP). L'utilisation d'un TVP rendrait trivial l'envoi d'une collection de n'importe quel type personnalisé. Les données peuvent être diffusées afin que vous puissiez éviter le DataTable (un peu comme dans la réponse de @Marc Gravell) et vous pouvez également éviter SqlBulkCopy. Les TVP permettent une flexibilité totale de la façon de gérer les données une fois qu'elles arrivent sur SQL Server lorsque vous appelez une procédure stockée pour transmettre les données TVP et elles apparaissent sous la forme d'une variable de table avec laquelle vous pouvez faire n'importe quoi, pas seulement INSERT (ce qui est le cas avec SqlBulkCopy). Vous pouvez également récupérer des données via un SqlDataReader, des données telles que les valeurs IDENTITY nouvellement créées. J'ai ajouté un exemple et quelques notes supplémentaires sur cette réponse: Comment puis-je insérer 10 millions d'enregistrements dans les plus brefs délais? . Et il y a plusieurs années, j'ai écrit un article sur SQL Server Central (inscription gratuite requise), Streaming Data Into SQL Server 2008 From an Application , qui est également noté dans cette réponse liée, fournissant un exemple pratique de passage dans une liste générique d'un type personnalisé, diffusée à partir d'un fichier texte de 3 millions de lignes.

1
Solomon Rutzky