簡體   English   中英

如何修改基於表達式的過濾器以避免在 Entity Framework Core 3.0 中進行客戶端評估

[英]How to modify expression-based filters to avoid client-side evaluation in Entity Framework Core 3.0

我有以下代碼用於將基於Func的過濾器轉換為Expression並過濾Entity Framework Core 2.2中的數據:

public async Task<TType> GetDataAsync<TType>(Func<TType, bool> filtering = null) where TType : class
{
  Expression<Func<TType, bool>> filteringExpression = (type) => filtering(type);
  if (filtering != null)
    //return await myContext.Set<TType>().FirstOrDefaultAsync(filteringExpression);
    return await myContext.Set<TType>().Where(filteringExpression ).FirstOrDefaultAsync();
  return await myContext.Set<TType>().FirstOrDefaultAsync();
}

這就是我使用它的方式:

public async Task<DataLog> GetDataLogByID(Guid dataLogID) => await GetDataAsync<DataLog>(dataLog => dataLog.ID == dataLogID);

(不)幸運的是,當我升級到Entity Framework Core 3.0時,代碼拋出了InvalidOperationException因為表達式無法轉換為 SQL 查詢(盡管它只過濾與數據庫列匹配的屬性):

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

那么你能告訴我,我應該如何修改代碼以確保所有(大部分)處理都留在服務器端? 保持通用代碼但符合標准的最佳實踐是什么?

恭喜,您發現 EF Core 3.0 中的一項重大更改 - LINQ 查詢不再在客戶端上評估

舊行為

在 3.0 之前,當 EF Core 無法將作為查詢一部分的表達式轉換為 SQL 或參數時,它會自動在客戶端上計算表達式。 默認情況下,客戶端對可能昂貴的表達式的評估只會觸發警告。

新行為

從 3.0 開始,EF Core 僅允許在客戶端評估頂級投影(查詢中的最后一個 Select() 調用)中的表達式。 當查詢的任何其他部分中的表達式無法轉換為 SQL 或參數時,將引發異常。

有關更多信息,請參閱文檔(上面的鏈接),但是您在升級之前遇到的警告現在正在生成 InvalidOperationExceptions 並且與 SQLite 無關,您會遇到與 SQL 服務器相同的問題。

解決此問題的唯一方法是確保您的過濾表達式/函數可以轉換為適當的 SQL... 或恢復為 EF Core < 3.0

更新

您可以嘗試包裝傳遞的 Func 並將參數類型更改為Expression<Func<TType, bool>> (它不需要對調用該方法的代碼進行任何更改)

public async Task<TType> GetDataAsync<TType>(Expression<Func<TType, bool>> filter = null)
    where TType : class
{
    var query = myContext.Set<TType>();

    if (filter != null)
        query = query.Where(filter);

    return await query.FirstOrDefaultAsync();
}

剛剛注意到對GetDataAsync的調用似乎不正確,並且有一個額外的類型參數Guid ,應該從這個示例中刪除。

public async Task<DataLog> GetDataLogByID(Guid dataLogID) =>
    await GetDataAsync<DataLog>(dataLog => dataLog.ID == dataLogID);

暫無
暫無

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

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