簡體   English   中英

實體框架、LINQ:不能將 Any 與 StartsWith 一起使用?

[英]Entity Framework, LINQ: can't use Any with StartsWith?

我有一個簡單的實體框架 LINQ 查詢。 目標是找到所有以 A、B 或 C 開頭的載體:

var letters = new List<string>() { "A", "B", "C" }; // Dynamic, can be many
var results = db.Carriers.AsNoTracking()
    .Where(c => letters.Any(val => c.Name.StartsWith(val)))
    .ToList();

我得到

System.InvalidOperationException: 'LINQ 表達式 'DbSet .Where(c => __letters_0 .Any(val => val == "" || c.Name != null && val != null && c.Name.StartsWith(val) ))' 無法翻譯。 以可翻譯的形式重寫查詢,或通過插入對 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的調用顯式切換到客戶端評估。 有關詳細信息,請參閱https://go.microsoft.com/fwlink/?linkid=2101038

沒有辦法做到這一點嗎?

在實體框架 6 中,此查詢可以正常運行。 EF6 支持StartsWith SQL 轉換,並且支持在查詢表達式中使用本地序列( letters )。

EF 核心 3(異常消息所指示的問題中的版本)還支持StartsWith SQL 轉換。 這里的問題(另一個答案完全忽略了)是本地序列的使用方式不受支持。 像這樣的查詢...

var results = db.Carriers.AsNoTracking()
    .Where(c => letters.Contains(c.Name))
    .ToList();

...將得到支持,因為letters可以簡單地轉換為IN子句。 但當然,這是一個完全不同的查詢。

使用letters.Any需要 EF 將letters轉換為可以在 SQL 中連接的“東西”。 EF6 通過在 SQL 查詢中構建結果集來做到這一點:

    WHERE  EXISTS (SELECT 
        1 AS [C1]
        FROM  (SELECT 
            N'A' AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]
        UNION ALL
            SELECT 
            N'B' AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable2]
        UNION ALL
            SELECT 
            N'C' AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable3]) AS [UnionAll2]
        WHERE ( CAST(CHARINDEX([UnionAll2].[C1], [Extent1].[Name]) AS int)) = 1

哪個有效,但根本無法擴展。 EF core 3 不支持它,並且沒有其他答案中建議的簡單解決方法。

一種可能的解決方法是使用||構建謂詞 (或)謂詞,例如:

var pred = letters.Aggregate(PredicateBuilder.False<Carrier>(), 
    (p,x) => p = p.Or(c => c.Name.StartsWith(x)));
var results = db.Carriers.AsNoTracking()
    .Where(pred)
    .ToList();

其中PredicateBuilder是像 Linqkit 或this one這樣的謂詞構建器。 但是這種方法也不是可擴展的。 EF創建中的每個條目參數letters ,所以你可能會碰到在SQL Server 2100的參數閾值。

實體框架不允許使用自定義謂詞。 這就是您的查詢失敗並顯示一條消息的原因:

……無法翻譯。 要么以可以翻譯的形式重寫查詢......

因此,這很可能意味着您的 EF 版本要么不支持StartsWith要么存在某些組合,例如無法翻譯的StartsWithAny

解決方法可能是:

  1. 首先,您可以使用Contains方法以最接近的方式過濾您的查詢。
var results = students.Where(s => letters.Any(l => s.name.Contains(l)));

是我為 .NET fiddle 編寫的一個類似示例。


  1. 接下來,如果您仍想使用StartsWith但您的 EF 不支持您可以將查詢轉換為 IN-MEMORY 集合:
var results = filter_by_contains_query
              .ToList() // convert filtered result to in-memory collection
              .Where(i => letters.Any(l => i.StudentName.StartsWith(l)));

其中filter_by_contains_query是第一部分的查詢。


更多的:

  • 關於 EF 支持的方法,請查看此處
  • 關於StartsWithEndsWithContains討論在這里

暫無
暫無

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

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