linq EF Core -C#方法上的过滤查询(缓冲与流)

mcvgt66p  于 9个月前  发布在  C#
关注(0)|答案(2)|浏览(134)

场景

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

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 -首先要确保查询写得很好,因为它可能不是。

nbysray5

nbysray51#

我想我明白缓冲和流的区别了
这里的区别是客户端评估和服务器端评估。
这是解决问题的有效方法吗?
有争议-不。
.AsEnumerable()之后的过滤究竟是如何工作的?
之前的一切都将转换为SQL并在数据库端执行,之后的一切都将在客户端处理(在存储器中)即对于Count所有匹配的行将被提取到存储器中,GetDescription将被调用,然后过滤将发生,然后项目将被计数。然后您将再次为ToList获取和过滤所有相同的项目。请注意,EF Core可以在最终投影中调用自定义函数(即last Select),而无需移动到客户端。EF Core还可以处理通过可翻译代码创建的投影属性的过滤(即不使用GetDescription等客户端函数的代码)。还要注意,GetDescription(x)应该会导致获取表的所有列。
如果Linq既不将GetDescription(x)加载到内存中,也不向数据库发送请求,那么它如何知道如何处理它?
将使用LINQ-to-Objects,它将只调用方法
检查DB调用时-仅进行单个调用
我想知道你是怎么做到的,你看到了什么查询。尝试启用simple logging for EF Core并检查日志。
你能做的很大程度上取决于GetDescription。你可以提供一个user defined function mapping,使它可以翻译。或者做一些类似的事情:

var query = _context.Products
   .Where(x => x.IsActive)
   .Where(x => x.DescriptionPartColumn1.Contains("winter") 
       || x.DescriptionPartColumn1.Contains("winter"))
   .Select(x => new ProductDto ...)

字符串
我也建议检查一下:

只需内联GetDescription函数:

var query = _context.Products
   .Where(x => x.IsActive)
   .Select(x => new ProductDto
   {
       //...
       Description = x.ProductType == 0
           ? "This is one Desc"
           :  x.ProductType == 1 
                 ? "This is two Desc" 
                 : string.Empty
   })

l7mqbcuq

l7mqbcuq2#

您的问题是调用GetDescription。EF Core无法翻译您的自定义函数。
使用AsEnumerable是一个坏主意,它将使它从服务器检索 * 所有 * 行并在内存中过滤它。
相反,您需要让它返回IQueryable

public static IQueryable<ProductDto> AsProductDto(this IQueryable<Product> query, int productType)
{
    return query.Select(x =>
        new ProductDto 
        {
            Id = x.Id,
            Name = x.Name,
            Description =
                x.ProductType == 0 ? "someDescrip" :   // use hard-coded literals, do not call functions
                x.ProductType == 1 ? "otherDescrip" :
                ""
        });
}

字符串
然后,您可以:

var query = _context.Products
                    .Where(x => x.IsActive)
                    .AsProductDto();

query = query.Where(x => x.Description.Contains("winter"));


请注意,调用.Count.ToList效率很低。如果需要,请将其拉入列表,然后检查计数和列表。

相关问题