LINQを動的に作成する式ツリー
業務システムでデータベースを使うコードを組んでいると、データの取得条件を動的に変更したいことがよくある。そういうときにLINQで組んでいるとつらい。式ツリーを用いてLINQの条件を動的に変更できるすべを覚えておけば楽だ。
データベースを使うシステムを作っていると、データの一覧を表示するケースが当たり前のように出てくる。そして、選択したデータのみを表示したいケースも当然ながら出てくる。SQL Serverのクエリーを直接書くならIN句を使えばいいが、LINQ to EntitiesなどでLINQでデータを取得しているときに動的に条件を変えるのはややこしい。
ややこしいがやらなくてはいけない以上はやるしかない。式ツリーを使って条件を動的に生成すれば実現できる。今回は一覧データの中から特定のデータのみを取得するケースを想定して記述する。
ID | Name | Value | UpdateDateTime ---|------------|-------|------------------------ 0 | Name of 0 | 0 | 2011-06-12 11:13:19.493 1 | Name of 1 | 1 | 2011-06-12 11:13:19.497 2 | Name of 2 | 2 | 2011-06-12 11:13:19.497 3 | Name of 3 | 3 | 2011-06-12 11:13:19.497 4 | Name of 4 | 4 | 2011-06-12 11:13:19.497 5 | Name of 5 | 5 | 2011-06-12 11:13:19.497 6 | Name of 6 | 6 | 2011-06-12 11:13:19.497 7 | Name of 7 | 7 | 2011-06-12 11:13:19.497 8 | Name of 8 | 8 | 2011-06-12 11:13:19.497 9 | Name of 9 | 9 | 2011-06-12 11:13:19.497 10 | Name of 10 | 10 | 2011-06-12 11:13:19.497 11 | Name of 11 | 11 | 2011-06-12 11:13:19.497 12 | Name of 12 | 12 | 2011-06-12 11:13:19.497 13 | Name of 13 | 13 | 2011-06-12 11:13:19.497 14 | Name of 14 | 14 | 2011-06-12 11:13:19.497 15 | Name of 15 | 15 | 2011-06-12 11:13:19.497 16 | Name of 16 | 16 | 2011-06-12 11:13:19.500 17 | Name of 17 | 17 | 2011-06-12 11:13:19.500 18 | Name of 18 | 18 | 2011-06-12 11:13:19.500 19 | Name of 19 | 19 | 2011-06-12 11:13:19.500
コードが横に長くなりがちなので、コツとしては推論型varを活用して変数名をできるだけ短くすると見やすくなる。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace ExpressionTreesTest
{
class Program
{
static void Main(string[] args)
{
// 対象とする項目のID一覧を作成
var target = new List<int>() { 2, 4, 5, 7, 15 };
// LINQ作成(DB名はTestでEntityはウィザードで作成したまま)
var linq = from i in new TestEntities().Item select i;
var parameter = Expression.Parameter(typeof(Item), "i");
var property = Expression.Property(parameter, "ID");
Expression body = null;
target.ForEach(id =>
{
var constant = Expression.Constant(id, typeof(int));
body = body == null ? Expression.Equal(property, constant) :
Expression.OrElse(body, Expression.Equal(property, constant));
});
var result = linq.Where(Expression.Lambda<Func<Item, bool>>(body, parameter)).ToList();
// 画面に表示
result.ForEach(item => Console.WriteLine("ID: {0} Name: {1} Value: {2} Update: {3}",
item.ID, item.Name, item.Value, item.UpdateDateTime));
Console.Read();
}
}
}