You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于多语言翻译值对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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 23:18:41