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

基于嵌套列表的C# LINQ高效筛选及AutoMapper映射问题

嘿,我来给你拆解这个需求的具体实现方案,分步骤来很清晰:

1. 数据筛选逻辑实现

首先咱们需要从数据库中筛选出包含指定语言翻译的Category,并且只保留该语言的翻译项。用EF Core的LINQ查询来实现最方便:

// 假设你有DbContext,里面包含Categories、CategoryTranslations、Languages三个DbSet
public async Task<List<Category>> GetCategoriesWithSpecifiedLanguageAsync(string languageCode)
{
    return await _dbContext.Categories
        // 关联翻译表和语言表
        .Include(c => c.CategoryTranslations)
            .ThenInclude(ct => ct.Language)
        // 筛选出存在指定语言翻译的分类
        .Where(c => c.CategoryTranslations.Any(ct => ct.Language.Code == languageCode))
        // 投影只保留目标语言的翻译,避免加载多余数据
        .Select(c => new Category
        {
            Id = c.Id,
            // 映射Category本身的其他属性,比如排序值、状态等
            CategoryTranslations = c.CategoryTranslations
                .Where(ct => ct.Language.Code == languageCode)
                .ToList()
        })
        .ToListAsync();
}

这里用Select投影是为了只加载我们需要的翻译数据,比先加载所有翻译再过滤更高效,能减少数据库查询的数据量。

2. AutoMapper配置

接下来配置AutoMapper,把筛选后的Category映射到CategoryOutputModel。先创建一个Profile类:

public class CategoryProfile : Profile
{
    public CategoryProfile()
    {
        CreateMap<Category, CategoryOutputModel>()
            // 映射Category本身的基础属性
            .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
            // 映射翻译内容——因为筛选阶段已经确保每个分类都有目标语言的翻译,直接取第一个即可
            .ForMember(dest => dest.CategoryName, opt => opt.MapFrom(src => src.CategoryTranslations.FirstOrDefault()!.Name))
            .ForMember(dest => dest.CategoryDesc, opt => opt.MapFrom(src => src.CategoryTranslations.FirstOrDefault()!.Description));
            // 其他需要映射的输出模型属性按需添加
    }
}

注:如果担心空引用问题,可以加个空判断(比如用??给默认值),不过因为我们在筛选阶段已经过滤掉了没有目标语言翻译的分类,所以FirstOrDefault()不会返回null,这里用!抑制空警告就好。

3. 完整调用流程

在业务逻辑层,把筛选和映射的逻辑结合起来:

private readonly IMapper _mapper;
private readonly ICategoryRepository _categoryRepository;

public CategoryService(IMapper mapper, ICategoryRepository categoryRepository)
{
    _mapper = mapper;
    _categoryRepository = categoryRepository;
}

public async Task<List<CategoryOutputModel>> GetCategoriesForLanguageAsync(string languageCode)
{
    var filteredCategories = await _categoryRepository.GetCategoriesWithSpecifiedLanguageAsync(languageCode);
    return _mapper.Map<List<CategoryOutputModel>>(filteredCategories);
}
4. 额外优化建议
  • 如果Language表的Code是唯一约束,可以提前查询出LanguageId,用ID来筛选翻译,比字符串匹配更高效:
    var targetLanguage = await _dbContext.Languages.FirstOrDefaultAsync(l => l.Code == languageCode);
    if (targetLanguage == null)
    {
        // 处理语言不存在的情况,比如返回空列表或抛出异常
        return new List<Category>();
    }
    // 后续用targetLanguage.Id来筛选CategoryTranslations
    
  • 可以把筛选逻辑封装成可复用的扩展方法,方便在其他地方调用。
  • 如果用的是AutoMapper 9+,可以直接用ProjectTo在数据库查询阶段完成映射,进一步优化性能:
    return await _dbContext.Categories
        .Where(c => c.CategoryTranslations.Any(ct => ct.Language.Code == languageCode))
        .ProjectTo<CategoryOutputModel>(_mapper.ConfigurationProvider, new { languageCode })
        .ToListAsync();
    
    对应的Profile需要调整为支持传递参数:
    CreateMap<Category, CategoryOutputModel>()
        .ForMember(dest => dest.CategoryName, opt => opt.MapFrom((src, dest, _, context) => 
            src.CategoryTranslations.First(ct => ct.Language.Code == context.Items["languageCode"] as string).Name));
    

内容的提问来源于stack exchange,提问作者Matthew Flynn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:11:35