LoginSignup
6
11

More than 5 years have passed since last update.

動的にLINQのWhereを生成する [C#]

Last updated at Posted at 2018-12-26

プロジェクトでの開発時に、動的にLINQのWhereを生成できたら、もっとコードがスマートに書けるのに、、、と思いつつもプロジェクト中には時間がなく出来ませんでした。
そのため、次の機会に向けて対応できるようになりたいと思い、勉強した時のメモです。
以下のbeforeのように書いていたコードをafterのようなイメージに書けるようにすることを目標にしました。
Where条件のプロパティ名とキーワードを文字列で指定することが目標です。

before


var query = StudentList.AsQueryable().Where(null != Student.Name && Student.Name.Contains("A"));

after(※イメージです)

var query = StudentList.AsQueryable().Where("Name","A");

Expressionオブジェクトを利用すると動的に、式を組み立てられるということが分かりました。
そのため、まずWhereの引数に設定するラムダ式を勉強し、その後、ラムダ式を動的に組み立てる、Expressionについて勉強しました。
勉強の際には以下のサイトを参考にさせていただきました。
https://ufcpp.net/study/csharp/sp3_linq.html
https://ufcpp.net/study/csharp/sp3_expression.html

上記を勉強した後、以下のサイトを参考にafterになるように書いてみました。
ラムダ式、Expressionを勉強する前に以下のサイト見ても、あまり理解できず、自分の実施したい処理を書けるまでにはいたらなかったのですが、勉強後に見ると理解が進みました。

http://www.atmarkit.co.jp/fdotnet/dotnettips/986dynamiclinq/dynamiclinq.html
https://stackoverflow.com/questions/3703386/iqueryable-extension-create-lambda-expression-for-querying-a-column-for-a-keywo

勉強の結果、作成したコードが以下になります。Extensionsを使用すればもっときれいに書けると思いますが、現状はここまでとしています。

    class Program
    {
        private static List<Student> StudentList;

        static void Main(string[] args)
        {
            SetStudentsData();
            var query1 = StudentList.AsQueryable().Where(GetWhereContainsExpression("Name","A"));

            Console.WriteLine("Studentのフィルター結果(Name)");

            foreach (var item in query1)
            {
                Console.WriteLine(item.Name.ToString());
            }

            var query2 = query1.AsQueryable().Where(GetWhereContainsExpression("Floor", "B")); ;

            Console.WriteLine("Studentのフィルター結果(Floor)");

            foreach (var item in query2)
            {
                Console.WriteLine("Name:{0},Floor:{1}",item.Name.ToString(),item.Floor.ToString());
            }

            Console.ReadLine();
        }

        public static Expression<Func<Student, bool>> GetWhereContainsExpression(string columnName, string keyword)
        {
            var type = typeof(Student);
            var property = type.GetProperty(columnName);
            var parameter = Expression.Parameter(type, "p");

            // 文字列を指定してメソッドの情報を取得する
            MethodInfo Contains = typeof(string).GetMethod("Contains");

            //例)p.Nameの形式を作成
            var propertyAccess = Expression.MakeMemberAccess(parameter, property);

            //例)null != p.Nameの形式を作成
            var body0 = Expression.NotEqual(Expression.Constant(null), propertyAccess);

            //p.Name.Contains(keyword)の形式を作成
            var body1 = Expression.Call(propertyAccess,
                 Contains, Expression.Constant(keyword));

            //例)null != p.Name && p.Name.Contains(keyword)の形式を作成
            var newBody = Expression.AndAlso(body0, body1);

            //例)p => null != p.Name && p.Name.Contains(keyword)の形式を作成
            return Expression.Lambda<Func<Student, bool>>(newBody, parameter);

        }

    }
    public class Student
    {
        public string Name { get; set; }
        public string Number { get; set; }
        public string Course { get; set; }
        public string Floor { get; set; }

        public Student(string name,string number,string course,string floor)
        {
            this.Name = name;
            this.Number = number;
            this.Course = course;
            this.Floor = floor;
        }
    }
6
11
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
11