【问题标题】:Linq OrderBy calculated propertyLinq OrderBy 计算属性
【发布时间】:2021-10-02 16:28:47
【问题描述】:

我使用表达式生成器对表数据进行排序和过滤。但是,当属性中有计算时,查询不会执行。

我在这里阅读了类似的帖子,但找不到答案!

例如如果我尝试按“到期”对下面的查询进行排序,则会失败并出现以下错误。

System.InvalidOperationException:LINQ 表达式 'DbSet() .Where(i => i.OrganisationId == __organisationId_0) .OrderBy(i => (Nullable)(DateTime.Now - i.PaymentDueDate).TotalDays)' 无法翻译。以可翻译的形式重写查询,或通过插入对“AsEnumerable”、“AsAsyncEnumerable”、“ToList”或“ToListAsync”的调用显式切换到客户端评估。请参阅https://go.microsoft.com/fwlink/?linkid=2101038 了解更多信息。

我该如何应对这种情况,或者我在这里做错了什么? 谢谢,非常感谢!

方法:

public async Task<PaginatedList<GetInvoiceListDto>> Handle(GetInvoiceListQuery request, CancellationToken cancellationToken)
    {
        var organisationId = _tenantService.GetOrganisationId();
        return await _context.Invoices
            .Include(i => i.InvoiceSaleItems)
            .Where(w => w.OrganisationId == organisationId)
            .Select(y => new GetInvoiceListDto
            {
                Status = y.PaymentDueDate < DateTime.Now ? "Overdue" : "Sent",
                Due = (DateTime.Now - y.PaymentDueDate).TotalDays,
                InvoiceDate = y.InvoiceDate,
                InvoiceNumber = y.InvoiceNumber,
                CustomerName = y.Customer.CustomerName,
                AmountDue = y.InvoiceSaleItems.Sum(yx => yx.Amount) + y.InvoiceSaleItems.SelectMany(ss => ss.InvoiceSaleItemTaxes).Sum(sg => sg.Amount),
            }).PaginatedListAsync(request.PageNumber, request.PageSize, request.SortColumn, request.SortOrder, request.q);

    }

扩展名:

public static Task<PaginatedList<TDestination>> PaginatedListAsync<TDestination>(this IQueryable<TDestination> queryable, int pageNumber, int pageSize, string sortColumn, string sortOrder, string filter)
        => PaginatedList<TDestination>.CreateAsync(queryable, pageNumber, pageSize, sortColumn, sortOrder, filter);


public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageIndex, int pageSize, string sortColumn, string sortOrder, string filter)
    {
        var query = source;
        // query = query.Filter<T>(filter); // commented for simplicity.
        query = query.OrderBy<T>(sortColumn, sortOrder);
        var count = query.Count(); // <= The error is thrown at this line.
        var items = await Task.FromResult (query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList());

        return new PaginatedList<T>(items, count, pageIndex, pageSize);
    }

public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string sortColumn, string direction)
    {
        var methodNameFirst = string.Format("OrderBy{0}", direction.ToLower() == "asc" ? "" : "descending");
        var methodNameContinue = string.Format("ThenBy{0}", direction.ToLower() == "asc" ? "" : "descending");
        ParameterExpression parameter = Expression.Parameter(query.ElementType, "p");
        Expression result = query.Expression;
        var methodName = methodNameFirst;
        foreach (var fields in sortColumn.Split(','))
        {
            Expression memberAccess = null;
            foreach (var property in fields.Split('.'))
            {
                if (string.IsNullOrEmpty(property.Trim()) == false)
                {
                    var newProp = "";
                    if (property.IndexOf(" asc") == -1)
                    {
                        newProp = property.Trim();
                    }
                    else
                    {
                        newProp = property.Substring(0, property.IndexOf(" asc")).Trim();
                    }
                    try
                    {
                        memberAccess = MemberExpression.Property(memberAccess ?? (parameter as Expression), newProp);
                    }
                    catch (Exception e)
                    {
                    }
                }
            }
            LambdaExpression orderByLambda = Expression.Lambda(memberAccess, parameter);
            result = Expression.Call(
                typeof(Queryable),
                methodName,
                new[] { query.ElementType, memberAccess.Type },
                result,
                Expression.Quote(orderByLambda));
            methodName = methodNameContinue;
        }
        return query.Provider.CreateQuery<T>(result);
    }

【问题讨论】:

  • C# 表达式可以表示与可以执行的操作相比的操作的超集。您正在尝试做一些无法在数据库上执行但可以在内存中执行的操作。正如错误消息中明确阐述的那样,您的选择是重写您的查询以不使用您的自定义计算,或者在计算之前将结果放入内存,并使用纯 C# 代码进行计算。
  • @Enigmativity,谢谢!如何在内存中执行查询并仍然使用分页?你能给我举个例子吗?
  • 或者我如何重写查询以便它可以在数据库级别执行?
  • 您必须将所有数据放入内存,进行计算,然后分页。现在,这可能会占用太多内存,因此您可能需要引入最少的数据来确定哪些 id 在哪个页面中,然后返回数据库获取实际数据。
  • 我无法告诉您如何重写您的查询。您只需要删除有问题的代码,然后找出可以在数据库上进行的计算。这不一定是一件容易的事。

标签: linq asp.net-core


【解决方案1】:

不要使用(DateTime.Now - y.PaymentDueDate).TotalDays,而是尝试用 EF Core 可以转换为 SQL 的东西替换:

return await _context.Invoices
    .Where(w => w.OrganisationId == organisationId)
    .Select(y => new GetInvoiceListDto
    {
        Status = y.PaymentDueDate < DateTime.Now ? "Overdue" : "Sent",
        Due = EF.Functions.DateDiffDay(DateTime.Now, y.PaymentDueDate),
        InvoiceDate = y.InvoiceDate,
        InvoiceNumber = y.InvoiceNumber,
        CustomerName = y.Customer.CustomerName,
        AmountDue = y.InvoiceSaleItems.Sum(yx => yx.Amount) + y.InvoiceSaleItems.SelectMany(ss => ss.InvoiceSaleItemTaxes).Sum(sg => sg.Amount),
    }).PaginatedListAsync(request.PageNumber, request.PageSize, request.SortColumn, request.SortOrder, request.q);

请注意,我已删除 Include,如果您有 Select,则不需要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多