我有这样的代码(简化):
var query = _context.Products
.Where(x => x.IsActive)
.Select(x => new ProductDto
{
Id = x.Id,
Name = x.Name,
Description = GetDescription(x)
});
// Builing predicates (using LinqKit)
var predicate = PredicateBuilder.New<T>();
query = query.Where(predicate);
var count = await query.CountAsync();
var list = await query.ToListAsync();
当我按 ID、名称过滤时,它会起作用。当我想按描述过滤时,就会出现问题,例如:
query = query.Where(x => x.Description.Contains("winter"));
这里抛出了一个异常 - LinQ 无法将其转换为 SQL - 可以理解。
你认为我能如何处理这个问题?这就是我构建过滤/排序等的方式,我想保持这种方式。如何按使用无法转换为 SQL 的 C# 方法的属性进行筛选/排序?
读完这篇高效查询 EF Core后,我有了一个想法,也许这就是要走的路。
我尝试这样做:
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 级别进行过滤的能力(但我也许能够分离在哪个阶段应用哪些过滤器),但我不确定它是如何工作的。
我想我理解缓冲和流式传输之间的区别 - 这里我不会将所有结果加载到内存中 - 我希望流式传输发生。检查数据库调用时 - 仅进行单个调用(使用
.Count()
和 .ToList()
),这听起来不错。
这是解决问题的有效方法吗?
.AsEnumerable()
之后过滤到底是如何工作的?如果 LinQ 既不将其加载到内存中也不向数据库发送请求,它如何知道如何处理 GetDescription(x)
?
大家干杯
您使用
.AsEnumerable()
采取的方法可行,但有一些重要的注意事项。
当您使用
.AsEnumerable()
时,您实际上是从使用 IQueryable LINQ 提供程序(与数据库交互)切换到 IEnumerable LINQ-to-Objects(在内存中工作)。这意味着调用 .AsEnumerable()
后,后续操作(如 Where
)都是在客户端的内存中执行,而不是翻译成 SQL 在数据库服务器上执行。
其一般工作原理如下:
之前
.AsEnumerable()
: LINQ 表达式被转换为 SQL 并在数据库服务器上执行。这可以实现高效的查询、过滤和排序。
.AsEnumerable()
之后: 剩余的 LINQ 表达式在客户端内存中执行。如果您处理大型数据集,这可能会降低效率,因为它将所有数据从数据库服务器提取到内存中。
关于您关于
GetDescription(x)
的具体问题,行为取决于 GetDescription(x)
的复杂性:
如果
GetDescription(x)
是一个可以转换为 SQL 的简单方法(即,它是 LINQ 提供程序支持的方法的一部分),那么在使用 IQueryable 时它将成为 SQL 查询的一部分。
如果
GetDescription(x)
比较复杂,无法翻译成SQL,使用.AsEnumerable()
会将数据带入内存,然后对内存中的每个对象执行GetDescription(x)
。
您对失去 SQL 级别过滤能力的担忧是有道理的。使用
.AsEnumerable()
时,您可能会失去数据库查询优化的优势,并且性能可能会受到影响,尤其是对于大型数据集。
如果您想要对无法转换为 SQL 的属性进行筛选或排序,您可能需要考虑折衷方案。您可以使用数据库功能尽可能多地进行筛选和排序,然后根据需要使用 LINQ-to-Objects 在内存中执行其他筛选或排序。
总之,您的方法是可行的,但要谨慎对待性能影响,特别是在处理大型数据集时。这通常是 LINQ 的表达能力和数据库查询的效率之间的权衡。