如何在EF Core中对子关联集合对象创建动态OrderBy排序语句
解决方案
场景1:仅需动态切换外层Item集合的排序升降
你现有的OrderBySortDynamic扩展方法可以直接使用,不需要额外修改,直接把你原来的排序键选择器传入即可:
// sortDescending由前端传入的SortDirection转换,比如降序为true bool sortDescending = false; var result = await _context.Items.Include(a => a.ItemProductTypes) .ThenInclude(a => a.ProductType) .OrderBySortDynamic(a => a.ItemProductTypes.OrderBy(b => b.ProductType.ProductTypeName) .Select(c => c.ProductType.ProductTypeName) .FirstOrDefault(), sortDescending ).ToListAsync();
场景2:需要动态切换子导航属性ItemProductTypes内部的排序方向
由于EF Core解析LINQ表达式树时,无法识别自定义的IEnumerable扩展方法,直接写嵌套的自定义扩展会抛出「无法翻译」异常,因此需要通过构造标准表达式树的方式生成动态排序键:
首先添加动态生成键选择器的辅助方法:
using System.Linq.Expressions; public static Expression<Func<Item, string>> GetProductTypeSortKeySelector(bool innerSortDesc) { var parameter = Expression.Parameter(typeof(Item), "a"); // 访问Item.ItemProductTypes属性 var itemProductTypesProp = Expression.Property(parameter, nameof(Item.ItemProductTypes)); // 构造内部排序的键选择器:b => b.ProductType.ProductTypeName var innerParam = Expression.Parameter(typeof(ItemProductType), "b"); var productTypeProp = Expression.Property(innerParam, nameof(ItemProductType.ProductType)); var productTypeNameProp = Expression.Property(productTypeProp, nameof(ProductType.ProductTypeName)); var innerKeySelector = Expression.Lambda<Func<ItemProductType, string>>(productTypeNameProp, innerParam); // 选择OrderBy/OrderByDescending方法 var innerSortMethodName = innerSortDesc ? nameof(Enumerable.OrderByDescending) : nameof(Enumerable.OrderBy); var innerSortMethod = typeof(Enumerable) .GetMethods() .First(m => m.Name == innerSortMethodName && m.GetParameters().Length == 2) .MakeGenericMethod(typeof(ItemProductType), typeof(string)); var innerSortCall = Expression.Call(innerSortMethod, itemProductTypesProp, innerKeySelector); // 构造Select方法调用:.Select(c => c.ProductType.ProductTypeName) var selectMethod = typeof(Enumerable) .GetMethods() .First(m => m.Name == nameof(Enumerable.Select) && m.GetParameters().Length == 2) .MakeGenericMethod(typeof(ItemProductType), typeof(string)); var selectCall = Expression.Call(selectMethod, innerSortCall, innerKeySelector); // 构造FirstOrDefault方法调用 var firstOrDefaultMethod = typeof(Enumerable) .GetMethods() .First(m => m.Name == nameof(Enumerable.FirstOrDefault) && m.GetParameters().Length == 1) .MakeGenericMethod(typeof(string)); var firstOrDefaultCall = Expression.Call(firstOrDefaultMethod, selectCall); return Expression.Lambda<Func<Item, string>>(firstOrDefaultCall, parameter); }
之后调用时组合你原有的动态排序扩展即可:
bool outerSortDesc = 外层排序是否降序; bool innerSortDesc = 子集合内部排序是否降序; var sortKeySelector = GetProductTypeSortKeySelector(innerSortDesc); var result = await _context.Items.Include(a => a.ItemProductTypes) .ThenInclude(a => a.ProductType) .OrderBySortDynamic(sortKeySelector, outerSortDesc) .ToListAsync();
说明
自定义IEnumerable扩展方法无法被EF Core识别的原因:EF Core需要将LINQ查询翻译为SQL语句,仅支持解析标准的LINQ方法调用,自定义扩展方法属于运行时执行逻辑,无法被表达式树解析器识别,因此必须通过构造标准表达式树的方式实现嵌套排序的动态切换。
内容的提问来源于stack exchange,提问作者Susil kumar
相关产品推荐
相关产品推荐

