2016-06-30 10 views
0

剣道グリッドを使用するMVCサイトがあり、動的フィルタを実装しようとしています。私が表示しているデータには、1対多のテーブルがいくつか含まれています。たとえば、私は人の行を持っている、各人が0以上の項目を割り当てられていることができます。私は、グリッド内の平坦化されたリストを表示しています:LINQ to Entities文字列ベースの動的場所

Bob | Item 1, Item 2 
Jane | Item 3 

を私はアイテム欄にハードコードするフィルタであれば、それは次のようになります。

people.Where(p=> p.Items.Any(i=>i.name.contains("Item 1"))).ToList() 

私が思い付くしたいです式ツリーを構築するための一般的な方法です。異なる1対多のフィールドをフィルタリングしたり、異なる比較(例:contains、startswith、equalsなど)を実行することもできます。理想的には私は次の構文で拡張メソッドを持っているでしょう:

public static IQueryable<TEntity> Where(this IQueryable<TEntity> source, string tableName, string fieldName, string comparisonOperator, string searchVal) where TEntity : class 

その後、私は複数の1対多のテーブルにクエリを実行できます。

if(searchOnItems) 
    persons = persons.Where("Items", "name", "Contains", "item 1); 
if(searchOnOtherTableName) 
    persons = persons.Where("OtherTableName", "name", "Equals", "otherSearchValue); 
persons.ToList(); 

私は出発点としてLINQ to Entities string based dynamic OrderByを使用しようとしています概念は似ているので、私はGenerateSelectorメソッドを変更する方法を理解できません。どんなアイデアでも大歓迎です。

編集 - 私のコードは閉鎖されたネットワーク上にあるので、私が努力しているものを複製するのに最善を尽くします。ここで私が修正しようとしているコードです。コメントブロックは私が立ち往生した場所です。上記の "Where"拡張メソッドを呼び出す例は、依然として有効です。

public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, string tableName, string fieldName, string comparisonOperator, string searchVal) where TEntity : class 
{ 
    MethodCallExpression resultExp = GenerateMethodCall<TEntity>(source, "Where", tableName, fieldName, comparisonOperator, searchVal); 
    return source.Provider.CreateQuery<TEntity>(resultExp) as IOrderedQueryable<TEntity>; 
} 

private static MethodCallExpression GenerateMethodCall<TEntity>(IQueryable<TEntity> source, string methodName, string tableName, String fieldName, string comparisonOperator, string searchVal) where TEntity : class 
{ 
    Type type = typeof(TEntity); 
    Type selectorResultType; 
    LambdaExpression selector = GenerateSelector<TEntity>(tableName, fieldName, comparisonOperator, searchVal, out selectorResultType); 
    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, 
        new Type[] { type, selectorResultType }, 
        source.Expression, Expression.Quote(selector)); 
    return resultExp; 
} 

private static LambdaExpression GenerateSelector<TEntity>(string tableName, String fieldName, string comparisonOperator, string searchVal, out Type resultType) where TEntity : class 
{ 
    // Create a parameter to pass into the Lambda expression (Entity => Entity.OrderByField). 
    var parameter = Expression.Parameter(typeof(TEntity), "Entity"); 

    PropertyInfo property = typeof(TEntity).GetProperty(tableName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);; 
    Expression propertyAccess = Expression.MakeMemberAccess(parameter, property);; 

    /************************************************/ 
    //property is now "TEntity.tableName" 
    //how do I go another step further so it becomes "TEntity.tableName.comparisonOperator(searchVal)" 
    /************************************************/ 

    resultType = property.PropertyType; 
    // Create the order by expression. 
    return Expression.Lambda(propertyAccess, parameter); 
}  
+0

[ダイナミックLINQライブラリ](http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library)の使用についてはどうですか?あなたが提供する機能をチェックアウトする必要があります。おそらくそれはあなたを助けるでしょう。 DynamicQueryライブラリは、LINQデータプロバイダ(LINQ to SQL、LINQ to XML、LINQ to XML、LINQ to Entities、LINQ to SharePoint、LINQ to TerraServerなど)に対して使用できます。 – Legends

+0

問題はGenerateSelectorメソッドを動作させることに問題があると言いますが、問題が発生しているコードは表示されません。コードに問題がある場合は、コードを表示する必要があります。 – Hogan

答えて

3

私はコンセプトが似ているので、出発点として、エンティティ文字列ベースの動的[並べ替えにLINQを使用しようとするんだけど、私はGenerateSelector方法を変更する方法を見つけ出すことはできません。

Where述語を期待セレクタ等対ThenByOrderBySelectような方法を期待する方法、Any間に有意な差は、それが前提としているため、後は上記GenerateMethodCallを使用することはできませんあり2つの一般的な引数(new Type[] { type, selectorResultType })、述語のメソッドは1つの汎用的な引数を使用します。

ここに目標を達成する方法があります。私は表現ビルディングの各ステップに従うことができるように、それを作ろうとしました。

public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, string collectionName, string fieldName, string comparisonOperator, string searchVal) where TEntity : class 
{ 
    var entity = Expression.Parameter(source.ElementType, "e"); 
    var collection = Expression.PropertyOrField(entity, collectionName); 
    var elementType = collection.Type.GetInterfaces() 
     .Single(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)) 
     .GetGenericArguments()[0]; 
    var element = Expression.Parameter(elementType, "i"); 
    var elementMember = Expression.PropertyOrField(element, fieldName); 
    var elementPredicate = Expression.Lambda(
     GenerateComparison(elementMember, comparisonOperator, searchVal), 
     element); 
    var callAny = Expression.Call(
     typeof(Enumerable), "Any", new[] { elementType }, 
     collection, elementPredicate); 
    var predicate = Expression.Lambda(callAny, entity); 
    var callWhere = Expression.Call(
     typeof(Queryable), "Where", new[] { entity.Type }, 
     source.Expression, Expression.Quote(predicate)); 
    return source.Provider.CreateQuery<TEntity>(callWhere); 
} 

private static Expression GenerateComparison(Expression left, string comparisonOperator, string searchVal) 
{ 
    var right = Expression.Constant(searchVal); 
    switch (comparisonOperator) 
    { 
     case "==": 
     case "Equals": 
      return Expression.Equal(left, right); 
     case "!=": 
      return Expression.NotEqual(left, right); 
    } 
    return Expression.Call(left, comparisonOperator, Type.EmptyTypes, right); 
} 
+0

あなたの例では十分にありがとうございます。それは私の質問にすべて答えました。 – cas4