首页 > 解决方案 > 具有范围依赖关系的 ASP.NET CORE Automapper 配置文件

问题描述

在我的用例中,我需要根据上下文映射一个属性。请记住,我通过调用ProjectTo函数将 Automapper 与实体框架一起使用。因此,可悲的是,自定义值解析器是没有选择的。

简单的例子:

public class Comment 
{
  public int Id { get;set; }
  public int UserId { get;set; }
  ...
}

public class Source 
{
  public int Id { get; set; }
  public IEnumerable<Comment> Comments  { get; set; }
}

public class Destination 
{
  public int Id { get; set; }
  public int NumOwnComments { get; set; }
}

基本上,目的地应该包含自己的评论数量。当前用户是使用ICurrentUserService带有属性的动态解析的UserId

我已经通过以下方式解决了这个问题:

Startup.cs我添加了一个Transient映射器/配置。

  services.AddTransient(provider => new MapperConfiguration(cfg => { cfg.AddProfile(new MappingProfile(provider.GetService<ICurrentUserService>())); })
        .CreateMapper());

然后在 MappingProfile 中,我按以下方式创建了映射:

public class MappingProfile : Profile {

    public MappingProfile(ICurrentUserService currentUserService) {
        CreateMap<Source, Destination>()
            .ForMember(vm => vm.NumOwnComments, opts => opts.MapFrom(s => s.Comments.Count(c => c.UserId == currentUserService.UserId))
        ;
    }
}

虽然这可行,但将映射器配置作为瞬态/作用域依赖项并不是很好。每个请求都会创建这个映射器,这会消耗大量内存和 CPU 周期。

有没有更优雅的方法,比如将映射配置文件创建为单例,然后在作用域/瞬态映射器中执行它?

标签: c#asp.net-coredependency-injectionentity-framework-coreautomapper

解决方案


我将回答我自己的问题以供将来参考:

感谢@lucian-bargaoanu 在评论中提供链接:https ://docs.automapper.org/en/latest/Queryable-Extensions.html#parameterization

动态参数可以通过 ProjectTo 方法传递。

我最终为我的所有 DTO 投影创建了扩展方法

public static class DestinationProjection 
{
    public static IQueryable<Destination> ProjectToDestination(IQueryable source, IConfiguration configuration, int currentUserId) {
        return source.ProjectTo<Destination>(configuration, new { currentUserId });
    }
}

在映射中我使用了这个参数

public class MappingProfile : Profile 
{
    public MappingProfile() {
        int? currentUserId = null;
        CreateMap<Source, Destination>()
            .ForMember(vm => vm.NumOwnComments, opts => opts.MapFrom(s => s.Comments.Count(c => c.UserId == currentUserId.GetValueOrDefault()))
        ;
    }
}

这样我可以ICurrentUserService在处理程序类中注入我的。

public class DestinationListQueryHandler  
{
    public DestinationListQueryHandler(IMapper mapper, IDbContext dbContext, ICurrentUserService currentUserService) 
    {
         // field initialization logic
    }

    public async Task<IEnumerable<Destination>> Handle(CancellationToken cancellationToken)
    {
        return await dbContext.Sources.ProjectToDestination(mapper.ConfigurationProvider, currentUserId).ToListAsync(cancellationToken);
    }
}

推荐阅读