ASP.NET 具有作用域依赖性的 CORE Automapper 配置文件

ASP.NET CORE Automapper Profile with Scoped Dependency

在我的用例中,我需要根据上下文映射 属性。 请记住,我通过调用 ProjectTo 函数将 Automapper 与 Entity Framework 一起使用。所以很遗憾,自定义值解析器是没有选择的。

简单示例:

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; }
}

基本上Destination应该包含自己评论的数量。 使用 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 个周期。

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

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

感谢@lucian-bargaoanu 在评论中提供link: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()))
        ;
    }
}

这样我就可以在处理程序 class 中注入我的 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);
    }
}