2017-11-18 3 views
1

Ich habe ein kleines Problem mit der Erstellung dynamischer Ausdrucksbaum für Suchlogik. Das Erstellen eines Ausdrucksbaums für Entitätseigenschaft funktioniert gut, aber ich habe keine Ahnung, wie man einen Ausdruck hinzufügt, der nach Kindobjekteigenschaften filtern wird.Filtern, aber Eigentum und Kind Entitätseigenschaft

Hier ist meine Aufgabe Einheit ist:

public class Task: Entity 
{ 
    public TaskType Type { get; set; } 
    public TaskPriority Priority { get; set; } 
    public int ProjectId { get; set; } 
    public Project Project { get; set; } 
} 

Und hier Projektgesellschaft ist:

public class Project: Entity 
{   
    public int CustomerId { get; set; } 
    public Customer Customer { get; set; } 
} 

und Logik für die Entwicklung dynamischer Ausdruck:

public Func<TaskItem, bool> Build(IList<Filter> filters) 
{ 
    ParameterExpression param = Expression.Parameter(typeof(TaskItem), "task"); 
    List<Filter> priorityFilter = FilterFilters(filters, "Priority"); 
    List<Filter> typeFilter = FilterFilters(filters, "Type"); 
    List<Filter> customerFilter = FilterFilters(filters, "CustomerId"); 

    Expression expression = null; 

    // BuildExpression is a method which simply creates expression which is using Tasks properties (like Type or Priority) 
    expression = BuildExpression(param, priorityFilter, expression); 
    expression = BuildExpression(param, typeFilter, expression); 

    // This part need's to be reworked 
    ParameterExpression projectParam = Expression.Parameter(typeof(Project), "project"); 
    Expression projectCustomerExpression = Expression.Equal(Expression.PropertyOrField(projectParam, "CustomerId"), Expression.Constant(customerFilter[0].Value)); 
    Expression customerExpression = Expression.Equal(Expression.PropertyOrField(param, "Project"), projectCustomerExpression); 
    Expression finall = expression != null ? Expression.AndAlso(expression, projectCustomerExpression) : projectCustomerExpression;   
    // End of filtering by CutomerId 

    return Expression.Lambda<Func<TaskItem, bool>>(finall, param).Compile(); 
} 

Ich habe keine Ahnung, wie nach CustomerId filtern. Der Teil über dem Code, der als This part need's to be reworked markiert ist, ist wahrscheinlich falsch oder zumindest der letzte Teil davon. Die Idee besteht darin, existierende Ausdrücke (diese durch BuildExpression erstellte Methode) um einen Ausdruck zu erweitern, der nach CustomerId gefiltert wird.

Ich habe schon etwas Zeit verloren, versuche es selbst und suche nach Antworten, aber ohne Ergebnisse.

Irgendwelche Hilfe?

Antwort

2

Da Sie einen minimalen Code angegeben haben, ist unbekannt, wie Sie die tatsächlichen Ausdrücke erstellen. Also werde ich versuchen, ein allgemeines Rezept für dieses Szenario bereitzustellen.

Wenn Sie eine Liste von Task dann müssen Sie immer noch die gleiche ParameterExpression des Typs verwenden, Task filtern möchten, wie Sie bereits getan haben:

ParameterExpression param = Expression.Parameter(typeof(TaskItem), "task"); 

Es gibt keine Notwendigkeit, eine andere ParameterExpression vom Typ Project zu erstellen auch wenn Sie nach Eigenschaften von Project filtern möchten. Stattdessen müssen Sie nur die ehemalige ParameterExpression wiederverwenden. Beachten Sie, dass, wenn wir eine statische Prädikat wie unten bauen, ist dies auch der Fall, dass wir einen anderen Parameter Ausdruck nicht verwenden auch:

queryableTask.Where(t => t.Priority == TaskPriority.High && t.Project.CustomerId == 123); 

nun dynamisch Filter bauen auf Navigations (Kind) Eigenschaft ist der Schlüssel um den linken Ausdruck (dh Ausdruck für die Navigationseigenschaft) korrekt zu bilden.

Sagen wir, unsere Navigationseigenschaft ist in Punktnotation: Project.CustomerId. Dann können wir etwas tun, den linken Ausdruck für Eigenschaft zu erstellen:

// We already have the following commented properties 
// prop = "Project.CustomerId"; 
// ParameterExpression param = Expression.Parameter(typeof(TaskItem), "task"); 
var leftExpr = prop.Split('.') 
        .Aggregate<string, MemberExpression>(null, 
         (acc, p) => acc == null 
          ? Expression.Property(param, p) 
          : Expression.Property(acc, p)); 

Und dann können Sie den Rest wie eine normale Eigenschaft tun, wie der richtigen Ausdruck zu schaffen und sie mit einem anderen Ausdruck kombiniert Definieren des Operators (Equal, Not Equal etc.).

Hoffe, das hilft.

+0

Danke - Sie haben meinen Tag gerettet! Aber mit einem solchen Ausdruck bekomme ich "Objektreferenz nicht auf eine Instanz eines Objekts gesetzt", wenn ich ToList() so lade: 'Context.Tasks.Where (searchCondition) .ToList()' (Suchbedingung enthält das Ausdruck es ist 'IQueryable '). Der Stack-Trace: 'at lambda_method (Closure, Task), bei System.Linq.Enumerable.WhereEnumerableIterator1.ToList(), bei System.Linq.Enumerable.ToList [TSource] (IEnumerable1 Quelle) '. Irgendwelche Ideen? –

+1

@DawidRutkowski Wahrscheinlich ist 'Project'' null'. Wenn Sie "IQueryable ' füllen möchten, sollten Sie die Methodensignatur ändern, um 'Expression >' zurückzugeben und 'Compile()' zu entfernen. Derzeit, selbst wenn Ihre Quelle 'IQueryable ' ist, ruft der Compiler 'Where' Methode auf' IEnumerable '. –

+1

@DawidRutkowski Obwohl ich nicht sicher bin, aber eine mögliche Ursache wäre, wie von @ IvanStoev hingewiesen. Wenn Sie '.Compile' in' searchCondition' verwenden, müssen Sie das nicht tun, es sei denn, Sie möchten das verzögerte Laden vermeiden. Stattdessen können Sie einfach die 'searchCondition' direkt auf' IQueryable' Daten anwenden und dann 'ToList' zum Aufzählen. Wenn das nicht funktioniert, können Sie als Hack versuchen, den Navigationseigenschaftenpfad (mit Ausnahme der letzten Eigenschaft im Pfad; d. H. Die tatsächliche Eigenschaft des einfachen Typs) einzubeziehen, um eine Verknüpfung zu erzwingen. –

Verwandte Themen