linq 使用Automapper将同一实体Map(在内存中)或投影(SQL)到模型

xwbd5t1u  于 2023-04-27  发布在  其他
关注(0)|答案(1)|浏览(134)

我正在做一个POC,以确保我们将在重写我们的一个应用程序时实现的不同概念将满足我们的需求。我遇到了一个问题,我需要不同的行为来执行内存或SQL查询。
此外,我还得到了这些实体(只包括相关的属性):

public class ProductEntity
{
    public required int ProductNumber { get; set; }
    public required ICollection<ProductDescriptionEntity>? Descriptions { get; set; }
}

public class ProductDescriptionEntity
{
    public required int ProductNumber { get; set; }
    public required string Language { get; set; }
    public string? Description { get; set; }
    public ProductEntity? Product { get; set; }
}

并将其Map到这个模型:

public class ProductWithDescription
{
    public int ProductNumber { get; set; }

    public string? Description { get; set; }
}

请注意,我们希望采用与用户语言相对应的单个描述。
为了解决这个问题,我创建了这个配置文件:

public class ProductMappings : Profile
{
    public ProductMappings()
    {
        CreateMap<ProductEntity, ProductWithDescription>()
            .ForMember(dst => dst.Description,
                       exp => exp.MapFrom(src => GetLocalizedDescription(src)));

        CreateProjection<ProductEntity, ProductWithDescription>()
            .ForMember(dst => dst.Description,
                       exp => exp.MapFrom(src => src.Descriptions!
                                                    .First(description => description.Language == Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName)
                                                    .Description));
    }

    private static string? GetLocalizedDescription(ProductEntity product) =>
        product.Descriptions
               ?.FirstOrDefault(description => Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.Equals(description.Language, StringComparison.InvariantCultureIgnoreCase))
               ?.Description;
}

CreateMap<ProductEntity, ProductWithDescription>()中定义的是在内存中进行查询;用CreateProjection<ProductEntity, ProductWithDescription>()定义的是使用.ProjectTo()进行EF查询;但是我不能同时使用内存和SQL项目。
这里和here声明Automapper不支持Map和项目的不同配置。
我这样做是为了在缓存没有加载时回退到SQL查询:

public async Task<ICollection<TModel>> GetAllAsync<TModel>(Expression<Func<TEntity, bool>> predicate,
                                                           Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>? orderBy = null)
{
    if (_cache.RunningState != CacheRunningState.Running)
    {
        // fallback to repository if the cache isn't available (yet)
        return await _repository.GetAllAsync<TModel>(predicate, orderBy);
    }

    var filteredEntities = _cache.Entities
                                 .Where(predicate);

    filteredEntities = orderBy == null
        ? filteredEntities
        : orderBy(filteredEntities);

    return _mapper.Map<ICollection<TModel>>(filteredEntities);
}

你看到我的问题的解决方案了吗?

vaj7vani

vaj7vani1#

在@IvanStoev的帮助下(见评论中的讨论),我已经能够定义一个Map,它适用于内存Map和SQL投影:

CreateMap<ProductEntity, ProductWithDescription>()
  .ForMember(dst => dst.Description,
             exp => exp.MapFrom(src => src.Descriptions
                                          .Where(d => Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToUpperInvariant() == d.Language)
                                          .Select(d => d.Description)
                                          .FirstOrDefault()));

我不确定我是否会保留这个概念,因为在更复杂的场景中对齐Map和投影可能会很乏味/不可能。

相关问题