![](/img/trans.png)
[英]StartsWith with any string from list to SQL request with LINQ and Entity Framework
[英]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
要么存在某些組合,例如無法翻譯的StartsWith
和Any
。
解決方法可能是:
Contains
方法以最接近的方式過濾您的查詢。var results = students.Where(s => letters.Any(l => s.name.Contains(l)));
這是我為 .NET fiddle 編寫的一個類似示例。
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
是第一部分的查詢。
更多的:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.