Возможно ли получить значение столбца IDENTITY при вставке, используя SqlCommandBuilder (без использования Stored Proc)? - PullRequest
14 голосов
/ 26 сентября 2008

К вашему сведению: я работаю на dotnet 3.5 SP1

Я пытаюсь получить значение столбца идентификаторов в моем наборе данных после выполнения обновления (используя SqlDataAdapter и SqlCommandBuilder). После выполнения SqlDataAdapter.Update (myDataset) я хочу иметь возможность прочитать автоматически назначенное значение myDataset.tables(0).Rows(0)("ID"), но это System.DBNull (несмотря на тот факт, что строка была вставлена).

(Примечание. Я не хочу явно писать новую хранимую процедуру для этого!)

Один часто публикуемый метод http://forums.asp.net/t/951025.aspx изменяет SqlDataAdapter.InsertCommand и updatedRowSource следующим образом:

SqlDataAdapter.InsertCommand.CommandText += "; SELECT MyTableID = SCOPE_IDENTITY()"
InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord

По-видимому, в прошлом это, казалось, работало для многих людей, но не работает для меня.

Другой метод: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=619031&SiteID=1 у меня тоже не работает, так как после выполнения SqlDataAdapter.Update коллекция SqlDataAdapter.InsertCommand.Parameters сбрасывается на исходную (теряется дополнительный добавленный параметр).

Кто-нибудь знает ответ на этот вопрос?

Ответы [ 13 ]

10 голосов
/ 30 сентября 2011

Это проблема, с которой я сталкивался раньше, ошибка, по-видимому, заключается в том, что при вызове da.Update (DS); массив параметров команды вставки сбрасывается в начальный список, созданный из вашего компоновщика команд, он удаляет добавленные выходные параметры для идентификатора.

Решение состоит в том, чтобы создать новый адаптер данных и скопировать в команды, а затем использовать этот новый для создания da.update (ds);

как

SqlDataAdapter da = new SqlDataAdapter("select Top 0 " + GetTableSelectList(dt) + 
"FROM " + tableName,_sqlConnectString);
SqlCommandBuilder custCB = new SqlCommandBuilder(da);
custCB.QuotePrefix = "[";
custCB.QuoteSuffix = "]";
da.TableMappings.Add("Table", dt.TableName);

da.UpdateCommand = custCB.GetUpdateCommand();
da.InsertCommand = custCB.GetInsertCommand();
da.DeleteCommand = custCB.GetDeleteCommand();

da.InsertCommand.CommandText = String.Concat(da.InsertCommand.CommandText, 
"; SELECT ",GetTableSelectList(dt)," From ", tableName, 
" where ",pKeyName,"=SCOPE_IDENTITY()");

SqlParameter identParam = new SqlParameter("@Identity", SqlDbType.BigInt, 0, pKeyName);
identParam.Direction = ParameterDirection.Output;
da.InsertCommand.Parameters.Add(identParam);

da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;

//new adaptor for performing the update                     
SqlDataAdapter daAutoNum = new SqlDataAdapter();
daAutoNum.DeleteCommand = da.DeleteCommand;
daAutoNum.InsertCommand = da.InsertCommand;
daAutoNum.UpdateCommand = da.UpdateCommand;

daAutoNum.Update(dt);
4 голосов
/ 25 марта 2010

Что мне подходит, так это настройка действия MissingSchemaAction:

SqlCommandBuilder commandBuilder = new SqlCommandBuilder(myDataAdapter);
myDataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;

Это позволяет мне получить первичный ключ (если это идентификационный номер или номер) после вставки.

Удачи.

4 голосов
/ 26 сентября 2008

Команда вставки может быть проинструктирована обновлять вставленную запись, используя либо выходные параметры, либо первую возвращенную запись (или обе), используя свойство updatedRowSource ...

InsertCommand.UpdatedRowSource = UpdateRowSource.Both;

Если вы хотите использовать хранимую процедуру, все будет готово. Но вы хотите использовать необработанную команду (то есть, вывод команды Builder), которая не учитывает а) выходные параметры или б) возврат записи. Почему это? Хорошо, а) вот как будет выглядеть ваша команда InsertCommand ...

INSERT INTO [SomeTable] ([Name]) VALUES (@Name)

Нет способа ввести выходной параметр в команду. Так что насчет б)? К сожалению, DataAdapter выполняет команду Вставка, вызывая метод метода ExecuteNonQuery. Это не возвращает никаких записей, поэтому адаптер не может обновить вставленную запись.

Так что вам нужно либо использовать сохраненный процесс, либо отказаться от использования DataAdapter.

2 голосов
/ 31 августа 2011

Я нашел ответ, который работает для меня здесь: http://www.dotnetmonster.com/Uwe/Forum.aspx/dotnet-ado-net/4933/Have-soln-but-need-understanding-return-IDENTITY-issue

Код, который работал (с сайта - приписывается Гиришу)

//_dataCommand is an instance of SqlDataAdapter
//connection is an instance of ConnectionProvider which has a property called DBConnection of type SqlConnection
//_dataTable is an instance of DataTable

SqlCommandBuilder bldr = new SqlCommandBuilder(_dataCommand);
SqlCommand cmdInsert = new SqlCommand(bldr.GetInsertCommand().CommandText, connection.DBConnection);
cmdInsert.CommandText += ";Select SCOPE_IDENTITY() as id";

SqlParameter[] aParams = new
SqlParameter[bldr.GetInsertCommand().Parameters.Count];
bldr.GetInsertCommand().Parameters.CopyTo(aParams, 0);
bldr.GetInsertCommand().Parameters.Clear();

for(int i=0 ; i < aParams.Length; i++)
{
 cmdInsert.Parameters.Add(aParams[i]);
}

_dataCommand.InsertCommand = cmdInsert;
_dataCommand.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
_dataCommand.Update(_dataTable);
2 голосов
/ 07 декабря 2010

Если вы просто хотите (а) вставить одну запись в таблицу, (б) использовать набор данных и (в) не использовать хранимую процедуру, то вы можете выполнить следующее:

  1. Создайте свой dataAdapter, но в операторе select добавьте WHERE 1 = 0, чтобы вам не пришлось загружать всю таблицу - необязательный шаг для повышения производительности

  2. Создание пользовательского оператора INSERT с оператором выбора идентификатора области и выходным параметром.

  3. Выполните обычную обработку, заполнив набор данных, добавив запись и сохранив обновление таблицы.

  4. Теперь должна иметь возможность извлекать тождество непосредственно из параметра.

Пример: * * один тысяча двадцать-одна

    '-- post a new entry and return the column number
    ' get the table stucture
    Dim ds As DataSet = New DataSet()
    Dim da As SqlDataAdapter = New SqlDataAdapter(String.Concat("SELECT * FROM [", fileRegisterSchemaName, "].[", fileRegisterTableName, "] WHERE 1=0"), sqlConnectionString)
    Dim cb As SqlCommandBuilder = New SqlCommandBuilder(da)

    ' since we want the identity column back (FileID), we need to write our own INSERT statement
    da.InsertCommand = New SqlCommand(String.Concat("INSERT INTO [", fileRegisterSchemaName, "].[", fileRegisterTableName, "] (FileName, [User], [Date], [Table]) VALUES (@FileName, @User, @Date, @Table); SELECT @FileID = SCOPE_IDENTITY();"))
    da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord
    With da.InsertCommand.Parameters
        .Add("@FileName", SqlDbType.VarChar, 1024, "FileName")
        .Add("@User", SqlDbType.VarChar, 24, "User")
        .Add("@Date", SqlDbType.DateTime, 0, "Date")
        .Add("@Table", SqlDbType.VarChar, 128, "FileName")
        ' allow the @FileID to be returned back to us
        .Add("@FileID", SqlDbType.Int, 0, "FileID")
        .Item("@FileID").Direction = ParameterDirection.Output
    End With

    ' copy the table structure from the server and create a reference to the table(dt)
    da.Fill(ds, fileRegisterTableName)
    Dim dt As DataTable = ds.Tables(fileRegisterTableName)

    ' add a new record
    Dim dr As DataRow = dt.NewRow()
    dr("FileName") = fileName
    dr("User") = String.Concat(Environment.UserDomainName, "\", Environment.UserName)
    dr("Date") = DateTime.Now()
    dr("Table") = targetTableName
    dt.Rows.Add(dr)

    ' save the new record
    da.Update(dt)

    ' return the FileID (Identity)
    Return da.InsertCommand.Parameters("@FileID").Value

Но довольно долго делать то же самое, что и это ...

' add the file record
    Dim sqlCmd As SqlCommand = New SqlCommand(String.Concat("INSERT INTO [", fileRegisterSchemaName, "].[", fileRegisterTableName, "] (FileName, [User], [Date], [Table]) VALUES (@FileName, @User, @Date, @Table); SELECT SCOPE_IDENTITY();"), New SqlConnection(sqlConnectionString))
    With sqlCmd.Parameters
        .AddWithValue("@FileName", fileName)
        .AddWithValue("@User", String.Concat(Environment.UserDomainName, "\", Environment.UserName))
        .AddWithValue("@Date", DateTime.Now())
        .AddWithValue("@Table", targetTableName)
    End With
    sqlCmd.Connection.Open()
    Return sqlCmd.ExecuteScalar
2 голосов
/ 04 марта 2010

У меня была такая же проблема. Это было просто решено, когда я клонировал команду, сгенерированную командой buildbuilder. Похоже, что даже когда вы изменяете commandText команды insert, он продолжает получать команду, сгенерированную Commandbuilder ... Вот код, который я использовал для клонирования команды ...

        private static void CloneBuilderCommand(System.Data.Common.DbCommand toClone,System.Data.Common.DbCommand repository)
    {
        repository.CommandText = toClone.CommandText;
        //Copying parameters
        if (toClone.Parameters.Count == 0) return;//No parameters to clone? go away!
        System.Data.Common.DbParameter[] parametersArray= new System.Data.Common.DbParameter[toClone.Parameters.Count];
        toClone.Parameters.CopyTo(parametersArray, 0);
        toClone.Parameters.Clear();//Removing association before link to the repository one
        repository.Parameters.AddRange(parametersArray);            
    }
2 голосов
/ 08 января 2010

На самом деле, это работает для меня:

SqlDataAdapter.InsertCommand.CommandText += "; SELECT MyTableID = SCOPE_IDENTITY()" InsertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;

Приветствие.

1 голос
/ 24 августа 2012

Для меня проблема заключалась в том, где был размещен код.

Добавьте код в обработчик событий RowUpdating, примерно так:

void dataSet_RowUpdating(object sender, SqlRowUpdatingEventArgs e)
{
    if (e.StatementType == StatementType.Insert)
    {
        e.Command.CommandText += "; SELECT ID = SCOPE_IDENTITY()";
        e.Command.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
     }
}
1 голос
/ 26 сентября 2008

Если эти другие методы не работают для вас, предоставляемые инструменты .Net (SqlDataAdapter) и т. Д. На самом деле не предлагают ничего другого в отношении гибкости. Как правило, вам нужно перейти на следующий уровень и начать делать вещи вручную. Хранимая процедура будет одним из способов продолжать использовать SqlDataAdapter. В противном случае вам нужно перейти к другому инструменту доступа к данным, поскольку библиотеки данных .Net имеют ограничения, так как они предназначены для простого проектирования. Если ваша модель не работает с их зрением, вы должны свернуть свой собственный код в какой-то момент / уровень.

0 голосов
/ 15 ноября 2016

Мое решение:

SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Mytable;" , sqlConnection);

SqlCommandBuilder cb = new SqlCommandBuilder(da);

da.DeleteCommand = (SqlCommand)cb.GetDeleteCommand().Clone();
da.UpdateCommand = (SqlCommand)cb.GetUpdateCommand().Clone();

SqlCommand insertCmd = (SqlCommand)cb.GetInsertCommand().Clone();

insertCmd.CommandText += ";SET @Id = SCOPE_IDENTITY();";
insertCmd.Parameters.Add("@Id", SqlDbType.Int, 0, "Id").Direction = ParameterDirection.Output;
da.InsertCommand = insertCmd;
da.InsertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;

cb.Dispose();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...