14

参考までに: dotnet 3.5 SP1 で実行しています

(SqlDataAdapter と SqlCommandBuilder を使用して) 更新を実行した後、ID 列の値をデータセットに取得しようとしています。SqlDataAdapter.Update(myDataset) を実行した後、自動割り当てされた の値を読み取れるようにしたいのですがmyDataset.tables(0).Rows(0)("ID")、System.DBNull です (行が挿入されたにもかかわらず)。

(注: これを行うために新しいストアド プロシージャを明示的に記述したくありません!)

http://forums.asp.net/t/951025.aspxに投稿されることが多い 1 つの方法は、次のように 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 コレクションがリセットされるためです。元の (追加で追加されたパラメーターを失う)。

これの答えわかる人いますか???

4

14 に答える 14

10

これは私が以前に遭遇した問題です。バグは、da.Update(ds); を呼び出すときに発生するようです。挿入コマンドのパラメーター配列は、コマンド ビルダーから作成された初期リストにリセットされ、ID の追加された出力パラメーターが削除されます。

解決策は、新しい dataAdapter を作成してコマンドにコピーし、この新しいものを使用して 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);
于 2011-09-30T16:44:41.453 に答える
4

挿入コマンドは、出力パラメーターのいずれかを使用して挿入されたレコードを更新するか、UpdatedRowSourceプロパティを使用して最初に返されたレコード(または両方)を更新するように指示できます。

InsertCommand.UpdatedRowSource = UpdateRowSource.Both;

ストアドプロシージャを使用したい場合は、これで完了です。ただし、生のコマンド(コマンドビルダーの出力)を使用する必要があります。これでは、a)出力パラメーターまたはb)レコードを返すことはできません。どうしてこれなの?a)これはInsertCommandがどのように見えるかです...

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

コマンドに出力パラメーターを入力する方法はありません。では、b)はどうですか?残念ながら、DataAdapterは、コマンドExecuteNonQueryメソッドを呼び出してInsertコマンドを実行します。これはレコードを返さないため、アダプターが挿入されたレコードを更新する方法はありません。

したがって、ストアドプロシージャを使用するか、DataAdapterの使用をあきらめる必要があります。

于 2008-09-26T02:48:01.007 に答える
4

ここで私に合った答えを見つけました:http://www.dotnetmonster.com/Uwe/Forum.aspx/dotnet-ado-net/4933/Have-soln-but-need-understanding-return-IDENTITY-issue

機能したコード(サイトから-Girishに起因)

//_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);
于 2011-08-31T10:19:06.460 に答える
4

私にとってうまくいくのは、MissingSchemaAction を構成することです。

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

これにより、挿入後に主キー (ID または自動番号の場合) を取得できます。

幸運を。

于 2010-03-25T09:04:24.320 に答える
2

私にとっての問題は、コードが配置された場所でした。

次のように、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;
     }
}
于 2012-08-24T08:07:19.833 に答える
2

実際、これは私にとってはうまくいきます:

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

乾杯。

于 2010-01-08T15:47:18.667 に答える
2

私も同じ問題を抱えていました。commandbuilderによって生成されたコマンドをクローンしたときに解決されました。insertcommand の commandText を変更しても、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);            
    }
于 2010-03-04T07:17:15.140 に答える
2

(a) 1 つのレコードをテーブルに挿入し、(b) DataSet を使用し、(c) ストアド プロシージャを使用しない場合は、次のようにすることができます。

  1. dataAdapter を作成しますが、select ステートメントに WHERE 1=0 を追加して、テーブル全体をダウンロードする必要がないようにします - パフォーマンスのためのオプションの手順

  2. スコープ ID 選択ステートメントと出力パラメーターを使用してカスタム INSERT ステートメントを作成します。

  3. 通常の処理を行い、データセットを埋め、レコードを追加し、テーブルの更新を保存します。

  4. パラメータから ID を直接抽出できるようになりました。

例:

    '-- 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
于 2010-12-07T09:53:12.060 に答える
1

これらの他の方法がうまくいかなかった場合、.Net が提供するツール (SqlDataAdapter) などは、柔軟性に関して他にあまり提供しません。通常、次のレベルに進み、手動で作業を開始する必要があります。ストアド プロシージャは、SqlDataAdapter を使い続ける方法の 1 つです。それ以外の場合は、別のデータ アクセス ツールに移行する必要があります。.Net データ ライブラリはシンプルに設計されているため、制限があるためです。モデルが彼らのビジョンで機能しない場合は、独自のコードをある時点/レベルで展開する必要があります。

于 2008-09-26T01:54:24.397 に答える
1

コマンド オブジェクトのみを使用する別の方法。

Dim sb As New StringBuilder
sb.Append(" Insert into ")
sb.Append(tbl)
sb.Append(" ")
sb.Append(cnames)
sb.Append(" values ")
sb.Append(cvals)
sb.Append(";Select SCOPE_IDENTITY() as id") 'add identity selection

Dim sql As String = sb.ToString

Dim cmd As System.Data.Common.DbCommand = connection.CreateCommand
cmd.Connection = connection
cmd.CommandText = sql
cmd.CommandType = CommandType.Text
cmd.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord

'retrieve the new identity value, and update the object
Dim dec as decimal = CType(cmd.ExecuteScalar, Decimal)
于 2011-09-02T06:52:45.243 に答える
0

私の解決策:

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();
于 2016-11-15T16:27:51.957 に答える
-1

代わりにLINQの使用を検討しましたか?これは実際の質問に対応していないことは理解していますが、.NET 3.5を使用している場合は、実際にLINQを使用してみてください。実際、Code First EntityFrameworkの登場により、独自のDALをローリングする代わりに、比較的軽量な代替手段としてLINQtoSQLまたはEFのいずれかを簡単に選択できるようになったと思います。

于 2008-10-01T21:47:25.973 に答える