Как получить сгенерированные сервером значения идентификаторов при использовании 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 6

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, но вместо того, чтобы извлекать их обратно через WHERE IN (guid1, etc..., Вы сопоставляете их обратно к строкам, вставленным в память, на основе их порядка.

Поэтому я бы предложил добавить столбец в таблицу, чтобы сопоставить строку с транзакцией SqlBulkCopy, а затем сделать следующее, чтобы сопоставить сгенерированные идентификаторы обратно в коллекцию строк в памяти, которую вы только что вставили.
  • Создайте новый 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;
 }