【发布时间】: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