2016-06-30 3 views
0

J'ai un site MVC qui utilise la grille de Kendo et j'essaie d'implémenter des filtres dynamiques. Les données que j'affiche contiennent plusieurs tables 1-à-plusieurs. Par exemple, j'ai une rangée de personnes, chaque personne peut avoir 0 ou plusieurs objets qui lui sont assignés. J'affichons une liste aplatie dans la grille:LINQ to Entities string based dynamic Où

Bob | Item 1, Item 2 
Jane | Item 3 

Si je devais coder en dur le filtre sur la colonne Items, il ressemblerait à ceci:

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

Je veux venir avec une façon générique de construire l'arbre d'expression afin que je puisse filtrer sur différents champs 1-à-plusieurs et effectuer des comparaisons différentes (par exemple, contains, startswith, equals, etc.). Idéalement, je voudrais avoir une méthode d'extension avec la syntaxe suivante:

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

alors je pourrais interroger sur plusieurs one-to-many tables:

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

Je tente d'utiliser LINQ to Entities string based dynamic OrderBy comme point de départ puisque le concept est similaire, mais je n'arrive pas à comprendre comment modifier la méthode GenerateSelector. Toutes les idées seraient grandement appréciées.

Édition - Mon code est sur un réseau fermé, je ferai de mon mieux pour reproduire ce que j'essaie. Voici le code que je tente de modifier. Le bloc de commentaire est où je suis coincé. Les exemples d'appel de la méthode d'extension "Where" ci-dessus sont toujours valides.

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

Qu'en est-il en utilisant le (http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library) [bibliothèque dynamique LINQ]? Vous devez vérifier la fonctionnalité qu'il fournit. Vous pouvez utiliser la bibliothèque DynamicQuery avec n'importe quel fournisseur de données LINQ (y compris LINQ to SQL, LINQ to Objects, LINQ to XML, LINQ to Entities, LINQ to SharePoint, LINQ to TerraServer, etc.). – Legends

+0

Vous dites que votre problème est de faire fonctionner la méthode GenerateSelector, mais vous ne nous montrez pas le code avec lequel vous rencontrez un problème. Si vous avez un problème avec un code, vous devez nous montrer le code ou nous ne pouvons pas vous aider. – Hogan

Répondre

3

Je tente d'utiliser LINQ to Entities chaîne OrderBy dynamique basée comme point de départ puisque le concept est similaire, mais je ne peux pas comprendre comment modifier la méthode GenerateSelector.

Il y a une différence significative entre les méthodes qui attendent sélecteur comme Select, OrderBy, etc. ThenBy par rapport aux méthodes qui attendent prédicat comme Where, Any etc. plus tard ne peut pas utiliser le GenerateMethodCall ci-dessus parce qu'elle suppose 2 arguments génériques (new Type[] { type, selectorResultType }) alors que les méthodes de prédicat n'utilisent qu'un seul argument générique.

Voici comment vous pouvez atteindre votre objectif. J'ai essayé de faire en sorte que vous puissiez suivre chaque étape du bâtiment d'expression.

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

Ne peut pas vous remercier assez pour votre exemple. Il a répondu à toutes mes questions. – cas4