【C#】LINQ WHERE 動的 | demicadeのブログ

demicadeのブログ

iPhoneアプリ開発素人のメモ的なブログです。

書くときは書くdemicadeです。

 

今回はLINQです。

List<MyClass>などの時は問題ないけど、List<T>の時のWhere句って困りますよね。

 

実際困りました。焦

そんな時にWhere句を動的に作る方法です。

 

http://www.atmarkit.co.jp/fdotnet/joyofprogram/20080422linqtoxml02/linqtoxml02_02.html

 

// 検索する
private void Search()
{
  // 全レストランを取得する
  IEnumerable<Restaurant> result = GetRestaurants();

  // Idを取得する(未入力は0とする)
  int id = string.IsNullOrEmpty(this.textBoxId.Text)
                ? 0 : int.Parse(this.textBoxId.Text);

  //Idと名前のOR条件で検索する
  result = result.Where(Predicate.GetPredicate(id, this.textBoxName.Text));

  // 検索結果をリストに設定
  this.listView1.ItemsSource = result;
}


public class Predicate
{
  // 動的にId or 名前条件用ラムダ式を生成する
  // r => r.Id == id || r => r.Name.Contains(name)という式を動的に作る
  public static Func<Restaurant, bool> GetPredicate(int id, string name)
  {  
    // パラメータの定義する:r
    ParameterExpression param =
      Expression.Parameter(typeof(Restaurant), "r");

    // Idのbodyの左を定義する:r.Id
    MemberExpression left = Expression.Property(param, "Id");

    // Idのbodyの右を定義する:id
    ConstantExpression right =
      Expression.Constant(id, typeof(int));

    // Idのbodyを定義する:r.Id == id
    BinaryExpression body = Expression.Equal(left, right);

    // Nameの属性を定義する:r.Name
    MemberExpression nameProperty = Expression.Property(param, "Name");

    // Name定数を定義する:name
    ConstantExpression nameConstant = Expression.Constant(name, typeof(string));

    // Nameのbodyを定義する:r.Name.Contains(name)
    MethodCallExpression nameBody = Expression.Call(nameProperty,
        typeof(string).GetMethod("Contains"), nameConstant);

    // 全体のbodyを定義する
    // r => r.Id == id || r => r.Name.Contains(name)
    BinaryExpression body = Expression.Or(idBody, nameBody);

    // 式ツリーを(r => r.Id == id || r => r.Name.Contains(name))を組み立て、
    // 実行コードにコンパイルする
    return Expression.Lambda<Func<Restaurant, bool>>(
      body, param).Compile();
  }
}

 

IQueryableにキャストしないと使えないと思います。

result = result.Where(Predicate.GetPredicate(id, this.textBoxName.Text));

ではなくて、

IQueryableにキャストして、

var queryableList = result.IQueryable();

result = queryableList .Where(Predicate.GetPredicate(id, this.textBoxName.Text)).ToList();

な感じです。

 

実際使うときは拡張メソッドにしちゃうのが便利かなって思ってます。

 

先に考えてくれていた人に感謝。ありがたや。