EF Core - 在 C# 方法上过滤查询(缓冲与流式传输)

问题描述 投票:0回答:1

场景:

我有这样的代码(简化):

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)

大家干杯

c# linq entity-framework-core filtering asenumerable
1个回答
0
投票

您使用

.AsEnumerable()
采取的方法可行,但有一些重要的注意事项。

当您使用

.AsEnumerable()
时,您实际上是从使用 IQueryable LINQ 提供程序(与数据库交互)切换到 IEnumerable LINQ-to-Objects(在内存中工作)。这意味着调用
.AsEnumerable()
后,后续操作(如
Where
)都是在客户端的内存中执行,而不是翻译成 SQL 在数据库服务器上执行。

其一般工作原理如下:

  1. 之前

    .AsEnumerable()
    LINQ 表达式被转换为 SQL 并在数据库服务器上执行。这可以实现高效的查询、过滤和排序。

  2. .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 的表达能力和数据库查询的效率之间的权衡。

© www.soinside.com 2019 - 2024. All rights reserved.