SqlBulkCopy из списка <>

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

Я реализую свой собственный IDataReader?

Ответ 1

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

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

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

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

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

Ответ 2

С 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 t указать их в самом ObjectReader).

Ответ 3

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

(пример реализации [List].AsDataReader() :)

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

Ответ 4

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