式木がどんな風に役に立っているか、みんな大好き LINQ で見ていきたいと思います。
普段 LINQ はモリモリ使っていますが、その中身はボンヤリとしか理解していなかったのでおさらいしてみました。
LINQと式木の概要
例として LINQ to SQL を挙げてみていきます。LINQ to SQL で以下のようなクエリ式を書いた場合、
IQueryable<Student> query = from student in db.Student where student.Gender == "Male" select student ;
db.Student を foreach して Genderプロパティ が "Male" の項目を探す... ではなく。
ご存知の通り実際にはこんな感じのSQL文が生成され、ADO.NET によって実行されます。
SELECT [t0].[Id], [t0].[Name], [t0].[Gender] FROM [dbo].[Students] AS [t0] WHERE [t0].[Gender] = @p0 -- @p0 = 'Male'
このSQL文を作る過程に式木が利用されています。まず、上記のクエリ式は実際にはシンタックスシュガーで、以下の様なメソッド式に変換されます。
db.Student.Where(student => student.Gender == "Male");
さらにWhereメソッドは拡張メソッドなので、こうなりますね。
Where(db.Student, student => student.Gender == "Male");
この式がこんな感じの式木として扱われます。
同等の Expression を組み立てるとこんな感じです。
var whereMethod = typeof (Queryable) .GetMethods() .First(m => m.Name == "Where") .MakeGenericMethod(new Type[] { typeof(Student) }); var paramStudent = Expression.Parameter(typeof (Student), "student"); var expression = Expression.Call(whereMethod, Expression.Constant(db.Student), Expression.Lambda<Func<Student, bool>>( Expression.Equal( Expression.Property(paramStudent, "Gender"), Expression.Constant("Male") ), paramStudent ) ) ;
LINQ to SQL では結果を取得する際、まず式木を評価・解析することで適切なSQL文を作成してくれています。
LINQ to XXXXX
式自体(式木)を評価することで、LINQ to SQL や LINQ to Entities では式を SQL に変換しますが、LINQ to XML, LINQ to DataSet, LINQ to NHibernate ... などでは式を各種データソースに適切なアクセス方法に変換しています。
このような仕組みのおかげで統一的な式で、各種データソースへのアクセス、操作をうまく統合しています。
これが LINQ (統合言語クエリ) と呼ばれるゆえんであり、式木を最も活用している例でもあります。
応用してみる(?)
例えば LINQ to SQL では一括の UPDATE や DELETE ができません。例えば先程の例で 性別(Gender) が 男(Male) の生徒を全て削除しようと思うと以下のようになります。
var query = from student in db.Student where student.Gender == "Male" select student ; db.Student.DeleteAllOnSubmit(query); db.SubmitChanges();
この操作は以下の様な SQL を発行します。まず一度 Gender = 'Male' な生徒を全て取得してから...
SELECT [t0].[Id], [t0].[Name], [t0].[Gender] FROM [dbo].[Students] AS [t0] WHERE [t0].[Gender] = @p0 -- @p0 = 'Male'
それぞれ1つずつ DELETE句 を発行していきます。
DELETE FROM [dbo].[Students] WHERE [Id] = @p0 -- @p0 = 1 DELETE FROM [dbo].[Students] WHERE [Id] = @p0 -- @p0 = 2 DELETE FROM [dbo].[Students] WHERE [Id] = @p0 -- @p0 = 3 ...
これはあまりスマートとはいえないやり方ですね。本来以下のような SQL を発行すべきです。
DELETE FROM [dbo].[Students] WHERE [Gender] = @p0 -- @p0 = 'Male'
このように LINQ to SQL は更新や削除の操作がちょっと弱いです。しかしこの問題に対しては、自前の拡張メソッドを作って上記のような適切な SQL を発行するように実装することで対処できます。
以下の記事が大変参考になりますので、是非ご覧になってみて下さい。
もうちょっと具体的には
今回ご紹介した LINQ の仕組みは、IQueryableインターフェイス 及び IQueryProviderインターフェイス の組み合わせで実現されています。
以下の記事が大変わかり易く参考になりますので、紹介させて頂きます。
IQueryable と IQueryProvider の仕組みが把握できると、独自の LINQ to XXXXX を作ることができて夢が広がりますね。
さいごに
LINQ初心者の方や、式木がよくわからないぜ... という方の参考になれば幸いです。
※ 余談ですが http://ufcpp.net/ さんの C# の情報の充実度はハンパじゃないですね... C# について何か調べると必ずといっていいほど検索で挙がってきます。MSDNよりわかりやすい事が多く、いつも参考にさせて頂いています。