场景
我有这样的代码(简化):
var query = _context.Products
.Where(x => x.IsActive)
.Select(x => new ProductDto
{
Id = x.Id,
Name = x.Name,
Description = GetDescription(x)
});
// Building predicates (using LinqKit)
var predicate = PredicateBuilder.New<T>();
query = query.Where(predicate);
var count = await query.CountAsync();
var list = await query.ToListAsync();
字符串
当我按Id,Name过滤时,它可以正常工作。当我想按Description
过滤时,就会出现问题,例如:
query = query.Where(x => x.Description.Contains("winter"));
型
这里抛出了一个异常-- Linq无法将其转换为SQL --这是可以理解的。
问题
你认为我该如何处理这个问题?这就是我构建过滤/排序等的方式,我想保持这种方式。我如何通过使用C#方法的属性进行过滤/排序,而这些属性无法转换为SQL?
我努力
在阅读这篇高效查询EF核心之后,我有了一个想法,也许这就是要走的路。
我试着这样做:
var predicate = PredicateBuilder.New<T>();
var enumerable = query.AsEnumerable();
enumerable = enumerable.Where(predicate);
// I convert it back to Queryable to use things such as
// Take() and Skip() hoping it will be translated into SQL
query = enumerable.AsQueryable();
// Here I lost the ability to use "Async"
var count = query.Count();
var list = query.ToList();
型
我失去了在SQL级别上过滤的能力(但我可能能够区分在哪个阶段应用哪个过滤器),但我不确定它是如何工作的。
我想我理解缓冲和流之间的区别-在这里我不加载所有的结果到内存-我希望流发生。当检查DB调用时-只进行单个调用(使用.Count()
和.ToList()
),这听起来不错。
这是解决问题的有效方法吗?.AsEnumerable()
之后的过滤是如何工作的?如果Linq既不将GetDescription(x)
加载到内存中,也不向数据库发送请求,它如何知道如何处理GetDescription(x)
?
//编辑以获取更多上下文:
我可能简化得太多了,很抱歉。我的GetDescription方法看起来像这样:
public static string GetDescription(int productType)
{
switch (productType)
{
case 0:
return ProductTypeEnum.Value1.GetDescription();
case 1:
return ProductTypeEnum.Value2.GetDescription();
}
return string.Empty;
}
型
我在查询中这样使用它:
Description = x.ProductType != null ? GetDescription(x.ProductType) : string.Empty
型
解决问题后的思考
我误解了查询是如何被翻译的。事实误导了我,没有通过有问题的属性进行过滤-一切都很好。我以为这意味着它在服务器端进行评估并转换为SQL。发生的事情是,一旦获取记录,此属性被标记为在客户端进行评估。
如果你的查询不能被转换成SQL -首先要确保查询写得很好,因为它可能不是。
2条答案
按热度按时间nbysray51#
我想我明白缓冲和流的区别了
这里的区别是客户端评估和服务器端评估。
这是解决问题的有效方法吗?
有争议-不。
.AsEnumerable()
之后的过滤究竟是如何工作的?之前的一切都将转换为SQL并在数据库端执行,之后的一切都将在客户端处理(在存储器中)即对于
Count
所有匹配的行将被提取到存储器中,GetDescription
将被调用,然后过滤将发生,然后项目将被计数。然后您将再次为ToList
获取和过滤所有相同的项目。请注意,EF Core可以在最终投影中调用自定义函数(即lastSelect
),而无需移动到客户端。EF Core还可以处理通过可翻译代码创建的投影属性的过滤(即不使用GetDescription
等客户端函数的代码)。还要注意,GetDescription(x)
应该会导致获取表的所有列。如果Linq既不将GetDescription(x)加载到内存中,也不向数据库发送请求,那么它如何知道如何处理它?
将使用LINQ-to-Objects,它将只调用方法
检查DB调用时-仅进行单个调用
我想知道你是怎么做到的,你看到了什么查询。尝试启用simple logging for EF Core并检查日志。
你能做的很大程度上取决于
GetDescription
。你可以提供一个user defined function mapping,使它可以翻译。或者做一些类似的事情:字符串
我也建议检查一下:
民主联盟
只需内联
GetDescription
函数:型
l7mqbcuq2#
您的问题是调用
GetDescription
。EF Core无法翻译您的自定义函数。使用
AsEnumerable
是一个坏主意,它将使它从服务器检索 * 所有 * 行并在内存中过滤它。相反,您需要让它返回
IQueryable
字符串
然后,您可以:
型
请注意,调用
.Count
和.ToList
效率很低。如果需要,请将其拉入列表,然后检查计数和列表。