如何将值传递给 AutoMapper Profile 投影?

How to pass value to AutoMapper Profile projection?

我们可以查看支持工单列表。

如果当前登录的用户已回复支持票,我们现在需要显示一个额外的图标。 这意味着我们必须向我们的投影添加一个额外的 属性,我们不能只过滤当前用户的列表。

现在我们的想法是将这个额外的 属性 映射到配置文件。但是我们不知道如何将当前用户 ID 传递给配置文件。

在它可能看起来像这样

public class SupportTicketProjectionProfile : AutoMapper.Profile
{
    public SupportTicketProjectionProfile()
    {
        CreateMap<SupportTicketEntity, SupportTicketProjection>()
            .ForMember(p => p.CurrentUserHasReplied, 
               opt => opt.MapFrom(e=>e.Editors.Any(editor=> editor.Id == **passedIdValue**))

但是如何传递userId?

我们发现我们可以将 Dictionary 传递给 ProjectTo 方法。 Bot 我们找不到任何文档如何在 AutoMapper 配置文件中访问此词典。

感谢任何提示。

地图

你可以使用解析上下文字典,你在映射对象时传递。这比仅在映射后分配一个字段更难维护和调试。如果在 source class 之外分配了新字段,您还可以考虑为 2 个 source classes 创建两个映射。话虽如此,这是代码:

映射class:

Mapper.CreateMap<Src, Dest>()
    .ForMember(dest => dest.Param
        , opt => opt.ResolveUsing((src, dest, destMember, resContext)
            => src.Param = resContext.Items["item"]));

您还可以将字典传递给嵌套对象映射为。映射器对象和字典可通过解析上下文访问。

Mapper.CreateMap<Src, Dest>()
    .ForMember(dest => dest.Param
        , opt => opt.ResolveUsing((src, dest, destMember, resContext)
            => resContext.Mapper.Map<NestedDestClass>(src, opts =>
                {
                    opts.Items["item"] = resContext.Items["item"];
                }
            )));

映射对象

var dto = Mapper.Map<Dest>(item, 
    opts => opts.Items["item"] = "somevalue");

这是一个fiddle

ProjectTo

您可以在使用 ProjectTo 映射时使用字典。为此,您必须在我们创建运行时参数的地方指定映射定义,然后在我们提供它的执行时指定映射定义。

为了创建带有运行时参数的映射定义,我们“伪造”了一个包含命名局部变量的闭包。我们无法访问我们在配置中执行期间使用的“真实”值,因此我们创建了一个 stand-in 仍然为我们创建了一个闭包。构建的底层表达式树识别此外部输入并创建一个占位符参数以在运行时提供。

var configuration = new MapperConfiguration(cfg => 
    string name = null;
    cfg.CreateMap<Dest, Source>()
        .ForMember(m => m.CurrentUserName, opt => opt.MapFrom(src => name))
    ));

执行投影时,我们可以使用字典提供参数值(documentation 中提供了带有 annonymus 对象的示例)

data.ProjectTo<OrderLineDTO>(configuration, new Dictionary<string, object> {["name"] = "test"}).ToList();

当底层 LINQ 提供程序执行投影时,表达式中会替换正确的运行时值,让您可以在投影中使用 per-map 运行时值。您可以在任何运行表达式的配置选项中使用这些运行时值

我终于找到诀窍了:创建一个临时映射模型。

public class SupportTicketUserContextExpressionModel
{
    public SupportTicketEntity SupportTicket { get; set; }
    public bool UserHasReplied { get; set; }
}

借助(让我们命名)表达式模型,您可以注入额外的 sub-query 结果:

var query = _dbContext.SupportTickets
    .Select(ticket => new
    {
        ticket,
        hasReplied = ticket.Editors.Any(e => e.UserId == userId)
    })
    .Select(temp => new SupportTicketUserContextExpressionModel
    {
        SupportTicket = temp.ticket,
        HasReplied = temp.hasReplied
    }));

现在您需要一个新的附加配置文件:

CreateMap<SupportTicketUserContextExpressionModel, SupportTicketUserView>()
     .ForMember(p => p.TicketId,
         opt => opt.MapFrom(e => e.SupportTicket.Id))

     // more here

     .ForMember(p => p.UserHasReplied,
         opt => opt.MapFrom(e => e.HasReplied));

并且您可以使用您的 ProjectTo 的配置文件:

.ProjectTo<SupportTicketUserView>(_mapper.ConfigurationProvider);

谢谢,本!