ID 値および Autonumber 値の取得
DataTable の列を自動的にインクリメントする主キーとして設定することにより、テーブルの各行に一意の値が設定されるようにします。しかし、アプリケーションに対して複数のクライアントが存在し、各クライアントがそれぞれの DataTable インスタンスを使用している場合もあります。その場合は、DataTable の個別のインスタンスに最終的に同じデータが格納されるようにする必要があります。すべてのクライアントが同じ 1 つのデータ ソースを使用するため、データ ソースに自動インクリメント値を定義することで、このような競合を解決できます。自動インクリメント値を定義するには、Microsoft SQL Server の ID フィールドまたは Microsoft Access の Autonumber フィールドを使用します。
データ ソースを使用して DataSet に追加された新しい行の ID 列または Autonumber 列にデータを読み込むと、DataSet が直接データ ソースに接続されていないために特有の状況が発生します。その結果、DataSet は、データ ソースによって自動的に生成された値を認識できなくなります。しかし、出力パラメータを持つストアド プロシージャを作成できる Microsoft SQL Server などのデータ ソースでは、新しい ID 値などの自動生成値を出力パラメータとして指定し、DataAdapter を使用してその値を DataSet 内の対応する列に割り当てることができます。
データ ソースが、出力パラメータを持つストアド プロシージャをサポートしていない場合もあります。その場合は RowUpdated イベントを使用して自動生成値を取得し、その値を DataSet 内の挿入または更新された列に配置できます。このセクションでは、Microsoft Access 2000 以降で Jet 4.0 OLE DB プロバイダを使用して RowUpdated イベントにコードを追加することにより、挿入が発生したかどうかを確認し、自動インクリメント値を取得し、その値を現在の更新行に格納する方法を示すサンプルを提供します。
次のストアド プロシージャおよびコード例では、Microsoft SQL Server テーブルの自動的にインクリメントされた ID 値を、DataSet のテーブルに追加された行の対応する列に割り当てる方法を示します。このストアド プロシージャは、Northwind データベースの Categories テーブルに新しい行を挿入し、SCOPE_IDENTITY() から返された ID 値を出力パラメータとして返すために使用されています。
CREATE PROCEDURE InsertCategory
@CategoryName nchar(15),
@Identity int OUT
AS
INSERT INTO Categories (CategoryName) VALUES(@CategoryName)
SET @Identity = SCOPE_IDENTITY()
その後、InsertCategory ストアド プロシージャを DataAdapter.InsertCommand のソースとして指定できます。ID 出力パラメータを受け取るパラメータが作成されます。作成されたパラメータは、ParameterDirection.Output の Direction と、DataSet のローカル Categories テーブルの CategoryID 列として指定された SourceColumn を持っています。追加された行に対して InsertCommand が処理されると、自動インクリメント ID 値が出力パラメータとして返され、現在の行の CategoryID 列に設定されます。
自動インクリメント値を出力パラメータとして返し、その値を DataSet の CategoryID 列のソース値として指定する方法を次のコード例に示します。
Dim nwindConn As SqlConnection = New SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind")
Dim catDA As SqlDataAdapter = New SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)
catDA.InsertCommand = New SqlCommand("InsertCategory", nwindConn)
catDA.InsertCommand.CommandType = CommandType.StoredProcedure
catDA.InsertCommand.Parameters.Add("@CategoryName", SqlDbType.NChar, 15, "CategoryName")
Dim myParm As SqlParameter = catDA.InsertCommand.Parameters.Add("@Identity", SqlDbType.Int, 0, "CategoryID")
myParm.Direction = ParameterDirection.Output
nwindConn.Open()
Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")
Dim newRow As DataRow = catDS.Tables("Categories").NewRow()
newRow("CategoryName") = "New Category"
catDS.Tables("Categories").Rows.Add(newRow)
catDA.Update(catDS, "Categories")
nwindConn.Close()
[C#]
SqlConnection nwindConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind");
SqlDataAdapter catDA = new SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);
catDA.InsertCommand = new SqlCommand("InsertCategory", nwindConn);
catDA.InsertCommand.CommandType = CommandType.StoredProcedure;
catDA.InsertCommand.Parameters.Add("@CategoryName", SqlDbType.NChar, 15, "CategoryName");
SqlParameter myParm = catDA.InsertCommand.Parameters.Add("@Identity", SqlDbType.Int, 0, "CategoryID");
myParm.Direction = ParameterDirection.Output;
nwindConn.Open();
DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");
DataRow newRow = catDS.Tables["Categories"].NewRow();
newRow["CategoryName"] = "New Category";
catDS.Tables["Categories"].Rows.Add(newRow);
catDA.Update(catDS, "Categories");
nwindConn.Close();
Microsoft Access では、ストアド プロシージャやバッチ コマンド処理がサポートされていないため、上の例のようにテーブルのソース列に出力パラメータを割り当てることはできません。しかし、Microsoft Access 2000 以降では、INSERT 後に Autonumber フィールドの値を取得する、@@IDENTITY プロパティがサポートされています。RowUpdated イベントを使用すると、INSERT が発生したかどうかを確認し、最新の @@IDENTITY 値を取得し、その値を DataSet のローカル テーブルの ID 列に配置できます。
Microsoft Access 2000 Northwind データベースの Categories テーブルに新しい値を挿入する方法を次のコード例に示します。この例では、レコードが Categories テーブルに挿入されたときに、Jet エンジンによって生成された Autonumber 値を、RowUpdated イベントおよび Access データベースを使用して格納しています。この操作を実行できるのは、Jet 4.0 OLE DB プロバイダおよび Microsoft Access 2000 以降だけです。
Imports System
Imports System.Data
Imports System.Data.OleDb
Imports Microsoft.VisualBasic
Public class Sample
Shared nwindConn As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=c:\Program Files\Microsoft Office\Office\Samples\northwind.mdb;")
Public Shared Sub Main()
' Use the DataAdapter to fill and update the DataSet.
Dim catDA As OleDbDataAdapter = New OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories ORDER BY CategoryID", nwindConn)
catDA.InsertCommand = New OleDbCommand("INSERT INTO Categories (CategoryName) Values(?)", nwindConn)
catDA.InsertCommand.CommandType = CommandType.Text
catDA.InsertCommand.Parameters.Add("@CategoryName", OleDbType.Char, 15, "CategoryName")
nwindConn.Open()
' Fill the DataSet.
Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")
' Add a new row.
Dim newRow As DataRow = catDS.Tables("Categories").NewRow()
newRow("CategoryName") = "New Category"
catDS.Tables("Categories").Rows.Add(newRow)
' Include an event to fill in the Autonumber value.
AddHandler catDA.RowUpdated, New OleDbRowUpdatedEventHandler(AddressOf OnRowUpdated)
' Update the DataSet.
catDA.Update(catDS, "Categories")
nwindConn.Close()
End Sub
Private Shared Sub OnRowUpdated(sender As Object, args As OleDbRowUpdatedEventArgs)
' Include a variable and a command to retrieve the identity value from the Access database.
Dim newID As Integer = 0
Dim idCMD As OleDbCommand = New OleDbCommand("SELECT @@IDENTITY", nwindConn)
If args.StatementType = StatementType.Insert
' Retrieve the identity value and store it in the CategoryID column.
newID = CInt(idCMD.ExecuteScalar())
args.Row("CategoryID") = newID
End If
End Sub
End Class
[C#]
using System;
using System.Data;
using System.Data.OleDb;
public class Sample
{
static OleDbConnection nwindConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" +
@"Data Source=c:\Program Files\Microsoft Office\Office\Samples\northwind.mdb;");
public static void Main()
{
// Use the DataAdapter to fill and update the DataSet.
OleDbDataAdapter catDA = new OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories ORDER BY CategoryID", nwindConn);
catDA.InsertCommand = new OleDbCommand("INSERT INTO Categories (CategoryName) Values(?)", nwindConn);
catDA.InsertCommand.CommandType = CommandType.Text;
catDA.InsertCommand.Parameters.Add("@CategoryName", OleDbType.Char, 15, "CategoryName");
nwindConn.Open();
// Fill the DataSet.
DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");
// Add a new row.
DataRow newRow = catDS.Tables["Categories"].NewRow();
newRow["CategoryName"] = "New Category";
catDS.Tables["Categories"].Rows.Add(newRow);
// Include an event to fill in the Autonumber value.
catDA.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);
// Update the DataSet.
catDA.Update(catDS, "Categories");
nwindConn.Close();
}
protected static void OnRowUpdated(object sender, OleDbRowUpdatedEventArgs args)
{
// Include a variable and a command to retrieve the identity value from the Access database.
int newID = 0;
OleDbCommand idCMD = new OleDbCommand("SELECT @@IDENTITY", nwindConn);
if (args.StatementType == StatementType.Insert)
{
// Retrieve the identity value and store it in the CategoryID column.
newID = (int)idCMD.ExecuteScalar();
args.Row["CategoryID"] = newID;
}
}
}
参照
サンプル ADO.NET シナリオ | ADO.NET を使用したデータのアクセス | .NET Framework データ プロバイダによるデータのアクセス