こんにちは!トミセンです。
C#で便利なlinqですが、
「lingでWhere句って動的に作れないの?」
情報が少なくて思ったより手間取ってしまうこともありますよね。
今回はそんなお悩みを解決します。
ずばり!「C# linqで動的なWhere句を生成する方法」についてご紹介します。
目次
C# linqで動的なWhere句を生成する方法
拡張メソッドを使って、動的なWhere句を生成していきます。
ソースコード
public static class EnumerableExtensions
{
/// <summary>
/// 動的Where句作成(複数)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="expressions">カラム名, 値, 型, "And"・"Or"</param>
/// <returns></returns>
public static IEnumerable<T> DynamicWhere<T>(this IEnumerable<T> source, List<Tuple<string, object, Type, LinqExpression>> expressions)
{
var queryableSource = source.AsQueryable();
// 条件作成
var lambda = GetPredicate<T>(expressions);
return queryableSource.Where(lambda);
}
/// <summary>
/// 動的Where句条件作成
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expressions"></param>
/// <returns></returns>
private static Func<T, bool> GetPredicate<T>(List<Tuple<string, object, Type, LinqExpression>> expressions)
{
// パラメータの定義する:x
ParameterExpression param = Expression.Parameter(typeof(T), "x");
// 全体のbody
BinaryExpression body = null;
int index = 0;
foreach (var exp in expressions)
{
// x => x.Id == id
// Idのbodyの左を定義する:x.Id
MemberExpression left = Expression.Property(param, exp.Item1);
// Idのbodyの右を定義する:id
ConstantExpression right = Expression.Constant(exp.Item2, exp.Item3);
// Idのbodyを定義する:x.Id == id
BinaryExpression bodyDetails = Expression.Equal(left, right);
if (exp.Item4 == LinqExpression.And)
{
body = (index == 0) ? bodyDetails : Expression.And(body, bodyDetails);
}
else
{
body = (index == 0) ? bodyDetails : Expression.Or(body, bodyDetails);
}
index++;
}
// 式ツリーを(x => x.Id == id || x => x.Name == name)を組み立て、
// 実行コードにコンパイルする
return Expression.Lambda<Func<T, bool>>(body, param).Compile();
}
}
/// <summary>
/// 条件式
/// </summary>
public enum LinqExpression : int
{
And = 1,
Or = 2,
}
使い方
それでは、使い方です。
条件を3回追加して絞っていきます。
public class Test
{
public void Main()
{
// テストデータ作成
var models = CreateModels();
Console.WriteLine($"元データ");
// 結果出力
WriteConsole(models);
// 【実行結果】
// 元データ
// Code:1111 PatternNo:10 Kind:333333
// Code:1111 PatternNo:10 Kind:333333
// Code:1111 PatternNo:10 Kind:444444
// Code:1111 PatternNo:10 Kind:444444
// Code:1111 PatternNo:66 Kind:444444
// Code:1111 PatternNo:66 Kind:444444
// Code:3333 PatternNo:10 Kind:444444
// Code:3333 PatternNo:10 Kind:333333
var exp = new List<Tuple<string, object, Type, LinqExpression>>();
Console.WriteLine($"");
Console.WriteLine($"① 追加条件:Code == 1111");
// 動的な検索条件作成
exp.Add(CreateWhere(nameof(Member.Code), 1111, typeof(int)));
// 動的な検索
var models2 = models.DynamicWhere(exp).ToList();
// 結果出力
WriteConsole(models2);
// 【実行結果】
// ① 追加条件:Code == 1111
// Code:1111 PatternNo:10 Kind:333333
// Code:1111 PatternNo:10 Kind:333333
// Code:1111 PatternNo:10 Kind:444444
// Code:1111 PatternNo:10 Kind:444444
// Code:1111 PatternNo:66 Kind:444444
// Code:1111 PatternNo:66 Kind:444444
Console.WriteLine($"");
Console.WriteLine($"② 追加条件:PatternNo == 10");
// 動的な検索条件作成
exp.Add(CreateWhere(nameof(Member.PatternNo), 10, typeof(int)));
// 動的な検索
models2 = models.DynamicWhere(exp).ToList();
// 結果出力
WriteConsole(models2);
// 【実行結果】
// ② 追加条件:PatternNo == 10
// Code:1111 PatternNo:10 Kind:333333
// Code:1111 PatternNo:10 Kind:333333
// Code:1111 PatternNo:10 Kind:444444
// Code:1111 PatternNo:10 Kind:444444
Console.WriteLine($"");
Console.WriteLine($"③ 追加条件:Kind == 333333");
// 動的な検索条件作成
exp.Add(CreateWhere(nameof(Member.Kind), 333333L, typeof(long)));
// 動的な検索
models2 = models.DynamicWhere(exp).ToList();
// 結果出力
WriteConsole(models2);
// 【実行結果】
// ③ 追加条件:Kind == 333333
// Code:1111 PatternNo:10 Kind:333333
// Code:1111 PatternNo:10 Kind:333333
}
/// <summary>
/// 検索条件作成
/// </summary>
/// <param name="columnName">項目名</param>
/// <param name="value">値</param>
/// <param name="type">クラス型</param>
/// <returns></returns>
private Tuple<string, object, Type, LinqExpression> CreateWhere(string columnName, object value, Type type)
{
return new Tuple<string, object, Type, LinqExpression>(columnName, value, type, LinqExpression.And);
}
// ======================
// テスト用
// ======================
/// <summary>
/// テストデータ作成
/// </summary>
/// <returns></returns>
private List<Member> CreateModels()
{
var models = new List<Member>();
models.Add(Create(1111, 10, 333333L));
models.Add(Create(1111, 10, 333333L));
models.Add(Create(1111, 10, 444444L));
models.Add(Create(1111, 10, 444444L));
models.Add(Create(1111, 66, 444444L));
models.Add(Create(1111, 66, 444444L));
models.Add(Create(3333, 10, 444444L));
models.Add(Create(3333, 10, 333333L));
return models;
}
/// <summary>
/// Memberクラス作成
/// </summary>
/// <param name="code"></param>
/// <param name="patternNo"></param>
/// <param name="kind"></param>
/// <returns></returns>
private Member Create(int code, int patternNo, long kind)
{
return new Member
{
Code = code,
PatternNo = patternNo,
Kind = kind,
};
}
/// <summary>
/// 結果出力
/// </summary>
/// <param name="models"></param>
private void WriteConsole(List<Member> models)
{
foreach (var model in models)
{
Console.WriteLine($" Code:{model.Code} PatternNo:{model.PatternNo} Kind:{model.Kind}");
}
}
}
public class Member
{
///<summary>コード</summary>
public int Code { get; set; }
///<summary>パターン</summary>
public int PatternNo { get; set; }
///<summary>種別</summary>
public long Kind { get; set; }
}
まとめ
画面項目などによって、条件が変わる場合など使えるケースもあると思います。
それでは、また。