クエリの左側と右側の両方に対して動的に LINQ クエリを生成する必要がある状況がありました。つまり、Where("何らかのプロパティ == 何らかの値") です。私は LINQPad の担当者が提供する PredicateBuilder をいじり、他にもいくつか試してみましたが、最終的には LINQ 動的クエリ ライブラリ (System.Linq.Dynamic) によってこの作業が非常に簡単になりました。
すべての詳細を説明しなくても、私が持っているのは、MVC ページの jqGrid でアイテムをフィルター処理およびソートするためのパラメーターを受け取るメソッドです。QueryOptions というオブジェクトは、さまざまなクエリ設定を保持します。Data.ImportDataSearchView は、バックエンドのデータベース ビューに関連付けられている Entity Framework エンティティです。
フィルター式は、次の呼び出しによって作成されます。
options.FilterExpression += filterList.BuildFilterExpression<Data.ImportDataSearchView>();
BuildFilterExpression の一部は次のとおりです。
public string BuildFilterExpression<T>()
{
var type = typeof(T);
var exp = string.Empty;
foreach (Filter filter in this)
{
var typeName = filter.DataType.ToLower();
// Skip if no values
if (!filter.Values.Any())
continue;
switch (typeName)
{
case "string":
// html decode string and escape single quotes
var stringVal = System.Web.HttpUtility.HtmlDecode(filter.Values[0]);
stringVal = stringVal.Replace("'", "''");
if (filter.Operator == Enums.FilterOperator.CONTAINS)
exp += string.Format("{0}.Trim().ToLower().Contains(\"{1}\")", filter.Attribute, stringVal.Trim().ToLower());
else if (filter.Operator == Enums.FilterOperator.DOES_NOT_EQUAL)
exp += string.Format("!{0}.ToLower().Equals(\"{1}\")", filter.Attribute, stringVal.ToLower());
else if (filter.Operator == Enums.FilterOperator.DOES_NOT_CONTAIN)
exp += string.Format("!{0}.Trim().ToLower().Contains(\"{1}\")", filter.Attribute, stringVal.Trim().ToLower());
else if (filter.Operator == Enums.FilterOperator.ENDS_WITH)
exp += string.Format("{0}.Trim().ToLower().EndsWith(\"{1}\")", filter.Attribute, stringVal.Trim().ToLower());
else if (filter.Operator == Enums.FilterOperator.EQUALS)
exp += string.Format("{0}.ToLower().Equals(\"{1}\")", filter.Attribute, stringVal.ToLower());
else if (filter.Operator == Enums.FilterOperator.STARTS_WITH)
exp += string.Format("{0}.Trim().ToLower().StartsWith(\"{1}\")", filter.Attribute, stringVal.Trim().ToLower());
break;
//case "select": -- for dropdowns
//case "datetime": -- for dates, etc. etc.
// add spaces around expression
exp = string.Format(" {0} ", exp);
// add and/or to expression
if (this.IndexOf(filter) != this.Count() - 1)
exp += string.Format(" {0} ", ExpressionType.ToLower() == "and" ? "&&" : "||");
}
return exp;
}
そして、式文字列を構築した後、データは次のように取得されました。
options.OrderBy = string.IsNullOrEmpty(sortIndex) ? "TrackingId asc" : string.Format(" {0} {1} ", sortIndex, sortOrder);
var db = new Data.BmpDB();
var list = string.IsNullOrEmpty(options.FilterExpression)
? db.ImportDataSearchViews.OrderBy(options.OrderBy).ToList()
: db.ImportDataSearchViews.Where(options.FilterExpression).OrderBy(options.OrderBy).ToList();
以下の options.FilterExpression 文字列は、メソッド BuildFilterExpression によって 3 つの検索基準フィールドをつなぎ合わせて、LINQ where 句の適切で単純な述語文字列にした例です。
"BmpName.Trim().ToLower().Contains(\"crops\") && DataProviderId.ToLower().Equals(\"123\") && StatusId == 1"