SqlBulkCopy из списка <> - PullRequest
       43

SqlBulkCopy из списка <>

25 голосов
/ 12 октября 2010

Как сделать большую вставку с помощью SqlBulkCopy из списка <> простого объекта?

Реализовать ли я свой собственный IDataReader?

Ответы [ 4 ]

50 голосов
/ 07 июня 2013

С FastMember , вы можете сделать это без необходимости проходить через DataTable (что, в моих тестах, более чем удваивает производительность):

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

Обратите внимание, что ObjectReader также может работать с неуниверсальными источниками, и нет необходимости указывать имена членов заранее (хотя вы, вероятно, захотите использовать ColumnMappings аспект SqlBulkCopy, если вы не не указывайте их в самом ObjectReader.

20 голосов
/ 12 октября 2010

Просто создайте DataTable из вашего списка объектов и вызовите SqlBulkCopy.WriteToServer, передавая таблицу данных.

Может оказаться полезным следующее:

Для максимальной производительности с SqlBulkCopy вы должны установить соответствующий BatchSize .Кажется, 10 000 работает хорошо - но профиль для ваших данных.

Вы также можете наблюдать лучшие результаты при использовании SqlBulkCopyOptions.TableLock .

Интересный и информативный анализ производительности SqlBulkCopy. здесь .

9 голосов
/ 24 апреля 2016

Поздно, но если вы добавите этот класс EntityDataReader от Microsoft, есть метод расширения AsDataReader(), который делает именно это: https://github.com/matthewschrager/Repository/blob/master/Repository.EntityFramework/EntityDataReader.cs

(пример * реализация 1006 *:)

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);
    }
}
1 голос
/ 11 сентября 2014

В зависимости от того, что вы пытаетесь выполнить, сначала позвонив по номеру SqlBulkCopy, может иметь смысл использовать параметр с табличным значением (TVP).Использование TVP сделает тривиальной отправку коллекции любого пользовательского типа.Данные могут быть переданы в поток, так что вы можете избежать DataTable (очень похоже на ответ @Marc Gravell), а также вы можете избежать SqlBulkCopy.TVP позволяют полностью гибко обрабатывать данные, когда они попадают на SQL Server, когда вы вызываете хранимую процедуру для передачи данных TVP, и она выглядит как табличная переменная, с которой вы можете делать что угодно, не только INSERTэто случай с SqlBulkCopy).Вы также можете получить данные обратно через SqlDataReader, такие данные, как вновь созданные значения IDENTITY.Я добавил пример и некоторые дополнительные примечания к этому ответу: Как я могу вставить 10 миллионов записей в кратчайшие сроки? .И несколько лет назад я написал статью о SQL Server Central (требуется бесплатная регистрация) Потоковая передача данных в SQL Server 2008 из приложения , которая также отмечена в этом связанном ответе, предоставляя рабочий пример передачиОбщий список пользовательского типа, передаваемый из текстового файла с 3 миллионами строк.

...