2017-11-18 146 views
1

我爲構建搜索邏輯的動態表達式樹遇到了一個小問題。爲實體自己的屬性創建一個表達式樹工作正常,但我不知道如何添加一個表達式,它將按照子實體屬性進行過濾。過濾但屬性和子實體屬性

這是我的任務實體:

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

這裏是項目實體:

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

和邏輯構建動態表達:

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(); 
} 

我不知道怎麼樣通過CustomerId進行過濾。上面代碼標記爲This part need's to be reworked的部分可能是錯誤的,或者至少是其最後部分。這個想法是擴展現有的表達式(這個由BuildExpression方法構建的表達式)和一個將由CustomerId過濾的表達式。

我已經在這方面失去了一些時間,嘗試自己尋找答案,但沒有結果。

任何幫助?

回答

2

由於您提供了最少的代碼,因此如何創建實際表達式是未知的。所以,我會嘗試爲這種情況提供一個通用配方。

如果你想過濾的Task列表,那麼你仍然需要使用Task類型相同​​,就像你已經這樣做:

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

沒有必要創建Project類型的其他​​即使您想過濾Project的屬性。相反,你只需要重用前者​​。需要注意的是,如果我們建立一個靜態謂詞像下面,這也是我們也不使用不同的參數表達的情況:

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

我們動態地建立在導航(子)屬性篩選,關鍵是以正確形成左表達式(即導航屬性的表達式)。

可以說我們的導航屬性是用點符號表示的:Project.CustomerId。然後,我們可以做這樣的事情,以創建左表達財產:

// 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)); 

然後你就可以做其他像一個正常的特性,例如創建正確表達,並將其與另一種表達結合定義運算符(等於,不等於等)。

希望這會有所幫助。

+0

謝謝 - 你救了我的一天!但是當我像這樣調用ToList()時,我得到「對象引用未設置爲對象的實例」:'Context.Tasks.Where(searchCondition).ToList()'(搜索條件包含表達它是'IQueryable ')。堆棧跟蹤:在System.Linq.Enumerable的System.Linq.Enumerable.WhereEnumerableIterator1.ToList()處的lambda_method(Closure,Task)處。ToList [TSource](IEnumerable1 source)'。 任何想法? –

+1

@DawidRutkowski很可能'Project'是'null'。如果你想填充'IQueryable ',你最好改變方法簽名來返回'Expression >'並移除'Compile()'調用。目前,即使您的源代碼是'IQueryable ',編譯器也會調用'IEnumerable '上的Where'方法。 –

+1

@DawidRutkowski雖然我不確定,但是可能的原因會像@IvanStoev指出的那樣。如果你在'searchCondition'上使用'.Compile',那麼你不必這樣做,除非你想避免延遲加載。相反,您可以直接將'searchCondition'應用於'IQueryable'數據,然後'ToList'來枚舉。如果這不起作用,那麼可以嘗試包括導航屬性路徑(不包括路徑中的最後一個屬性;即簡單類型的實際屬性)以強制連接。 –