SqlDataAdapter не может получить строку после вставки - PullRequest
0 голосов
/ 15 октября 2018

Я пытаюсь использовать SqlDataAdapter для вставки строки, а затем сразу получить эту же строку.Я следовал совету на этом посте и использую SCOPE_IDENTITY, но он не работает.Вот мой код ...

using (var conn = _db.OpenConnection())
{
    var sqlQuery = "SELECT * FROM RotationItem WHERE [RotationItemId] = SCOPE_IDENTITY()";
    var adapter = new SqlDataAdapter(sqlQuery, conn);
    var builder = new SqlCommandBuilder(adapter);
    var dataSet = new DataSet();
    adapter.Fill(dataSet);
    Debug.WriteLine("First fill, rows " + dataSet.Tables[0].Rows.Count);
    var table = dataSet.Tables[0];
    table.Rows.InsertAt(table.NewRow(), 0);
    CopyJsonToRow(table, 0, item);
    if (adapter.Update(dataSet) != 1)
    {
        throw new InvalidOperationException("Insert failed");
    }
    // After insert, fetch the new record
    dataSet.Clear();
    adapter.Fill(dataSet);
    Debug.WriteLine("Second fill, rows " + dataSet.Tables[0].Rows.Count);
}

Мой вывод:

  • Первая заливка, строки 0
  • Вторая заливка, строки 0 <== это НЕ то, что я ожидаю </strong>

Почему вторая заливка не удалась?Разве он не должен получить строку, которую я только что вставил?!

Я не использую транзакции.Определение таблицы ниже ...

CREATE TABLE [dbo].[RotationItem] (
    [RotationItemId] INT NOT NULL IDENTITY(1,1),
    [RotationScheduleId] INT NOT NULL,   
    [QuestionnaireId] INT NOT NULL,  
    [Order] INT NOT NULL,
    PRIMARY KEY CLUSTERED ([RotationItemId] ASC)
);

1 Ответ

0 голосов
/ 16 октября 2018

Вот что я нашел после нескольких часов бездельничания.

  • Вы не должны использовать IDENT_CURRENT, потому что это очень ненадежно
  • Вы не можете использовать SCOPE_IDENTITY после SqlDataAdapter.Update (как в OP), потому что он закрывает область.
  • , когда вы вызываете new SqlCommandBuilder(adapter), он делает секретное вуду для вашего адаптера, что делает невозможным настройку.В частности, любые изменения, которые вы вносите в InsertCommand, игнорируются.

Вот моя умная работа (некоторые могут сказать ужасный взлом).Это работает отлично, даже если я забью это со многими одновременными клиентами.

using (var conn = _db.OpenConnection())
{
    var sqlQuery = "SELECT * FROM RotationItem WHERE [RotationItemId] = SCOPE_IDENTITY()";

    // Create a dummy adapter, so we can generate an appropriate INSERT statement
    var dummy = new SqlDataAdapter(sqlQuery, conn);
    var insert = new SqlCommandBuilder(dummy).GetInsertCommand();

    // Append the SELECT to the end of the INSERT command, 
    // and set a flag to copy the result back into the dataSet table row
    insert.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
    insert.CommandText += ";" + sqlQuery;

    // Now proceed as usual...
    var adapter = new SqlDataAdapter(sqlQuery, conn);
    adapter.InsertCommand = insert;
    var dataSet = new DataSet();
    adapter.Fill(dataSet);
    Debug.WriteLine("First fill, rows " + dataSet.Tables[0].Rows.Count);
    var table = dataSet.Tables[0];
    table.Rows.InsertAt(table.NewRow(), 0);
    CopyJsonToRow(table, 0, item);
    if (adapter.Update(dataSet) != 1)
    {
        throw new InvalidOperationException("Insert failed");
    }
    // After insert, the table AUTOMATICALLY has the new row ID (and any other computed columns)
    Debug.WriteLine("The new id is " + table.Rows[0].ItemArray[0]);
}

отрицательные баллы этого взлома - мне нужно сделать два SqlDataAdapters, один просто фиктивныйкормить до SqlCommandBuilder.Также использование конкатенации строк для склеивания двух SQL-запросов очень хитро.Я не уверен, что внутренний аудит безопасности позволит мне сделать это из-за проблемы с инъекцией.У кого-нибудь есть идея получше?

...