簡體   English   中英

如何將 Expression<> 合並到實體框架查詢中?

[英]How to incorporate an Expression<> into an Entity Framework query?

我正在嘗試編寫一個 class 來幫助動態創建 LINQ 查詢。

protected Func<T, TColumn> GetColumn;

public MyClass(Func<T, TColumn> getColumn)
{
    GetColumn = getColumn;
}

public virtual IQueryable<T> ApplyFilter(IQueryable<T> query)
{
    if (FilterMode == FilterModeMatchAny)
        return query.Where(x => FilterIds.Contains(GetColumn(x)));
    return query;
}

這個 class 是這樣調用的:

MyClass<Location, string> myClass = new MyClass<Location, string>(l => l.State);

var locations = myClass.ApplyFilter(DbContext.Locations);

但是,上面的代碼失敗了:

LINQ 表達式'DbSet
.Where(l => Invoke(__GetJoiningTables_0, l[Location])
.Any(xx => __FilterIds_1
.Contains(Invoke(__GetColumn_2, xx)
)))' 無法翻譯。 以可翻譯的形式重寫查詢,或通過插入對 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的調用顯式切換到客戶端評估。 有關詳細信息,請參閱https://go.microsoft.com/fwlink/?linkid=2101038

問題似乎是我使用GetColumn的方式。 所以我改變了GetColumn的聲明,使它現在是一個表達式。

protected Expression<Func<T, TColumn>> GetColumn;

傳遞給我的構造函數的相同參數可以很容易地轉換為這種類型。 我只需要更改參數類型。

但是現在如何在我的ApplyFilter()方法中使用這個新的GetColumn呢?

更新:

最后,我還需要對以下兩個表達式做同樣的事情。

// Expressions to incorporate
protected Expression<Func<T, ICollection<TJoinTable>>> GetJoiningTables;
protected new Expression<Func<TJoinTable, TColumn>> GetColumn;

// Match any query
return query.Where(x => GetJoiningTables(x).Any(xx => FilterIds.Contains(GetColumn(xx))));

// Match all query
return query.Where(x => GetJoiningTables(x).Count(xx => FilterIds.Contains(GetColumn(xx))) >= FilterIds.Count());

首先,您需要將列保留為表達式,否則 c# 將編譯 lambda function 並且 EF 將無法提取它是哪一列;

protected Expression<Func<T, TColumn>> GetColumn;

public MyClass(Expression<Func<T, TColumn>> getColumn)
{
    GetColumn = getColumn;
}

現在,您可以使用Expression的 static 方法手動構建整個過濾器表達式。 但在你的情況下,有一條捷徑。 因為您可以重用輸入表達式的參數和主體(例如x => x.Column ),並將其包裝在您的Contains調用中(例如x => FilterIds.Contains(x.Column) )。

編輯:

既然您現在已經發現FilterIds是一個IEnumerable<T> ,那么FilterIds.Contains()實際上是 static 擴展方法Enumerable.Contains() 查找匹配的通用 static 方法的最簡單方法是創建匹配的委托。

public virtual IQueryable<T> ApplyFilter(IQueryable<T> query)
{
    if (FilterMode == FilterModeMatchAny)
        return query.Where(
            Expression.Lambda<Func<T, bool>>(
                Expression.Call(
                    null,
                    new Func<IEnumerable<TColumn>,TColumn,bool>(Enumerable.Contains).Method,
                    Expression.Constant(FilterIds),
                    GetColumn.Body),
                GetColumn.Parameters)
        );
    return query;
}

編輯:

.Where(x => GetJoiningTables(x).Any(...

好的,那是一罐蠕蟲....我假設您在這里嘗試做的是收集到其他表和列的導航集合,然后對其應用過濾器?

我發現它有助於構建一個您想要實現的示例Expression 我假設您正在嘗試構建表達式,例如;

Expression<Func<T,bool>> filter = t => 
    Enumerable.Any(t.Child1, c => FilterIds.Contains(c.ChildCol))
    || Enumerable.Any(t.Child2, c => FilterIds.Contains(c.OtherChildCol))
    ... ;

Expression<Func<T,bool>> filter = t => 
    Enumerable.Count(t.Child1, c => FilterIds.Contains(c.ChildCol))
    + Enumerable.Count(t.Child2, c => FilterIds.Contains(c.OtherChildCol))
    ... ;

我建議您編寫此代碼,讓 c# 編譯器將它們轉換為Expression圖,並使用調試器查看這些圖的樣子。

如果可能的話,我建議嘗試找到一種方法來“將凱撒的東西交給凱撒”。 並通過內聯或轉換某些模板Expression來組裝Expression的片段。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM