Как получить сгенерированные сервером значения идентификаторов при использовании SqlBulkCopy
Я знаю, что могу сделать массовую вставку в мою таблицу со столбцом идентификатора, не указывая SqlBulkCopyOptions.KeepIdentity
, Как упоминалось здесь.
Что я хотел бы сделать, так это получить значения идентификаторов, которые генерирует сервер, и поместить их в мой datatable или даже список. Я видел этот пост, но я хочу, чтобы мой код был общим, и я не могу иметь столбец version во всех моих таблицах. Любые предложения очень ценятся. Вот мой код:
public void BulkInsert(DataTable dataTable, string DestinationTbl, int batchSize)
{
// Get the DataTable
DataTable dtInsertRows = dataTable;
using (SqlBulkCopy sbc = new SqlBulkCopy(sConnectStr))
{
sbc.DestinationTableName = DestinationTbl;
// Number of records to be processed in one go
sbc.BatchSize = batchSize;
// Add your column mappings here
foreach (DataColumn dCol in dtInsertRows.Columns)
{
sbc.ColumnMappings.Add(dCol.ColumnName, dCol.ColumnName);
}
// Finally write to server
sbc.WriteToServer(dtInsertRows);
}
}
2 ответа:
АФАИК, ты не можешь.
Единственный способ (который я знаю) получить значения(ы) поля идентичности-это использовать либо
SCOPE_IDENTITY()
при вставке строки за строкой, либо подходOUTPUT
при вставке всего набора."Самый простой" подход, вероятно, будет заключаться в том, что вы будете SqlBulkCopy записей в таблице, а затем получить их обратно позже. Проблема может заключаться в том, что может быть трудно правильно (и быстро) извлечь эти строки с сервера снова. (например, это будет довольно уродливо (и медленно) иметь предложение
WHERE
сIN (guid1, guid2, .., guid999998, guid999999)
=)Я предполагаю, что производительность здесь является проблемой, поскольку вы уже используете SqlBulkCopy, поэтому я бы предложил использовать подход
OUTPUT
, в котором вам сначала понадобится промежуточная таблица для SqlBulkCopy ваших записей. Указанная таблица должна включать в себя какой-то идентификатор пакета (GUID?) как позволить нескольким протекторам работать бок о бок. Вам понадобится хранимая процедура дляINSERT <table> OUTPUT inserted.* SELECT
передачи данных из промежуточной таблицы в фактическое место назначения. стол, а также снова очистить промежуточный стол. Набор записей returend из указанной процедуры затем будет соответствовать 1:1 набору данных origanal, ответственному за заполнение промежуточной таблицы, но, конечно, вы не должны полагаться на его порядок. Другими словами: ваша следующая задача, чем будет соответствовать возвращенные идентификационные поля обратно к исходным записям в вашем приложении.Поразмыслив, я бы сказал, что во всех случаях-за исключением подхода row-by-row & SCOPY_IDENTITY() , что будет очень медленно - вам нужно будет иметь (или добавить) "ключ" к вашим данным, чтобы связать сгенерированный идентификатор с исходными данными = /
Вы можете сделать аналогичный подход, описанный выше deroby, но вместо того, чтобы извлекать их обратно через
Поэтому я бы предложил добавить столбец в таблицу, чтобы сопоставить строку с транзакцией SqlBulkCopy, а затем сделать следующее, чтобы сопоставить сгенерированные идентификаторы обратно в коллекцию строк в памяти, которую вы только что вставили.WHERE IN (guid1, etc...
, Вы сопоставляете их обратно к строкам, вставленным в память, на основе их порядка.Это даст вам лучшую производительность, чем предоставление каждой отдельной строке уникального ключа. Так что после тебя массовая вставка таблицы данных вы можете сделать что-то вроде этого (в моем примере у меня будет список объектов, из которых я создам таблицу данных, а затем сопоставлю им сгенерированные идентификаторы)
Создайте новый Guid и задайте это значение для всех строк в массовом копировании отображение в новый столбец
Запустите Метод
WritToServer
объекта BulkCopyИзвлеките все строки, имеющие тот же ключ
Повторите этот список, который будет в порядке их добавления, они будут в том же порядке, что и коллекция строк в памяти, поэтому вы будете знать сгенерированный идентификатор для каждого элемента.
List<myObject> myCollection = new List<myObject> Guid identifierKey = Guid.NewGuid(); //Do your bulk insert where all the rows inserted have the identifierKey //set on the new column. In this example you would create a data table based //off the myCollection object. //Identifier is a column specifically for matching a group of rows to a sql //bulk copy command var myAddedRows = myDbContext.DatastoreRows.AsNoTracking() .Where(d => d.Identifier == identiferKey) .ToList(); for (int i = 0; i < myAddedRows.Count ; i++) { var savedRow = myAddedRows[i]; var inMemoryRow = myCollection[i]; int generatedId = savedRow.Id; //Now you know the generatedId for the in memory object you could set a // a property on it to store the value inMemoryRow.GeneratedId = generatedId; }