如何基于多语言翻译值对Order列表进行Linq排序?
问题描述
我拥有一个Order列表,其中Order包含int类型的Status属性。每个状态对应不同语言的翻译文本,我希望根据选中语言的翻译值而非状态的数字值对列表进行排序,请问最佳实践是什么?
我尝试新增TranslatedStatusValue属性存储翻译值并基于它排序,但当前实现无效(TranslatedStatusValue始终为空),也希望了解不使用额外属性的实现方式。
现有代码定义
翻译实体与订单实体
public record OrderTranslation { public string OrderStatus { get; set; } public string StatusDescription { get; set; } public Language Language { get; set; } } public record Order { public int? Id { get; set; } public int Status { get; set; } // 新增属性用于存储翻译值并尝试基于它排序 public string TranslatedStatusValue { get; set;} }
排序过滤函数
public async Task<FilterResult> FilterAsync(FilterRequest filterRequest, List<string> filterProperties, Language selectedLanguage) { var orderTranslations = dataContext .OrderTranslations .Where(ot => ot.Language == selectedLanguage) .ToList(); var orders = dataContext.Orders.AsNoTracking(); foreach (var order in orders) { var description = orderTranslations .Single(x => x.OrderStatus == serviceContract.Status) .StatusDescription; serviceContract.TranslatedValue = description; } // 此处TranslatedValue始终为空 // 这种方式无效,我希望基于翻译值排序,有没有不使用额外属性的实现方式? IQueryable<ServiceContractOrder> query = orders .OrderBy("TranslatedStatusValue", filterRequest.IsSortAscending) .WhereMatchesFilter(filterRequest, filterProperties); result.FilterHits = await query .Skip(filterRequest.ItemsToSkip()) .Take(filterRequest.ItemsPerPage) .Cast<object>() .ToListAsync(); result.TotalCount = await query.CountAsync(); result.ObjectType = typeof(Order).AssemblyQualifiedName; result.FilteredProperties = filterProperties; }
解决方案
问题根源
你当前代码无效的核心原因:orders是IQueryable<Order>,遍历操作不会触发EF Core的查询执行(延迟加载特性),后续调用OrderBy时依然基于数据库原生查询,而TranslatedStatusValue并非数据库字段,因此排序时该属性值为空。
最佳实践(两种方案)
方案1:数据库层面关联翻译表排序(推荐)
直接在EF Core查询中关联OrderTranslations表,基于翻译后的StatusDescription完成排序,无需额外属性,所有逻辑在数据库层执行,性能更优:
public async Task<FilterResult> FilterAsync(FilterRequest filterRequest, List<string> filterProperties, Language selectedLanguage) { // 关联订单与对应语言的翻译表,注意类型匹配:Order.Status是int,OrderTranslation.OrderStatus是string,需转类型对齐 var query = dataContext.Orders.AsNoTracking() .Join( dataContext.OrderTranslations.Where(ot => ot.Language == selectedLanguage), order => order.Status.ToString(), translation => translation.OrderStatus, (order, translation) => new { Order = order, TranslatedStatus = translation.StatusDescription }) // 按翻译值排序 .OrderBy(x => x.TranslatedStatus) // 升序用OrderBy,降序用OrderByDescending .WhereMatchesFilter(filterRequest, filterProperties) .Select(x => x.Order); // 最终返回Order实体,也可投影到带翻译值的DTO // 分页查询 result.FilterHits = await query .Skip(filterRequest.ItemsToSkip()) .Take(filterRequest.ItemsPerPage) .Cast<object>() .ToListAsync(); result.TotalCount = await query.CountAsync(); result.ObjectType = typeof(Order).AssemblyQualifiedName; result.FilteredProperties = filterProperties; return result; }
注意:如果
OrderTranslation.OrderStatus存储的是枚举字符串,需确保和Order.Status的枚举ToString()结果一致;如果是数字字符串,转类型时要避免格式错误。
方案2:内存中排序(适合小数据量场景)
如果订单数据量不大,可以先将数据加载到内存,再基于翻译字典排序:
public async Task<FilterResult> FilterAsync(FilterRequest filterRequest, List<string> filterProperties, Language selectedLanguage) { // 加载翻译数据并转为字典,键为状态数字,值为翻译文本 var translationDict = await dataContext.OrderTranslations .Where(ot => ot.Language == selectedLanguage) .ToDictionaryAsync(ot => int.Parse(ot.OrderStatus), ot => ot.StatusDescription); // 先加载过滤后的订单到内存 var orders = await dataContext.Orders.AsNoTracking() .WhereMatchesFilter(filterRequest, filterProperties) .ToListAsync(); // 按翻译值排序 var sortedOrders = filterRequest.IsSortAscending ? orders.OrderBy(o => translationDict[o.Status]) : orders.OrderByDescending(o => translationDict[o.Status]); // 分页处理 result.FilterHits = sortedOrders .Skip(filterRequest.ItemsToSkip()) .Take(filterRequest.ItemsPerPage) .Cast<object>() .ToList(); result.TotalCount = orders.Count; result.ObjectType = typeof(Order).AssemblyQualifiedName; result.FilteredProperties = filterProperties; return result; }
补充:修复原方案的问题
如果坚持使用TranslatedStatusValue属性,需先将订单加载到内存再赋值,之后再排序:
public async Task<FilterResult> FilterAsync(FilterRequest filterRequest, List<string> filterProperties, Language selectedLanguage) { var translationDict = await dataContext.OrderTranslations .Where(ot => ot.Language == selectedLanguage) .ToDictionaryAsync(ot => int.Parse(ot.OrderStatus), ot => ot.StatusDescription); // 先加载过滤后的订单到内存 var orders = await dataContext.Orders.AsNoTracking() .WhereMatchesFilter(filterRequest, filterProperties) .ToListAsync(); // 为每个订单赋值翻译值 foreach (var order in orders) { order.TranslatedStatusValue = translationDict[order.Status]; } // 基于内存中的属性排序 var sortedOrders = filterRequest.IsSortAscending ? orders.OrderBy(o => o.TranslatedStatusValue) : orders.OrderByDescending(o => o.TranslatedStatusValue); // 分页处理 result.FilterHits = sortedOrders .Skip(filterRequest.ItemsToSkip()) .Take(filterRequest.ItemsPerPage) .Cast<object>() .ToList(); result.TotalCount = orders.Count; result.ObjectType = typeof(Order).AssemblyQualifiedName; result.FilteredProperties = filterProperties; return result; }
内容的提问来源于stack exchange,提问作者trap
相关产品推荐
相关产品推荐

