2016-10-06 4 views
0

フィルターを通過するグリッドがあります。その後、私はそう、私はdynamic.LINQを使用して、これらのフィルタを適用する方法を持っていることを行うためにこれを取るとのIQueryableを拡張する必要がグリッドプロパティにエンティティプロパティがない場合、グリッドフィルタリングに基づいてLINQ述語/ dynamic.LINQクエリを作成するにはどうすればよいですか?

var filter = new Filter(){ 
    Member = "Titles", 
    Operator = Filter.Operators.IsEqualTo, 
    Value = "Developer" 
}; 

:だから私は次のようにオブジェクトを有していてもよく

private IQueryable<TReportClass> ApplyFilter(ReportFilter filter, IQueryable<TReportClass> baseQuery) 
    { 
     switch (filter.Operator) 
     { 
      case ReportFilter.Operators.Contains: 
       baseQuery = baseQuery.Where(string.Format("{0}.Contains(@0)", filter.Member), filter.Value); 
       break; 
      case ReportFilter.Operators.DoesNotContain: 
       baseQuery = baseQuery.Where(string.Format("!{0}.Contains(@0)", filter.Member), filter.Value); 
       break; 
      case ReportFilter.Operators.IsEqualTo: 
       baseQuery = baseQuery.Where(string.Format("{0} = @0", filter.Member), filter.Value); 
       break; 
      case ReportFilter.Operators.IsNotEqualTo: 
       baseQuery = baseQuery.Where(string.Format("{0} != @0", filter.Member), filter.Value); 
       break; 
      case ReportFilter.Operators.StartsWith: 
       baseQuery = baseQuery.Where(string.Format("{0}.StartsWith(@0)", filter.Member), filter.Value); 
       break; 
      case ReportFilter.Operators.EndsWith: 
       baseQuery = baseQuery.Where(string.Format("{0}.EndsWith(@0)", filter.Member), filter.Value); 
       break; 
      case ReportFilter.Operators.IsNull: 
       baseQuery = baseQuery.Where(string.Format("{0} = NULL", filter.Member)); 
       break; 
      case ReportFilter.Operators.IsNotNull: 
       baseQuery = baseQuery.Where(string.Format("{0} != NULL", filter.Member)); 
       break; 
      case ReportFilter.Operators.IsEmpty: 
       baseQuery = baseQuery.Where(string.Format("string.IsNullOrEmpty({0})", filter.Member)); 
       break; 
      case ReportFilter.Operators.IsNotEmpty: 
       baseQuery = baseQuery.Where(string.Format("!string.IsNullOrEmpty({0})", filter.Member)); 
       break; 
     } 

     return baseQuery; 
    } 

しかし、これは非コレクションに対してのみ機能します。コレクションを使用するにはどうすればよいですか?

public class UserReport : Entity 
{ 
    public string Name { get; set; } 
    public string Email { get; set; } 
    public List<string> Titles { get; set; } 
} 

そしてベースとして、このクエリ::私はこのモデルがある場合は、私はに変換するには、上記のフィルタを変換するにはどうすればよい

IQueryable<UserReport> filteredQuery = ApplyFilters(filters, baseQuery); 

IQueryable<UserReport> baseQuery = MyDbContext.DbSet<User>.Select(user => new UserReport 
     { 
      Id = user.Id, 
      Name = user.FirstName + " " + user.LastName, 
      Email = user.Email, 
      Titles = user.Positions.Select(apptment => apptment.Title).ToList() 
     }) 

をだから私は次のように呼び出すことができますLINQのような:

baseQuery.Where(userReport => userReport.Titles.Any(title => title == "Developer") 

Can tha動的LINQで完了するか?あるいは私自身の述語を構築する必要がありますか?もしそうなら、どうしたらいいですか?

答えて

1

System.Linq.DynamicSystem.Linq.Expressionsの両方で可能です。

System.Linq.Dynamicを解決して、現在のコードに近づけてください。必要なのは、メンバーが集まりであれば特定し、動的な基準について、次のパターンを使用することです:

コレクション:{PropertyName}.Any(it{condition})
オブジェクト:{PropertyName}{condition}

と実装は、基本的には、(このようなことができ)Func<string, string, string>string.Formatを置き換える:

private IQueryable<TReportClass> ApplyFilter(ReportFilter filter, IQueryable<TReportClass> baseQuery) 
{ 
    var property = typeof(TReportClass).GetProperty(filterMember); 
    bool isCollection = property.Type != typeof(string) && 
     && typeof(IEnumerable).IsAssignableFrom(property.Type); 
    Func<string, string, string> condtion; 
    if (isCollection) 
     condition = (format, member) => string.Format("{0}.Any({1})", member, string.Format(format, "it")); 
    else 
     condition = (format, member) => string.Format(format, member); 
    switch (filter.Operator) 
    { 
     case ReportFilter.Operators.Contains: 
      baseQuery = baseQuery.Where(condition("{0}.Contains(@0)", filter.Member), filter.Value); 
      break; 
     case ReportFilter.Operators.DoesNotContain: 
      baseQuery = baseQuery.Where(condition("!{0}.Contains(@0)", filter.Member), filter.Value); 
      break; 
     case ReportFilter.Operators.IsEqualTo: 
      baseQuery = baseQuery.Where(condition("{0} = @0", filter.Member), filter.Value); 
      break; 
     case ReportFilter.Operators.IsNotEqualTo: 
      baseQuery = baseQuery.Where(condition("{0} != @0", filter.Member), filter.Value); 
      break; 
     case ReportFilter.Operators.StartsWith: 
      baseQuery = baseQuery.Where(condition("{0}.StartsWith(@0)", filter.Member), filter.Value); 
      break; 
     case ReportFilter.Operators.EndsWith: 
      baseQuery = baseQuery.Where(condition("{0}.EndsWith(@0)", filter.Member), filter.Value); 
      break; 
     case ReportFilter.Operators.IsNull: 
      baseQuery = baseQuery.Where(condition("{0} = NULL", filter.Member)); 
      break; 
     case ReportFilter.Operators.IsNotNull: 
      baseQuery = baseQuery.Where(condition("{0} != NULL", filter.Member)); 
      break; 
     case ReportFilter.Operators.IsEmpty: 
      baseQuery = baseQuery.Where(condition("string.IsNullOrEmpty({0})", filter.Member)); 
      break; 
     case ReportFilter.Operators.IsNotEmpty: 
      baseQuery = baseQuery.Where(condition("!string.IsNullOrEmpty({0})", filter.Member)); 
      break; 
    } 

    return baseQuery; 
} 
+0

おかげで、それは一度だけ使用反射によりパフォーマンスだろうoutsi deのApplyFilterを呼び出し、各ApplyFilter呼び出しでリフレクションを使用する代わりに、すべてのコレクションプロパティ名をApplyFilterに渡しますか?また、プロパティが 'typeof(string)'でなく、 'typeof(IEnumerable)'から代入可能であるかどうかをリフレクションが最初に調べている理由についても興味がありますか? 'typeof(IEnumerable ).IsAssignableFrom(property.Type)'をチェックする文を1つだけ持つことはできませんでしたか? – SventoryMang

+0

(1)この場合、反射の影響はごくわずかです。結果セットの各要素ごとに実行されるものではありません。また、 'System.Linq.Expressions'クラスで式ツリーを構築することは、とにかく多くのリフレクションを行います - どのようにDynamic LINQが渡された文字列を処理すると思いますか? (2) 'string'リストだけをサポートしていれば十分でしょう。上記はより一般的であり、値を持つリストであれば動作します。もちろんフィルタ演算子のいくつかは文字列に対してのみ定義されていますが、 'Is(Not)EqualTo'、' Is(Not)Null'のようなものは 'int'、' DateTime'などにも使用できます。 –

+0

とにかく、上記の実装は単なる実装ですが、あなたのニーズに合わせて調整することができます:) –

関連する問題