注
DataSet クラスと関連クラスは、アプリケーションがデータベースから切断されている間にアプリケーションがメモリ内のデータを操作できるようにする、2000 年代初頭のレガシ .NET Framework テクノロジです。 このテクノロジは、ユーザーがデータを変更し、変更をデータベースに保持できるアプリに特に役立ちます。 データセットは実証済みの成功したテクノロジですが、新しい .NET アプリケーションには Entity Framework Core を使用することをお勧めします。 Entity Framework は、オブジェクト モデルとして表形式データを操作するより自然な方法を提供し、よりシンプルなプログラミング インターフェイスを備えています。
コンカレンシー例外 (System.Data.DBConcurrencyException) は、2 人のユーザーがデータベース内の同じデータを同時に変更しようとすると発生します。 このチュートリアルでは、 DBConcurrencyExceptionをキャッチし、エラーの原因となった行を特定し、その処理方法を学習する方法を示す Windows アプリケーションを作成します。
このチュートリアルでは、次のプロセスについて説明します。
新しい Windows フォーム アプリ (.NET Framework) プロジェクトを 作成します。
Northwind Customers テーブルに基づいて新しいデータセットを作成します。
データを表示する DataGridView を含むフォームを作成します。
Northwind データベースの Customers テーブルのデータをデータセットに入力します。
サーバー エクスプローラーのテーブル データの表示機能を使用して、Customers-table のデータにアクセスし、レコードを変更します。
同じレコードを別の値に変更し、データセットを更新し、データベースに変更を書き込もうとすると、コンカレンシー エラーが発生します。
エラーをキャッチし、レコードのさまざまなバージョンを表示して、ユーザーがデータベースを続行して更新するか、更新を取り消すかを判断できるようにします。
[前提条件]
このチュートリアルでは、SQL Server Express LocalDB と Northwind サンプル データベースを使用します。
SQL Server Express LocalDB がない場合は、 SQL Server Express のダウンロード ページまたは Visual Studio インストーラーからインストールします。 Visual Studio インストーラーでは、データ ストレージと処理ワークロードの一部として、または個々のコンポーネントとして SQL Server Express LocalDB をインストールできます。
次の手順に従って Northwind サンプル データベースをインストールします。
Visual Studio で、 SQL Server オブジェクト エクスプローラー ウィンドウを開きます。 (SQL Server オブジェクト エクスプローラーは、Visual Studio インストーラーの データ ストレージと処理 ワークロードの一部としてインストールされます)。 SQL Server ノードを展開します。 LocalDB インスタンスを右クリックし、[ 新しいクエリ] を選択します。
クエリ エディター ウィンドウが開きます。
Northwind Transact-SQL スクリプトをクリップボードにコピーします。 この T-SQL スクリプトは、Northwind データベースを最初から作成し、データを設定します。
T-SQL スクリプトをクエリ エディターに貼り付け、[ 実行] ボタンを選択します。
しばらくすると、クエリの実行が完了し、Northwind データベースが作成されます。
新しいプロジェクトを作成する
まず、新しい Windows フォーム アプリケーションを作成します。
Visual Studio の [ファイル] メニューで、 [新規作成]>[プロジェクト] を選択します。
左側のウィンドウで Visual C# または Visual Basic を展開し、 Windows デスクトップを選択します。
中央のウィンドウで、 Windows フォーム アプリ プロジェクトの種類を選択します。
プロジェクトに ConcurrencyWalkthrough という名前を付け、[ OK] を選択します。
ConcurrencyWalkthrough プロジェクトが作成され、ソリューション エクスプローラーに追加され、デザイナーで新しいフォームが開きます。
Northwind データセットを作成する
次に、 NorthwindDataSet という名前のデータセットを作成します。
[ データ ] メニューの [ 新しいデータ ソースの追加] を選択します。
データ ソース構成ウィザードが開きます。
[ データ ソースの種類の選択 ] 画面で、[データベース] を選択 します。
使用可能な接続の一覧から Northwind サンプル データベースへの接続を選択します。 接続の一覧で接続が使用できない場合は、[ 新しい接続] を選択します。
注
ローカル データベース ファイルに接続している場合は、プロジェクトにファイルを追加するかどうかを確認するメッセージが表示されたら、[ いいえ ] を選択します。
[ アプリケーション構成ファイルに接続文字列を保存する ] 画面で、[ 次へ] を選択します。
[ テーブル ] ノードを展開し、[ Customers ] テーブルを選択します。 データセットの既定の名前は NorthwindDataSet である必要があります。
[ 完了] を 選択して、データセットをプロジェクトに追加します。
データ-バインドされたDataGridViewコントロールを作成する
このセクションでは、[データ ソース] ウィンドウから Windows フォームに Customers 項目をドラッグして、System.Windows.Forms.DataGridViewを作成します。
[ データ ソース ] ウィンドウを開くには、[ データ ] メニューの [ データ ソースの表示] を選択します。
[ データ ソース ] ウィンドウで、[ NorthwindDataSet ] ノードを展開し、[ Customers ] テーブルを選択します。
テーブル ノードの下矢印を選択し、ドロップダウン リストで DataGridView を選択します。
フォームの空の領域にテーブルをドラッグします。
CustomersDataGridView という名前のDataGridView コントロールと CustomersBindingNavigator という名前のBindingNavigatorが、BindingSourceにバインドされているフォームに追加されます。 これは、NorthwindDataSet の Customers テーブルにバインドされます。
フォームをテストする
これで、フォームをテストして、この時点まで想定どおりに動作することを確認できます。
F5 キーを押してアプリケーションを実行します。
フォームには、Customers テーブルのデータが入力された DataGridView コントロールが表示されます。
[デバッグ] メニューの [デバッグの停止] を選択します。
コンカレンシー エラーの処理
エラーの処理方法は、アプリケーションを管理する特定のビジネス ルールによって異なります。 このチュートリアルでは、コンカレンシー エラーを処理する方法の例として、次の戦略を使用します。
アプリケーションは、次の 3 つのバージョンのレコードをユーザーに提示します。
データベース内の現在のレコード
データセットに読み込まれた元のレコード
データセット内の提案された変更
その後、ユーザーは、提案されたバージョンでデータベースを上書きするか、更新を取り消してデータベースの新しい値でデータセットを更新できます。
コンカレンシー エラーの処理を有効にするには
カスタム エラー ハンドラーを作成します。
ユーザーに選択肢を表示します。
ユーザーの応答を処理します。
更新プログラムを再送信するか、データセット内のデータをリセットします。
コンカレンシー例外を処理するコードを追加する
更新を実行しようとして例外が発生した場合は、通常、発生した例外によって提供される情報を使用して何かを行います。 このセクションでは、データベースの更新を試みるコードを追加します。 また、発生する可能性のある DBConcurrencyException や、その他の例外も処理します。
注
CreateMessageメソッドとProcessDialogResultsメソッドは、チュートリアルの後半で追加します。
Form1_Loadメソッドの下に次のコードを追加します。private void UpdateDatabase() { try { this.customersTableAdapter.Update(this.northwindDataSet.Customers); MessageBox.Show("Update successful"); } catch (DBConcurrencyException dbcx) { DialogResult response = MessageBox.Show(CreateMessage((NorthwindDataSet.CustomersRow) (dbcx.Row)), "Concurrency Exception", MessageBoxButtons.YesNo); ProcessDialogResult(response); } catch (Exception ex) { MessageBox.Show("An error was thrown while attempting to update the database."); } }
CustomersBindingNavigatorSaveItem_Clickメソッドを置き換えて、次のようにUpdateDatabaseメソッドを呼び出します。
ユーザーに選択肢を表示する
先ほど記述したコードは、 CreateMessage プロシージャを呼び出して、エラー情報をユーザーに表示します。 このチュートリアルでは、メッセージ ボックスを使用して、さまざまなバージョンのレコードをユーザーに表示します。 これにより、ユーザーはレコードを変更で上書きするか、編集を取り消すかを選択できます。 ユーザーがメッセージ ボックスでオプションを選択 (ボタンをクリック) すると、応答が ProcessDialogResult メソッドに渡されます。
次のコードを コード エディターに追加して、メッセージを作成します。
UpdateDatabase メソッドの下に次のコードを入力します。
private string CreateMessage(NorthwindDataSet.CustomersRow cr)
{
return
"Database: " + GetRowData(GetCurrentRowInDB(cr), DataRowVersion.Default) + "\n" +
"Original: " + GetRowData(cr, DataRowVersion.Original) + "\n" +
"Proposed: " + GetRowData(cr, DataRowVersion.Current) + "\n" +
"Do you still want to update the database with the proposed value?";
}
//--------------------------------------------------------------------------
// This method loads a temporary table with current records from the database
// and returns the current values from the row that caused the exception.
//--------------------------------------------------------------------------
private NorthwindDataSet.CustomersDataTable tempCustomersDataTable =
new NorthwindDataSet.CustomersDataTable();
private NorthwindDataSet.CustomersRow GetCurrentRowInDB(NorthwindDataSet.CustomersRow RowWithError)
{
this.customersTableAdapter.Fill(tempCustomersDataTable);
NorthwindDataSet.CustomersRow currentRowInDb =
tempCustomersDataTable.FindByCustomerID(RowWithError.CustomerID);
return currentRowInDb;
}
//--------------------------------------------------------------------------
// This method takes a CustomersRow and RowVersion
// and returns a string of column values to display to the user.
//--------------------------------------------------------------------------
private string GetRowData(NorthwindDataSet.CustomersRow custRow, DataRowVersion RowVersion)
{
string rowData = "";
for (int i = 0; i < custRow.ItemArray.Length ; i++ )
{
rowData = rowData + custRow[i, RowVersion].ToString() + " ";
}
return rowData;
}
ユーザーの応答を処理する
また、メッセージ ボックスに対するユーザーの応答を処理するコードも必要です。 オプションは、提案された変更でデータベース内の現在のレコードを上書きするか、ローカルの変更を破棄して、データベース内の現在のレコードでデータ テーブルを更新することです。 ユーザーが [はい] を選択した場合、 Merge メソッドは preserveChanges 引数を true に設定して呼び出 されます。 これにより、レコードの元のバージョンがデータベース内のレコードと一致するため、更新の試行が成功します。
前のセクションで追加したコードの下に、次のコードを追加します。
// This method takes the DialogResult selected by the user and updates the database
// with the new values or cancels the update and resets the Customers table
// (in the dataset) with the values currently in the database.
private void ProcessDialogResult(DialogResult response)
{
switch (response)
{
case DialogResult.Yes:
northwindDataSet.Merge(tempCustomersDataTable, true, MissingSchemaAction.Ignore);
UpdateDatabase();
break;
case DialogResult.No:
northwindDataSet.Merge(tempCustomersDataTable);
MessageBox.Show("Update cancelled");
break;
}
}
フォームの動作をテストする
これで、フォームをテストして、期待どおりに動作することを確認できます。 コンカレンシー違反をシミュレートするには、NorthwindDataSet を入力した後でデータベース内のデータを変更します。
F5 キーを押してアプリケーションを実行します。
フォームが表示されたら、実行したままにして、Visual Studio IDE に切り替えます。
[ 表示 ] メニューの [ サーバー エクスプローラー] を選択します。
サーバー エクスプローラーで、アプリケーションで使用している接続を展開し、[テーブル] ノードを展開します。
Customers テーブルを右クリックし、[テーブル データの表示] を選択します。
最初のレコード (ALFKI) で 、ContactName をMaria Anders2 に変更します。
注
別の行に移動して変更をコミットします。
ConcurrencyWalkthrough の実行中のフォームに切り替えます。
フォームの最初のレコード (ALFKI) で 、ContactName をMaria Anders1 に変更します。
[保存] ボタンを選択します。
コンカレンシー エラーが発生し、メッセージ ボックスが表示されます。
[ いいえ ] を選択すると、更新が取り消され、データベース内の現在の値でデータセットが更新されます。 [ はい ] を選択すると、提案された値がデータベースに書き込まれます。