EF Core 6 Cosmos DB筛选订单数4-6的客户查询报错问题
问题场景
现有如下实体模型,需通过EF Core 6操作Cosmos DB,查询订单数在4到6之间的客户:
public class CustomerEntity { public string Id { get; set; } public string Name{ get; set; } public IList<Order> Orders { get; set; } } public class Order { public string Id { get; set; } public double TotalPrice { get; set; } public double TotalDiscount { get; set; } }
尝试使用以下Linq表达式查询时触发翻译异常:
dbContext.Customers .Where(c => c.Orders.Count >= 4 && c.Orders.Count <= 6) .Count()
异常信息:
System.Private.CoreLib: Exception while executing function: GetStreams. Microsoft.EntityFrameworkCore: The LINQ expression 'DbSet() [2023-03-31T08:25:24.420Z] .Where(EF.Property
(s, "Order") [2023-03-31T08:25:24.421Z] .AsQueryable() [2023-03-31T08:25:24.423Z] .Count() >= __get_Item_0 && EF.Property (s, "Order") [2023-03-31T08:25:24.424Z] .AsQueryable() [2023-03-31T08:25:24.424Z] .Count() <= __get_Item_1 && True && True && True)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.
由于Azure Functions兼容性限制,无法升级至EF Core 7,需寻求替代方案。
原因分析
EF Core 6对Cosmos DB的嵌套集合Count()方法的LINQ翻译支持有限,无法将其转换为Cosmos DB可识别的原生SQL查询,导致触发翻译异常。
解决方案
方案1:使用Cosmos DB原生SQL查询(推荐)
直接利用Cosmos DB的ARRAY_LENGTH函数查询数组长度,通过FromSqlRaw执行原生SQL,EF Core 6可正确处理这种查询的翻译:
参数化版本(避免SQL注入)
var minOrderCount = new SqlParameter("@min", 4); var maxOrderCount = new SqlParameter("@max", 6); var customerCount = dbContext.Customers .FromSqlRaw(@" SELECT * FROM c WHERE ARRAY_LENGTH(c.Orders) >= @min AND ARRAY_LENGTH(c.Orders) <= @max", minOrderCount, maxOrderCount) .Count();
直接值版本(仅适用于固定值场景)
var customerCount = dbContext.Customers .FromSqlRaw(@" SELECT * FROM c WHERE ARRAY_LENGTH(c.Orders) >= 4 AND ARRAY_LENGTH(c.Orders) <= 6") .Count();
方案2:投影后过滤(部分场景可用)
若原生SQL不符合你的编码习惯,可尝试先投影出订单数量再过滤,部分场景下EF Core 6可正确翻译该逻辑:
var customerCount = dbContext.Customers .Select(c => new { Customer = c, OrderCount = c.Orders.Count }) .Where(x => x.OrderCount >= 4 && x.OrderCount <= 6) .Select(x => x.Customer) .Count();
注:若该方案仍触发翻译异常,优先使用方案1。
内容的提问来源于stack exchange,提问作者Jorge Lopez

