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

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

## before
```c#

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

## after（※イメージです）
```c#
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を使用すればもっときれいに書けると思いますが、現状はここまでとしています。

```c#
    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);

        }

    }
```

```c#
    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;
        }
    }
```