ScottGu's blog translated by Chica @ Wankuma

LINQ to SQL (パート 8- カスタムSQL文を実行)

  

数週間に渡り、LINQ to SQLをカバーしたブログ投稿シリーズを書いてきました。LINQ to SQLはビルドインのO/RM(オブジェクトリレーショナルマッパー)のことで、.NET Framework 3.5リリースで出荷されます。これにより.NETクラスを使用してリレーショナルデータベースをモデル化することができます。LINQ文を使用してデータベースを検索し、データの更新、挿入、削除を行うことができます。

以下はこのシリーズの最初の7つのパートです。

最後の2つのパート (パート 6 and パート 7)では、LINQ to SQLのデータモデルを使用してどのようにデータベースのストアドプロシージャ(SPROC)をオプションとして使用してデータの検索、挿入、更新、および削除を行うことができるかについてご紹介しました。

これらの投稿を始めてから何人かの方々から頂いた質問の1つが"もしLINQ to SQLを使用してSQL文を完全に管理したいのだが、ストアドプロシージャにそれを行って欲しくない場合は?"というものでした。本日のブログ投稿はこれをカバーして、どのように(LINQ to SQLデータモデルクラスに格納するため、および挿入、更新、削除を実行するための)カスタムのSQL文を使用することができるかについてお話したいと思います。

LINQ to SQLとLINQのクエリ文を使用

このブログ投稿の目的のためですが、Northwindデータベースに対して以下のようなデータモデルクラス一式を定義するためにVS 2008でLINQ to SQL ORMデザイナを使用していると仮定しましょう。(注: このシリーズのパート 2を読んで頂ければ、これを行うためのLINQ to SQL ORMデザイナの使用方法をご確認頂けます。):

このブログのパート 3では、上記のデータモデルクラスの検索やデータベースで行や列を表すオブジェクト一式を取得するために、VBとC#で新しいLINQ言語サポートをどのように使用することができるかについてカバーしています。

例えば、(データベースからProductオブジェクトを得るためにLINQクエリを使用する)"GetProductsByCategory"ヘルパーメソッドをデータモデルのデータコンテキストクラスに追加することができます。

VB:

C#:

一度カプセル化されたLINQヘルパーメソッドを定義すると、以下の様なコード(製品を取得して結果をループさせるのに使用)を書くことができます。

VB:

 

"GetProductsByCategory"メソッド内のLINQ文が評価された時、LINQ to SQL ORMは自動的に動的SQLを実行してProductデータを取得しProductオブジェクトに格納します。LINQ to SQL デバッグビジュアライザを使用してどのようにこのLINQ文が最終的に評価されているのかをデバッガでみることができます。

LINQ to SQLとカスタムSQLクエリを使用

上記のサンプルではデータベースの検索や強く型付けされたProductオブジェクトを取得するためのSQLコードを書く必要はありませんでした。その代りに、LINQ to SQL ORMが自動的にLINQ文をSQLに翻訳し、それをデータベースに対して評価していました。

しかしデータベースに対して走っているSQLをLINQ to SQLではなく自分で完全に制御したい場合はどうすれば? これを達成する1つの方法が、このシリーズのパート 6 および パート 7で述べたようにストアドプロシージャを使用することです。他の方法はデータコンテキストベースクラス上の"ExecuteQuery"ヘルパーメソッドを使用し、提供しているカスタムSQL文を使用することです。

ExecuteQuery メソッドの使用

ExecuteQueryメソッドはSQLクエリ文を引数として取り、そのクエリにオプションとして値を代替えするのに使用する一式のパラメータも持っています。それを使用すると、データベースで使用したい生のSQLを全て実行することができます。(複数のテーブルに渡るのカスタムJOINを含みます。)

ExecuteQueryメソッドが非常に有用な点はSQL文の返り値の型を特定することができるところです。これは、型オブジェクトをメソッドにパラメータとして渡すか、メソッドのジェネリックベースバージョンを使用することで行うことができます。

例えば、LINQ文を使って、代わりにExecuteQueryメソッドを使用して生SQL文をデータベースに対して実行し"Product"オブジェクトを結果として返すように、先に作成したGetProductsByCategory() ヘルパーメソッドを変更することができます。

VB:

C#:

そうすると、前と全く同じコードを使用してGetProductsByCategory() ヘルパーメソッドを呼び出すことができるようになります:

しかし前とは違って、データベースに対して走っているのはカスタムのSQL文であり、LINQクエリ文の使用により実行された動的SQLではありません。

カスタムSQL文と更新のオブジェクトトラッキング

デフォルトではLINQ to SQLを使用してデータモデルオブジェクトを取得した時、全て行われた変更や更新をトラッキングします。もし"SubmitChanges()"メソッドをデータコンテキストクラス上で呼び出した場合、処理的に全ての更新をデータベースに保存します。これについて詳しくこのLINQ to SQLシリーズのパート 4でカバーしています。

ExecuteQuery()メソッドのかっこいい機能の1つはこのオブジェクトトラッキングと更新モデルに完全に参加することができるところです。例えば、以下のようなコードを書いて、全ての製品をある特定のカテゴリから取得し、それらの価格を10%割り引くことができます。:

これはGetProductsByCategory メソッドでExecuteQueryを呼び出したときの返り値を"Product"型に型付けしたためで、LINQ to SQLはそこから返されたProductオブジェクトをトラッキングすることを知っているのです。コンテキストオブジェクト上で"SubmitChanges()"を呼び出した時データベースにそれらは保存されます。

カスタムクラスとカスタムSQL文

ExecuteQuery()メソッドではSQLクエリの返り値の型としてどのクラスでも指定することができます。そのクラスはLINQ to SQL ORMデザイナを使用して作成されたものでなければならない、またはカスタムのインターフェイス(そこへ普通の古いクラスを渡すことができる)を実行しなければならない ということはありません

例えば、以下のようなProductプロパティのサブセットを持った新しいProductSummaryクラスを定義することができます。(新しい C# 自動プロパティ 機能を使用していることがお分かりになると思います。):

そしてGetProductSummariesByCategory()ヘルパーメソッドをNorthwindDataContext上に作成することができます。それは、それをベースに結果を返します。どのように必要な製品の値のサブセットだけをSQL文が要求しているか以下でご確認頂けると思います。ExecuteQueryメソッドはその後自動的に返されたProductSummaryオブジェクト上にそれらの値を設定する処理を行います。:

そしてこのヘルパーメソッドを発生させてその結果を以下のコードを使用してループさせることができます。:

挿入・更新・削除のカスタムSQL文

カスタムSQL文を検索に使用する他、カスタムの挿入・更新・削除のロジックにも実行させることができます。

これは、データコンテキスト上のパーシャルクラスで変更したいエンティティに対して適切なパーシャルの挿入・更新・削除メソッドを作成することで達成することができます。その後データコンテキストベースクラス上でExecuteCommand メソッドを使用して実行したいSQLを書くことができます。例えば、Productクラスの削除の動作をオーバーライドするには、このDeleteProductパーシャルメソッドを以下のように定義することができます。:

これで特定のProductインスタンスをデータベースから削除するために以下のコードを書いた場合は、LINQ to SQLはDeleteProductメソッドを呼びだします。これは、通常LINQ to SQLが使用するデフォルトの動的SQLを実行する代わりにカスタムSQLを発生させます。

まとめ

LINQ to SQL ORMはデータベースに対して検索、更新、挿入、削除を実行する動的SQLを自動的に生成し実行します。

実行されたSQLクエリやコマンドに対して完全に制御を行いたい場合などの上級なシナリオやケースでは、ストアドプロシージャまたはカスタムのSQL文のどちらを使用するかORMをカスタマイズすることもできます。これによりデータアクセス層の構築や拡張の際にかなりの柔軟性を持つことができます。

今後のこのシリーズのブログ投稿で次のものを含む残りのLINQ to SQLをカバーしたいと思います。: シングルテーブルの継承、遅延/Eager(最初から持ってくる)ローディング、オプティミスティック同時実行制御、複数層シナリオの処理。

Hope this helps,

Scott

ScottGu's blog translated by Chica @ Wankuma 

※本翻訳に関しまして、Scottさんにはご了承頂いております。