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();
        }
    }
}

Follow me!

Feedlyで新着記事をチェックしよう!

Feedlyでフォローしておけば、新着記事をチェックすることができます。ぜひ、この機会にFeedlyに追加しておきましょう。