Как я могу сделать большую вставку с SqlBulkCopy из List < > простого объекта?
Я реализую свой собственный IDataReader?
Как я могу сделать большую вставку с SqlBulkCopy из List < > простого объекта?
Я реализую свой собственный IDataReader?
Просто создайте таблицу данных из списка объектов и вызовите SqlBulkCopy.WriteToServer
, передав таблицу данных.
Может оказаться полезным следующее:
Для максимальной производительности с SqlBulkCopy вы должны установить соответствующий BatchSize. 10 000, кажется, работают хорошо, но профиль для ваших данных.
Вы также можете наблюдать лучшие результаты при использовании SqlBulkCopyOptions.TableLock.
Интересный и информативный анализ производительности SqlBulkCopy можно найти здесь.
С 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
).
Поздно, но если вы добавите этот класс 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);
}
}
В зависимости от того, что вы пытаетесь выполнить, вызвав 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 миллиона строк.