2016-06-30 17 views
0

Ich habe eine MVC-Site, die das Kendo Grid verwendet und ich versuche, dynamische Filter zu implementieren. Die Daten, die ich zeige, enthalten mehrere 1-zu-viele-Tabellen. Zum Beispiel habe ich eine Reihe von Leuten, jeder Person können 0 oder mehr Gegenstände zugewiesen werden. Ich bin eine abgeflachte Liste im Raster Anzeigen:LINQ zu Entities string-basiert dynamisch Wo

Bob | Item 1, Item 2 
Jane | Item 3 

Wenn ich auf die Artikel Spalte der Filter auf Hart Code, würde es wie folgt aussehen:

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

ich mit kommen wollen eine generische Methode, um den Ausdrucksbaum zu erstellen, so dass ich verschiedene 1-zu-viele Felder filtern und auch verschiedene Vergleiche durchführen kann (zB contains, startswith, equals, etc.). Idealerweise würde ich eine Erweiterungsmethode mit folgenden Syntax:

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

Dann auf mehr I abfragen könnte eine Eins-zu-viele Tabellen:

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

bin ich versucht, LINQ to Entities string based dynamic OrderBy als Ausgangspunkt zu verwenden, weil das Konzept ähnlich ist, aber ich kann nicht herausfinden, wie man die GenerateSelector Methode ändert. Irgendwelche Ideen würden sehr geschätzt werden.

Bearbeiten - Mein Code ist in einem geschlossenen Netzwerk, also werde ich mein Bestes tun, zu replizieren, was ich versuche. Hier ist der Code, den ich ändern möchte. Der Kommentarblock ist wo ich feststecke. Die Beispiele zum Aufrufen der Erweiterungsmethode "Wo" sind weiterhin gültig.

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

Was die [Dynamische LINQ-Bibliothek] mit (http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library)? Sie müssen die bereitgestellten Funktionen auschecken. Vielleicht hilft es Ihnen. Sie können die DynamicQuery-Bibliothek für jeden LINQ-Datenprovider verwenden (einschließlich LINQ to SQL, LINQ to Objects, LINQ to XML, LINQ to Entities, LINQ to SharePoint, LINQ to TerraServer usw.). – Legends

+0

Sie sagen, Ihr Problem besteht darin, dass die GenerateSelector-Methode funktioniert, aber Sie zeigen uns nicht den Code, mit dem Sie ein Problem haben. Wenn Sie ein Problem mit irgendeinem Code haben, MÜSSEN SIE UNS DEN CODE ANZEIGEN, oder wir können Ihnen nicht helfen. – Hogan

Antwort

3

Ich versuche String LINQ to Entities basierte dynamische OrderBy als Ausgangspunkt zu verwenden, da das Konzept ähnlich ist, aber ich kann nicht herausfinden, wie die GenerateSelector Methode zu ändern.

Es gibt einen signifikanten Unterschied zwischen den Methoden, die Selektor wie Select, OrderBy, ThenBy usw. erwarten gegen die Methoden, die Prädikat wie Where erwarten, Any etc. Das kann später nicht die oben GenerateMethodCall verwenden, weil es annimmt, 2 generische Argumente (new Type[] { type, selectorResultType }), während die Prädikatmethoden nur ein generisches Argument verwenden.

So können Sie Ihr Ziel erreichen. Ich habe versucht, es so zu gestalten, dass du jeden Schritt des Ausdrucks nachvollziehen kannst.

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

Ich kann Ihnen nicht genug für Ihr Beispiel danken. Es beantwortete alle meine Fragen. – cas4