2011年02月13日
LINQ to SQL 番外編 目次
メールマガジン『C#プログラミングレッスン - LINQ to SQL編』で説明しきれなかった内容をブログに書いたものの目次です。
メールマガジンのバックナンバーも併せてどうぞ!
メールマガジンのバックナンバーも併せてどうぞ!
-
0.はじめに
1.コンパイル済みクエリ
2.DataContextのスコープ
3.接続文字列
4.DataContext.OnCreateって何?
5.読み込み専用でデータベースを利用する
6.データベースの作成
7.IDENTITY列の扱い
8.NULL許容の列を扱う
9.timestamp列とUpdateCheckプロパティ
10.ColumnAttribute.UpdateCheck プロパティ
11.独自メソッドを追加する
12.ADO.NETとの相互運用
13.エンティティクラスに検証を追加する
14.エンティティ クラスの更新時にデータを検証するには
15.挿入、更新、削除の動作のカスタマイズ
16.SelectWithUpdlock メソッドを自作
17.同時実行例外をいつスローさせるのか
18.競合時の処理
2011年02月10日
LINQ to SQL番外編 - 競合時の処理
LINQ to SQLでは、ChangeConflictException の例外が発生した場合、その競合解決に、
DataContext.ChangeConflictsコレクションのResolveAllメソッドが利用できます。
そのコード例を示します。
ResolveAllメソッドの引数の型であるRefreshModeには、
KeepChanges、KeepCurrentValues、OverwriteCurrentValues
の3つの値があり、この値によりどのように競合を解決するのかを指定します。
KeepChanges
変更された値と変更前(他のユーザによって変更された)値をマージします。
両方で変更されていた場合は、当該クライアントの変更を優先します。
KeepCurrentValues
他のユーザによって変更された値は無視され、当該クライアントからの変更が有効になります。
OverwriteCurrentValues
当該クライアントによって変更された値は無視され、他ユーザによって変更された値が有効になります。
これを表で表すと以下のようになります。
※ユーザ2で競合が発生し、ChangeConflicts.ResolveAllで競合解決を図った場合の例
競合の詳細な情報を取得したい場合は、DataContext.ChangeConflictsプロパティを参照します。
以下にそのサンプルコードを示します。
『LINQ to SQL番外編』は、今回を持って終了です。
DataContext.ChangeConflictsコレクションのResolveAllメソッドが利用できます。
そのコード例を示します。
ResolveAllメソッドの引数の型であるRefreshModeには、
KeepChanges、KeepCurrentValues、OverwriteCurrentValues
の3つの値があり、この値によりどのように競合を解決するのかを指定します。
KeepChanges
変更された値と変更前(他のユーザによって変更された)値をマージします。
両方で変更されていた場合は、当該クライアントの変更を優先します。
KeepCurrentValues
他のユーザによって変更された値は無視され、当該クライアントからの変更が有効になります。
OverwriteCurrentValues
当該クライアントによって変更された値は無視され、他ユーザによって変更された値が有効になります。
これを表で表すと以下のようになります。
| カラムA | カラムB | カラムC | |
| 1.User1,USer2が読み込んだ値 | aaa | bbb | ccc |
| 2.User1が更新 | sss | ttt | |
| 3.User2が更新 | xxx | yyy | |
| 結果:KeepChanges | xxx | ttt | yyy |
| 結果:KeepCurrentValues | xxx | bbb | yyy |
| 結果:OverwriteCurrentValues | sss | ttt | ccc |
※ユーザ2で競合が発生し、ChangeConflicts.ResolveAllで競合解決を図った場合の例
競合の詳細な情報を取得したい場合は、DataContext.ChangeConflictsプロパティを参照します。
以下にそのサンプルコードを示します。
『LINQ to SQL番外編』は、今回を持って終了です。
2011年02月06日
LINQ to SQL番外編 - 同時実行例外をいつスローさせるのか
LINQ to SQLでは、楽観的同時実行制御の仕組みが組み込まれています。
たとえば、Customersテーブルの行のContactNameカラムをSubmitChangesメソッドで更新する場合、
LINQ to SQLは以下のような UPDATE文を発行します。
パラメータ p0, p1, p2... には、変更前の値が渡されます。
LINQ to SQL では、これによって他のユーザによって変更されていない場合にだけ、
このUPDATE文が成功することになります。
失敗した場合は、System.Data.Linq.ChangeConflictException例外が発生しますので、
他のユーザによる変更を把握できることになります。
SubmitChangesメソッドには引数を持つメソッドがオーバーロードされていて、
この例外が発生するタイミングを以下の2種類から指定できます。
1. 最初の失敗のときにのみ、例外をスローする
2. 失敗したすべての更新についての情報を収集してから、例外をスローする。
具体的な例外発生時の処理を指定したコードを示します。
競合発生した場合、どのような対応ができるのかは、次回説明します。
LINQ to SQL番外編とは
メールマガジン『C#プログラミングレッスン』で連載した「LINQ to SQL編」には書ききれなかった情報を
「番外編」としてブログにアップしています。
併せて、メールマガジン『C#プログラミングレッスン - Linq to SQL編』も読んでいただけると、
より理解が深まると思います。
バックナンバーは、メールマガジン『C#プログラミングレッスン』書庫のページからダウンロードできます。
たとえば、Customersテーブルの行のContactNameカラムをSubmitChangesメソッドで更新する場合、
LINQ to SQLは以下のような UPDATE文を発行します。
パラメータ p0, p1, p2... には、変更前の値が渡されます。
LINQ to SQL では、これによって他のユーザによって変更されていない場合にだけ、
このUPDATE文が成功することになります。
失敗した場合は、System.Data.Linq.ChangeConflictException例外が発生しますので、
他のユーザによる変更を把握できることになります。
SubmitChangesメソッドには引数を持つメソッドがオーバーロードされていて、
この例外が発生するタイミングを以下の2種類から指定できます。
1. 最初の失敗のときにのみ、例外をスローする
2. 失敗したすべての更新についての情報を収集してから、例外をスローする。
具体的な例外発生時の処理を指定したコードを示します。
競合発生した場合、どのような対応ができるのかは、次回説明します。
LINQ to SQL番外編とは
メールマガジン『C#プログラミングレッスン』で連載した「LINQ to SQL編」には書ききれなかった情報を
「番外編」としてブログにアップしています。
併せて、メールマガジン『C#プログラミングレッスン - Linq to SQL編』も読んでいただけると、
より理解が深まると思います。
バックナンバーは、メールマガジン『C#プログラミングレッスン』書庫のページからダウンロードできます。
2011年02月03日
LINQ to SQL番外編 - SelectWithUpdlock メソッドを自作
LINQ to SQLでは、UPDLOCK付きの SQL文を発行させることができません。
以下のように書ければ良いのですが...
このような場合は、しかたないので、ExecuteQueryを使います。
これで、UPDLOCK付きのSELECT文を発行できます。
ただ、こういったSQLは、ストアドプロシージャを使う方が良いかな、と個人的には思います。
このあたり、LINQ to SQLの経験豊富な方の意見を聞いてみたいものです。
しかし、いちいちSQL文を手書きするのはイヤですよね。
ちょっと工夫してみましょう。
こんなメソッドを作ってみました。
このメソッドを使うコードは、こんな感じです。
※ 拡張メソッドにすれば、もっと使いやすくなりますね。
SelectWithUpdlockが、何をやっているかというと、
で、queryの実際のSQL文を文字列として取り出しています。
次に、
で、SQL文の FORM句の後ろに、"WITH (UPDLOCK)" という文字列を追加した、SQL文を作ります。
3行目の
ですが、Parametersに入っているパラメータの値を、配列に変換しています。
まず、Castメソッドを使って、Parametersを IEnumerableに
変換し、クエリ演算子が利用できるようにします。そして、Select(p => p.Value)で、
パラメータのシーケンスを取り出し、ToArray()で配列にします。
最後に
、
で、クエリを発行し、結果を戻しています。
このSelectWithUpdlockメソッドですが、すべてのクエリ式で試したわけではない
ですが、それなりに動いているみたいです。
かなり、強引な方法ですが、これを応用すれば、LINQ to SQLが対応していないSQL文の実行も、
わざわざSQL文を書かなくても、LINQ to SQL上で実行することが可能になる?かもしれません。
以下のように書ければ良いのですが...
このような場合は、しかたないので、ExecuteQueryを使います。
これで、UPDLOCK付きのSELECT文を発行できます。
ただ、こういったSQLは、ストアドプロシージャを使う方が良いかな、と個人的には思います。
このあたり、LINQ to SQLの経験豊富な方の意見を聞いてみたいものです。
しかし、いちいちSQL文を手書きするのはイヤですよね。
ちょっと工夫してみましょう。
こんなメソッドを作ってみました。
このメソッドを使うコードは、こんな感じです。
※ 拡張メソッドにすれば、もっと使いやすくなりますね。
SelectWithUpdlockが、何をやっているかというと、
で、queryの実際のSQL文を文字列として取り出しています。
次に、
で、SQL文の FORM句の後ろに、"WITH (UPDLOCK)" という文字列を追加した、SQL文を作ります。
3行目の
ですが、Parametersに入っているパラメータの値を、配列に変換しています。
まず、Castメソッドを使って、Parametersを IEnumerable
変換し、クエリ演算子が利用できるようにします。そして、Select(p => p.Value)で、
パラメータのシーケンスを取り出し、ToArray()で配列にします。
最後に
、
で、クエリを発行し、結果を戻しています。
このSelectWithUpdlockメソッドですが、すべてのクエリ式で試したわけではない
ですが、それなりに動いているみたいです。
かなり、強引な方法ですが、これを応用すれば、LINQ to SQLが対応していないSQL文の実行も、
わざわざSQL文を書かなくても、LINQ to SQL上で実行することが可能になる?かもしれません。
2011年01月30日
LINQ to SQL番外編 - 挿入、更新、削除の動作のカスタマイズ
まず、LINQ to SQLで更新を行った時に、どのようなSQLが発行されているのか、
以下のコードで見てみましょう。
発行されるSQLは、
となります。
SQL の SETで設定されるカラムは、ContactTitleだけです。これからわかるのは、
どのカラムの値が変更されたかで、発行されるSQLが異なるということです。
つまり、動的にSQL文を組み立てているのです。
なかなかすごいことをやっているなと思うのですが、動的にSQLを組み立てること
があだになり、一度に大量のデータを更新するような場合は、SQLの組み立てに
時間がかかり、速度低下が起こる可能性もあります。これは、実際に試していない
ので、僕の想像の域を脱しませんが...
この時に、独自のSQLが使えれば、速度向上につながるかもしれません。
また、あるカラムは更新させたくないといった場合も、独自のSQLが使えれば便利です。
LINQ to SQLは、そのような要望に応えられるよう、Delete, Insert, Update を
既定のものから、別のものに置き換えることができます。
ただし、置き換え後のものは、ストアドプロシージャのみとなります。
では、さっそく、Updateのカスタマイズをやってみましょう。
まず、以下のようなストアドプロシージャを作成します。
このストアドプロシージャだと楽観的同時実行制御が行えませんが、
Updateをカスタマイズする方法を示すことが目的なので、そこは大目にみてください。
サーバーエクスプローラの[ストアドプロシージャ]のフォルダを右クリックし、
[新しいストアドプロシージャの追加]を選ぶと、エディタ部にひな形が表示されますので、
それを上記のストアドプロシージャの記述に変更します。
[保存]ボタンを押し、ストアドプロシージャを Northwindデータベースに追加します。
追加されたストアドプロシージャを Northwind.dbml の O/Rデザイナー画面の空白部にD&Dします。
すると、Northwind.dbmlにストアドプロシージャに対応するメソッドが追加されます。
O/RデザイナーのCustomers クラスをクリックし、プロパティウィンドウに、
Customersデータクラスのプロパティを表示します。
Update プロパティに、先ほど作成した UpdateCustomer メソッドを割り当てます。
以上で、Updateメソッドのカスタマイズが完了しました。
これで最初に示したコードを実行すると、
というSQLが発行されているのがわかります。
なお、
と Cityカラムを更新しようとしても、ストアドプロシージャには、この値はわたりませんので、
Cityカラムは更新されることはありません。
同様の方法で、Insert, Delete もカスタマイズすることが可能です。
LINQ to SQL番外編とは
メールマガジン『C#プログラミングレッスン』で連載した「LINQ to SQL編」には書ききれなかった情報を
「番外編」としてブログにアップしています。
併せて、メールマガジン『C#プログラミングレッスン - Linq to SQL編』も読んでいただけると、
より理解が深まると思います。
バックナンバーは、メールマガジン『C#プログラミングレッスン』書庫のページからダウンロードできます。
以下のコードで見てみましょう。
発行されるSQLは、
となります。
SQL の SETで設定されるカラムは、ContactTitleだけです。これからわかるのは、
どのカラムの値が変更されたかで、発行されるSQLが異なるということです。
つまり、動的にSQL文を組み立てているのです。
なかなかすごいことをやっているなと思うのですが、動的にSQLを組み立てること
があだになり、一度に大量のデータを更新するような場合は、SQLの組み立てに
時間がかかり、速度低下が起こる可能性もあります。これは、実際に試していない
ので、僕の想像の域を脱しませんが...
この時に、独自のSQLが使えれば、速度向上につながるかもしれません。
また、あるカラムは更新させたくないといった場合も、独自のSQLが使えれば便利です。
LINQ to SQLは、そのような要望に応えられるよう、Delete, Insert, Update を
既定のものから、別のものに置き換えることができます。
ただし、置き換え後のものは、ストアドプロシージャのみとなります。
では、さっそく、Updateのカスタマイズをやってみましょう。
まず、以下のようなストアドプロシージャを作成します。
このストアドプロシージャだと楽観的同時実行制御が行えませんが、
Updateをカスタマイズする方法を示すことが目的なので、そこは大目にみてください。
サーバーエクスプローラの[ストアドプロシージャ]のフォルダを右クリックし、
[新しいストアドプロシージャの追加]を選ぶと、エディタ部にひな形が表示されますので、
それを上記のストアドプロシージャの記述に変更します。
[保存]ボタンを押し、ストアドプロシージャを Northwindデータベースに追加します。
追加されたストアドプロシージャを Northwind.dbml の O/Rデザイナー画面の空白部にD&Dします。
すると、Northwind.dbmlにストアドプロシージャに対応するメソッドが追加されます。
O/RデザイナーのCustomers クラスをクリックし、プロパティウィンドウに、
Customersデータクラスのプロパティを表示します。
Update プロパティに、先ほど作成した UpdateCustomer メソッドを割り当てます。
以上で、Updateメソッドのカスタマイズが完了しました。
これで最初に示したコードを実行すると、
というSQLが発行されているのがわかります。
なお、
と Cityカラムを更新しようとしても、ストアドプロシージャには、この値はわたりませんので、
Cityカラムは更新されることはありません。
同様の方法で、Insert, Delete もカスタマイズすることが可能です。
LINQ to SQL番外編とは
メールマガジン『C#プログラミングレッスン』で連載した「LINQ to SQL編」には書ききれなかった情報を
「番外編」としてブログにアップしています。
併せて、メールマガジン『C#プログラミングレッスン - Linq to SQL編』も読んでいただけると、
より理解が深まると思います。
バックナンバーは、メールマガジン『C#プログラミングレッスン』書庫のページからダウンロードできます。
2011年01月25日
LINQ to SQL番外編 - エンティティ クラスの更新時にデータを検証するには
たぶん、この記事に書いてある情報は、日本語初の情報です。
MSDNライブラリの情報も中途半端で正しい内容ではありません。
前回、変更時の値のチェックについて説明しましたが、エンティティクラス全体の更新時に
データを検証することもできます。
この更新時の検証では、業務アプリケーションの要件に応じて複数のカラムの値を見て、
検証することができます。
以下にNorthwindデータベースを例に、その手順を示します。
ExecuteDynamicInsertUpdateメソッドを呼び出すのがカギです。
なぜか、MSDNライブラリの以下のページには、このことが書いてありません。
http://msdn.microsoft.com/ja-jp/library/bb629296.aspx
これで、NorthwindDataContextクラスに検証機能を組み込むことができました。
DBを更新する側のコードを示します。
この例では、SubmitChanges メソッドを呼び出すと、InsertCustomer メソッドが呼び出され、
検証ロジックが動いた後、行が挿入されます。
★の行をコメントにして実行すると、検証で例外が発生します。
MSDNライブラリの情報も中途半端で正しい内容ではありません。
前回、変更時の値のチェックについて説明しましたが、エンティティクラス全体の更新時に
データを検証することもできます。
この更新時の検証では、業務アプリケーションの要件に応じて複数のカラムの値を見て、
検証することができます。
以下にNorthwindデータベースを例に、その手順を示します。
- Visual Studio のソリューションエクスプローラで、プロジェクトに追加されているNorthwind.dbml
ファイル(LINQ to SQL クラスファイル)を右クリックします。
- コンテキストメニューから「[コードの表示] をクリックします。コード エディターが開き、
NorthwindDataContext の部分クラスが表示されます。
- 検証メソッドを作成します。
サンプルコードのため、System.Exception例外を発生させていますが、
本来は別の例外(あるいは独自例外)を発生させるべきかと思います。
- NorthwindDataContextの部分クラスに部分メソッドを追加し、検証コードを呼び出す
コードを記述します。
"partial " までタイプすると、"partial " までタイプすると、インテリセンスが働き、
メソッド一覧が表示されますので、UpdateXxxxxx を選んでください。Xxxxxx は検証をしたいクラス名です。
すると、以下のようなコードが挿入されます。
- メソッドに検証メソッドの呼び出しと、DB更新のコードを追加します。
- 同様に、InsertCustomerを追加します。
ExecuteDynamicInsertUpdateメソッドを呼び出すのがカギです。
なぜか、MSDNライブラリの以下のページには、このことが書いてありません。
http://msdn.microsoft.com/ja-jp/library/bb629296.aspx
これで、NorthwindDataContextクラスに検証機能を組み込むことができました。
DBを更新する側のコードを示します。
この例では、SubmitChanges メソッドを呼び出すと、InsertCustomer メソッドが呼び出され、
検証ロジックが動いた後、行が挿入されます。
★の行をコメントにして実行すると、検証で例外が発生します。
2011年01月23日
LINQ to SQL番外編 - 「LINQ to SQLエンティティクラス」に検証を追加する
今回は、「LINQ to SQLエンティティクラス」にデータの検証ロジックを追加する方法を
説明します。
検証ロジックを追加することで、特定の列が変更されたときに検証を行う
ことが可能になります。
O/Rデザイナーが生成する「LINQ to SQLエンティティクラス」には、いくつかの
パーシャルメソッドが用意されており、
このパーシャルメソッドを実装することで、
データの挿入、変更時における検証処理を実現できます。
■検証コードの作成手順
ここでは、(LINQ to SQLエンティティクラスの)CategoryクラスのCategoryNameカラムが
変更された時に、ある検証を行うための手順を示します。
- O/Rデザイナーで、.dbmlを開きます。
- デザイナーに表示されるCategoryクラスを右クリックし、[コードの表示]をク
リックします。
- 選択したクラスのパーシャルクラスが表示されますので、このクラス内で、
以下のように、"partial " までタイプすると、
インテリセンスが働き、メソッド一覧が表示されますので、実装したいメ ソッド(OnCategoryNameChanging)を選択してください。
- OnCategoryNameChangingを選択すると、以下のようなコードが挿入されます。
- メソッドに検証ロジックを追加できます。ここでは、すべてが数字ならエラーにしています。
■検証ロジックの動作確認
★の行で、OnCategoryNameChanging()が呼び出され、例外が発生しているのを確認できると思います。
2011年01月18日
LINQ to SQL番外編 - ADO.NETとの相互運用
ADO.NETの接続型アクセスを使った以下のようなコードがあったとします。
ここにLINQのコードを追加することができます。
DataContextを作成する際に、既存の接続を指定するだけです。
Connectionプロパティを使用して、接続を明示的に閉じることもできます。
ここにLINQのコードを追加することができます。
DataContextを作成する際に、既存の接続を指定するだけです。
Connectionプロパティを使用して、接続を明示的に閉じることもできます。
2011年01月16日
LINQ to SQL番外編 - 独自メソッドを追加する
ADO.NET2.0のTableAdapterでは、GetXxxxxById() などといった独自のメソッドを
追加し、ビジネスロジック層からは、SQL文を意識しないで目的のデータを取得で
きるようにする方法がありました。
LINQ to SQLで同様のことを行う場合は、DataContextにメソッドを追加すること
で、対応できます。
コードジェネレータは用意されていないので、手動で追加することになります。
今回は、「ProductIDを指定しProductオブジェクトを取得する」メソッドを追加してみましょう。
1. Northwind.dbmlのO/Rデザイナーを開きます。
2. デザイナーの空白部分を右クリックし、[コードの表示]を選択します。
3. パーシャルクラス NorthwindDataContext が表示されますので、ここでGetProductById()メソッドを追加します。
もちろん、このメソッド内で、ストアドプロシージャを呼び出すことも可能です。
追加し、ビジネスロジック層からは、SQL文を意識しないで目的のデータを取得で
きるようにする方法がありました。
LINQ to SQLで同様のことを行う場合は、DataContextにメソッドを追加すること
で、対応できます。
コードジェネレータは用意されていないので、手動で追加することになります。
今回は、「ProductIDを指定しProductオブジェクトを取得する」メソッドを追加してみましょう。
1. Northwind.dbmlのO/Rデザイナーを開きます。
2. デザイナーの空白部分を右クリックし、[コードの表示]を選択します。
3. パーシャルクラス NorthwindDataContext が表示されますので、ここでGetProductById()メソッドを追加します。
もちろん、このメソッド内で、ストアドプロシージャを呼び出すことも可能です。
2011年01月12日
LINQ to SQL番外編 - ColumnAttribute.UpdateCheck プロパティ
前回の記事でUpdateCheckプロパティについて触れましたが、もう少し詳しくこの
UpdateCheckについてみていきましょう。
このプロパティは、LINQ to SQL がオプティミスティック同時実行競合を検出する方法を
取得または設定するものです。以下の3つの列挙値を利用できます。
このプロパティ値を変更することで、オプティミスティック同時実行競合を検出
する方法を変更することができます。
このプロパティ値を変更する手順を示します。
のColumn属性のUpdateCheckプロパティの値が変更されます。
なお、このプロパティが有効なのは、どのメンバーも、IsVersion=true として指定されていない場合だけです。
いずれかのメンバーに、IsVersion=true が指定されている場合、UpdateCheckは無効となります。
UpdateCheckについてみていきましょう。
このプロパティは、LINQ to SQL がオプティミスティック同時実行競合を検出する方法を
取得または設定するものです。以下の3つの列挙値を利用できます。
- Always 競合の検出に常にこの列を使用します。
- Never 競合の検出にこの列を使用しません。
- WhenChanged メンバーがアプリケーションによって変更された場合のみ、この列を使用します。
このプロパティ値を変更することで、オプティミスティック同時実行競合を検出
する方法を変更することができます。
このプロパティ値を変更する手順を示します。
- ソリューションエクスプローラで、Northwind.dbmlをダブルクリックし、O/Rデザイナー画面を開きます。
- エンティティクラスのプロパティ(以降メンバーと呼びます)をマウスでクリックします。
- プロパティウィンドウに、このメンバーに対応したプロパティが表示されます。
- [更新チェック]プロパティの値をAlways, Never, WhenChanged から選択します。
のColumn属性のUpdateCheckプロパティの値が変更されます。
なお、このプロパティが有効なのは、どのメンバーも、IsVersion=true として指定されていない場合だけです。
いずれかのメンバーに、IsVersion=true が指定されている場合、UpdateCheckは無効となります。
2011年01月10日
LINQ to SQL番外編 - timestamp列とUpdateCheckプロパティ
中断していた「LINQ to SQL番外編」を再開します。
LINQ to SQLでは、楽観的同時実行制御の仕組みが組み込まれています。
たとえば、Customers テーブルの行を更新する場合、LINQ to SQLは
以下のような UPDATE文を発行します。
パラメータ p0, p1, p2... には、変更前の値が渡されます。
このSQLは、他のユーザによって変更されていない場合にだけ、このUPDATE文
が成功することになりますので、他のユーザによる変更有無を把握できるわけです。
LINQ to SQLでは各カラムに規定値として、
UpdateCheck = UpdateCheck.Always
が設定されています。これにより上記SQLのWHERE句で変更チェックが行われることになります。
しかし、すべてのカラムで変更有無のチェックすることは、パフォーマンスに影響することもあります。
なのでもっと単純な方法でこの制御を行いたい場合もあります。
そのような場合で一般的なのは、Timestamp型のカラムをテーブルに追加することです。
Timestamp型がテーブルにあると、O/Rデザイナーはカラム属性のIsVersionプロパティに
Trueが設定されます。これにより、競合の発生の判断に
Timestamp型のカラムのみが利用されることになります。
O/Rデザイナーが付加する属性を以下に示します。(TimeStampという名前のカラムにしています)
また、
この場合のUPDATE文は、
となり、主キーとTimeStampカラムだけが、チェックされる単純なSQL文となります。
最近では、GUIDをカラムに追加して、同様のことを行うこともあるようですが、
LINQ to SQLがサポートしているかどうかまでは、調べきっていません。
ご存知の方がいましたら、コメントいただけると嬉しいです。
LINQ to SQLでは、楽観的同時実行制御の仕組みが組み込まれています。
たとえば、Customers テーブルの行を更新する場合、LINQ to SQLは
以下のような UPDATE文を発行します。
パラメータ p0, p1, p2... には、変更前の値が渡されます。
このSQLは、他のユーザによって変更されていない場合にだけ、このUPDATE文
が成功することになりますので、他のユーザによる変更有無を把握できるわけです。
LINQ to SQLでは各カラムに規定値として、
UpdateCheck = UpdateCheck.Always
が設定されています。これにより上記SQLのWHERE句で変更チェックが行われることになります。
しかし、すべてのカラムで変更有無のチェックすることは、パフォーマンスに影響することもあります。
なのでもっと単純な方法でこの制御を行いたい場合もあります。
そのような場合で一般的なのは、Timestamp型のカラムをテーブルに追加することです。
Timestamp型がテーブルにあると、O/Rデザイナーはカラム属性のIsVersionプロパティに
Trueが設定されます。これにより、競合の発生の判断に
Timestamp型のカラムのみが利用されることになります。
O/Rデザイナーが付加する属性を以下に示します。(TimeStampという名前のカラムにしています)
また、
この場合のUPDATE文は、
となり、主キーとTimeStampカラムだけが、チェックされる単純なSQL文となります。
最近では、GUIDをカラムに追加して、同様のことを行うこともあるようですが、
LINQ to SQLがサポートしているかどうかまでは、調べきっていません。
ご存知の方がいましたら、コメントいただけると嬉しいです。
2010年12月09日
LINQ to SQL番外編 - NULL許容の列を扱う
NorthwindデータベースのEmployeesテーブルの ReportsTo列は、NULLを許可する
型として定義されています。
そのため、「LINQ to SQLエンティティクラス」の Employee.ReprotsToプロパティも、
と、Northwind.designer.csに書かれています。
System.Nullableは、int? と同じ意味です。
これがわかれば、クエリ式を書くのもそう難しいことではありません。
ReportsToがNULLのものだけ取得したいのなら、
と書けばOKです。
では、Customersテーブルの Fax列(NULLを許可しています)は、どうでしょうか?。
Fax列は、「LINQ to SQLエンティティクラス」のCustomer.Faxでは、C#のString 型にマッピングされています。
System.Nullable とはなっていません。
string型は参照型で、もともとnullを保持できますから、そのままでOKということです。
というか、文法上、System.Nullable とすることはできません。
ですから、
と書けば、Fax番号が登録されていない顧客だけを抜き出すことができます。
そのため、「LINQ to SQLエンティティクラス」の Employee.ReprotsToプロパティも、
と、Northwind.designer.csに書かれています。
System.Nullable
これがわかれば、クエリ式を書くのもそう難しいことではありません。
ReportsToがNULLのものだけ取得したいのなら、
と書けばOKです。
では、Customersテーブルの Fax列(NULLを許可しています)は、どうでしょうか?。
Fax列は、「LINQ to SQLエンティティクラス」のCustomer.Faxでは、C#のString 型にマッピングされています。
System.Nullable
string型は参照型で、もともとnullを保持できますから、そのままでOKということです。
というか、文法上、System.Nullable
ですから、
と書けば、Fax番号が登録されていない顧客だけを抜き出すことができます。
2010年11月28日
LINQ to SQL 番外編 - IDENTITY列の扱い
IDENTITY列の扱い
テーブルに行を挿入した際に、自動で番号を振りたい場合に、列にIDENTITYを指定すると便利ですが、
挿入直後にこの値を取得したい場合に、LINQ to SQLはとても便利な機能を備えています。
主キーにIDENTITYを指定したカラムについて、O/Rデザイナーが作成する「LINQ to SQLエンティティクラス」の
プロパティがどうなっているのかを見てみましょう。
NortwindデータベースのCategoryテーブルのCategoryIDカラムがこれに該当します。
Northwind.designer.csを開いて、CategoryクラスのCategoryIDプロパティを見て ください。
以下のような記述があるのを確認できると思います。
注目すべきは、
の部分です。この指定があると、行が挿入されると自動で、CategoryIDカラムを 取得する select文が発行されます。
そのため、
というコードを実行すれば、SubmitChanges()後の、c1.CategoryIDには、データ ベースで
生成された値を参照することができます。
わずらわしいコーディングから解放されるのは、とても嬉しいですね。
ただ、2つのSQL文がリクエストされるのは避けたいという場合には、 ストアドプロシージャを利用することも検討してください。
挿入直後にこの値を取得したい場合に、LINQ to SQLはとても便利な機能を備えています。
主キーにIDENTITYを指定したカラムについて、O/Rデザイナーが作成する「LINQ to SQLエンティティクラス」の
プロパティがどうなっているのかを見てみましょう。
NortwindデータベースのCategoryテーブルのCategoryIDカラムがこれに該当します。
Northwind.designer.csを開いて、CategoryクラスのCategoryIDプロパティを見て ください。
以下のような記述があるのを確認できると思います。
注目すべきは、
の部分です。この指定があると、行が挿入されると自動で、CategoryIDカラムを 取得する select文が発行されます。
そのため、
というコードを実行すれば、SubmitChanges()後の、c1.CategoryIDには、データ ベースで
生成された値を参照することができます。
わずらわしいコーディングから解放されるのは、とても嬉しいですね。
ただ、2つのSQL文がリクエストされるのは避けたいという場合には、 ストアドプロシージャを利用することも検討してください。
2010年11月25日
LINQ to SQL 番外編 - データベースの作成
DataContext の CreateDatabase() メソッドを呼び出すことで、データベースの
新しいインスタンスを作成することが可能です。
「LINQ to SQLエンティティクラス」には、データベースのテーブルやカラムに関する
属性があるので、この情報を利用して、データベースのインスタンスを作成します。
「LINQ to SQLエンティティクラス」の定義
ここでは、方法 : データベースを動的に作成する (LINQ to SQL)に記載されている「LINQ to SQLエンティティクラス」を拡張し、
リレーショナル構造をもった「LINQ to SQLエンティティクラス」を定義してみました。
上記記事の例との意違いは、
・Categoriesテーブルを追加
・DVDテーブルに、CategoryIDカラムを追加
・DVDテーブルと、Categoriesテーブルを関連づけ
です。
DataContextの定義
DataContextを継承し、カスタムのDataContextクラスを定義します。DataContext派生クラスには、
作成する上記テーブルをメンバーとして宣言します。
データベースを作成するコード
以下、データベースを作成するコードです。
この例では、DatabaseExists()で、データベースの存在を確認し、存在していた場合は、
DeleteDatabase()で、データベースを削除後、CreateDatabase()でデータベースを
作成するようにしています。
新しいインスタンスを作成することが可能です。
「LINQ to SQLエンティティクラス」には、データベースのテーブルやカラムに関する
属性があるので、この情報を利用して、データベースのインスタンスを作成します。
「LINQ to SQLエンティティクラス」の定義
ここでは、方法 : データベースを動的に作成する (LINQ to SQL)に記載されている「LINQ to SQLエンティティクラス」を拡張し、
リレーショナル構造をもった「LINQ to SQLエンティティクラス」を定義してみました。
上記記事の例との意違いは、
・Categoriesテーブルを追加
・DVDテーブルに、CategoryIDカラムを追加
・DVDテーブルと、Categoriesテーブルを関連づけ
です。
DataContextの定義
DataContextを継承し、カスタムのDataContextクラスを定義します。DataContext派生クラスには、
作成する上記テーブルをメンバーとして宣言します。
データベースを作成するコード
以下、データベースを作成するコードです。
この例では、DatabaseExists()で、データベースの存在を確認し、存在していた場合は、
DeleteDatabase()で、データベースを削除後、CreateDatabase()でデータベースを
作成するようにしています。
2010年11月21日
LINQ to SQL 番外編 - 読み込み専用でデータベースを利用する
DataContextは、エンティティオブジェクトの変更を追跡していますが、データベースから
データを読み込むだけで、更新を行う必要がないシナリオが多く存在します。
そのようなときには、DataContextに、エンティティへの変更を追跡しないように指示することで、
パフォーマンスを向上させることができます。
その指示は、ObjectTrackingEnabledプロパティを使います。
これだけです。
※ 変数 db は、DataContext型のオブジェクトを表します。
もちろん、この状態で、SubmitChanges()することは出来ません。呼び出した時点で例外が発生します。
データを読み込むだけで、更新を行う必要がないシナリオが多く存在します。
そのようなときには、DataContextに、エンティティへの変更を追跡しないように指示することで、
パフォーマンスを向上させることができます。
その指示は、ObjectTrackingEnabledプロパティを使います。
これだけです。
※ 変数 db は、DataContext型のオブジェクトを表します。
もちろん、この状態で、SubmitChanges()することは出来ません。呼び出した時点で例外が発生します。
2010年11月18日
LINQ to SQL 番外編 - DataContext.OnCreateって何?
NorthwindDataContextのコンストラクタでは、OnCreateというメソッドを呼び出しています。
これってなんでしょうか?
Northwind.designer.csのNorthwindDataContextクラスの定義を見てみると、
とパーシャルメソッドとなっています。
つまり、NorthwindDataContextのパーシャルクラスに、OnCreated()メソッドを定義すれば、
このメソッドが、NorthwindDataContextインスタンス生成時に呼び出されるということです。
試しに、OnCreatedメソッドを実装してみましょう。
プロジェクトに、クラスの追加で、NorthwindDataContext.csというファイルを新規作成し、
そこに、以下のように記述します。
これで、再度ビルドし実行すれば、
が実行された時点で、
とコンソールに出力されることがわかると思います。
この機能を使うことで、ログ出力などのカスタムコードを挿入することが可能になります。
その他のパーシャルメソッドについても、カスタムコードを挿入する際に利用できるものだと
思いますが、調べきれていません。
もし、気が向いたら調べたいと思います。
これってなんでしょうか?
Northwind.designer.csのNorthwindDataContextクラスの定義を見てみると、
とパーシャルメソッドとなっています。
つまり、NorthwindDataContextのパーシャルクラスに、OnCreated()メソッドを定義すれば、
このメソッドが、NorthwindDataContextインスタンス生成時に呼び出されるということです。
試しに、OnCreatedメソッドを実装してみましょう。
プロジェクトに、クラスの追加で、NorthwindDataContext.csというファイルを新規作成し、
そこに、以下のように記述します。
これで、再度ビルドし実行すれば、
が実行された時点で、
NorthwindDataContext Created
とコンソールに出力されることがわかると思います。
この機能を使うことで、ログ出力などのカスタムコードを挿入することが可能になります。
その他のパーシャルメソッドについても、カスタムコードを挿入する際に利用できるものだと
思いますが、調べきれていません。
もし、気が向いたら調べたいと思います。
2010年11月16日
LINQ to SQL 番外編 - 接続文字列
DataContext は、データベースからオブジェクトを取得したり、変更を再送信したりする際に使用するクラスで、LINQ to SQLの最も中心となるクラスです。
Visual Studio のO/Rデザイナー画面で、「LINQ to SQLエンティティクラス」を作成すると、必ず、DataContextから派生したカスタムクラスのコードが作成されます。
DBにアクセスするには、次のように、派生したDataContextクラスのインスタンスを生成し、データベースにアクセスする準備をします。
Northwind.designer.csのソースコードの中の、NorthwindDataContextクラスを見てみると、そのコンストラクタに、
※都合により途中で改行しています。
と書いてあります。
ADO.NET2.0と同様に、Settings.settingsに設定した接続文字列を使い、データベースに接続しているのが分かります。
通常は、App.Configの connectionStringsで指定した接続文字列が使われますが、
このApp.Configが無い場合には、ソースに埋め込まれた接続文字列が使われます。
なんかややこしい作りです。
第2引数のmappingSourceについては、僕自身まだ使ったことがなく、よく分かっていませんm(_ _)m
なお、
と、デフォルトの接続文字列以外を利用することもできます。
これは、ADO.NET2.0より、使いやすくなっていますね。
Visual Studio のO/Rデザイナー画面で、「LINQ to SQLエンティティクラス」を作成すると、必ず、DataContextから派生したカスタムクラスのコードが作成されます。
DBにアクセスするには、次のように、派生したDataContextクラスのインスタンスを生成し、データベースにアクセスする準備をします。
Northwind.designer.csのソースコードの中の、NorthwindDataContextクラスを見てみると、そのコンストラクタに、
※都合により途中で改行しています。
と書いてあります。
ADO.NET2.0と同様に、Settings.settingsに設定した接続文字列を使い、データベースに接続しているのが分かります。
通常は、App.Configの connectionStringsで指定した接続文字列が使われますが、
このApp.Configが無い場合には、ソースに埋め込まれた接続文字列が使われます。
なんかややこしい作りです。
第2引数のmappingSourceについては、僕自身まだ使ったことがなく、よく分かっていませんm(_ _)m
なお、
と、デフォルトの接続文字列以外を利用することもできます。
これは、ADO.NET2.0より、使いやすくなっていますね。
2010年11月14日
LINQ to SQL 番外編 - DataContextのスコープ
DataContextのインスタンス生成にはそれほどコストはかかりませんので、
論理的な処理単位に対して生成、破棄をすることになります。
通常は、メソッドのスコープで生成、破棄することが多いかと思います。
この一連の記事(メルマガとこの番外編の記事)では、
以下のようにその都度NorthwindDataContextを生成するようになっています。
これは、コードをコピー&ペーストした際に、そのまま動くよう配慮したということもありますが、
実際のコードにおいても、小さな処理単位で、DataContext を使うことが多いと予想されるから
という意味もあります。
論理的な処理単位に対して生成、破棄をすることになります。
通常は、メソッドのスコープで生成、破棄することが多いかと思います。
この一連の記事(メルマガとこの番外編の記事)では、
以下のようにその都度NorthwindDataContextを生成するようになっています。
これは、コードをコピー&ペーストした際に、そのまま動くよう配慮したということもありますが、
実際のコードにおいても、小さな処理単位で、DataContext を使うことが多いと予想されるから
という意味もあります。
2010年11月08日
LINQ to SQL 番外編 - コンパイル済みクエリ
パラメータだけが異なる同じクエリを何回も実行することはよくありますが、その
ような場合、
クエリをコンパイルしてから、そのクエリを実行することでパフォー マンスを向上させることができます。
まず、以下のような、CompiledQuery.Compileを使い、デリゲート型のフィールド を定義します。
Compile メソッドは、入力パラメータを変更するだけで、
その後、何度も実行で きるキャッシュ可能なデリゲート型を返します。
この例では、nameがクエリのパラメータになります。
ProductByNameにはコンパイル済みクエリが格納されます。このコンパイル済みク エリを使うには、
のように書きます。
ここでは、パラメータは、nameひとつだけでしたが、もちろん、複数のパラメータ を渡すこともできます。
以下のようにラムダ式と var を使って書くこともできます。
デリゲートをいちいち定義するのも面倒なので、この書き方が良いかなと思ったり しましたが、
実際には、このコンパイル済みクエリは、何度も再利用することで効 果が得られますので、
上のようにローカル変数にして使うことは、あまり無いかなと思 います。
クエリをコンパイルしてから、そのクエリを実行することでパフォー マンスを向上させることができます。
まず、以下のような、CompiledQuery.Compileを使い、デリゲート型のフィールド を定義します。
Compile メソッドは、入力パラメータを変更するだけで、
その後、何度も実行で きるキャッシュ可能なデリゲート型を返します。
この例では、nameがクエリのパラメータになります。
ProductByNameにはコンパイル済みクエリが格納されます。このコンパイル済みク エリを使うには、
のように書きます。
ここでは、パラメータは、nameひとつだけでしたが、もちろん、複数のパラメータ を渡すこともできます。
以下のようにラムダ式と var を使って書くこともできます。
デリゲートをいちいち定義するのも面倒なので、この書き方が良いかなと思ったり しましたが、
実際には、このコンパイル済みクエリは、何度も再利用することで効 果が得られますので、
上のようにローカル変数にして使うことは、あまり無いかなと思 います。
2010年11月07日
LINQ to SQL 番外編 - はじめに
メールマガジン『C#プログラミングレッスン』で 今年の6月から「LINQ to SQL編」
を連載していますが、LINQ to SQLの全容を解説するには、あまりにも長期にわた
ってしまうため、一部の内容をメールマガジンに書くのを断念しました。
そのため、メールマガジンに書けなかった内容をこのブログで
紹介してゆくことにします。
なお、ここで書く内容は、メールマガジン『C#プログラミングレッスン』で説明した
内容を理解している事を前提にしています。
併せて、メールマガジン『C#プログラミングレッスン - Linq to SQL編』も読んで
いただけると、より理解が深まると思います。
バックナンバーは、後日、メールマガジン『C#プログラミングレッスン』書庫のページから
ダウンロードできるようにする予定ですが、
それまでは、「ここ」とか「ここ」を見てください。
では、次の記事から、「LINQ to SQL 番外編」の始まります。
を連載していますが、LINQ to SQLの全容を解説するには、あまりにも長期にわた
ってしまうため、一部の内容をメールマガジンに書くのを断念しました。
そのため、メールマガジンに書けなかった内容をこのブログで
紹介してゆくことにします。
なお、ここで書く内容は、メールマガジン『C#プログラミングレッスン』で説明した
内容を理解している事を前提にしています。
併せて、メールマガジン『C#プログラミングレッスン - Linq to SQL編』も読んで
いただけると、より理解が深まると思います。
バックナンバーは、後日、メールマガジン『C#プログラミングレッスン』書庫のページから
ダウンロードできるようにする予定ですが、
それまでは、「ここ」とか「ここ」を見てください。
では、次の記事から、「LINQ to SQL 番外編」の始まります。



