Automapper指的是在自定义成员映射中引用现有子类型映射。

3
我的数据库实体如下(只保留相关信息):
public class Task
{
    public Guid? StatusId { get; set; }
    public Status Status { get; set; }

    public DateTime? StatusLastModified { get; set; }
}

public class Status
{
    public string Name { get; set; }
    // several other properties redacted
}

任务最初没有状态(此时StatusLastModified也为null),一旦分配了第一个状态,任务将始终具有状态(此时StatusLastModified也不为null)。

为了回答这个问题,您可以假设这些属性始终都是null或非null,我不需要考虑其中一个为null而另一个不是的异常情况。

对于我的DTO,我希望将其映射到以下结构:

public struct TaskDto
{
    public TaskStatusDto? Status { get; set; }
}

public struct TaskStatusDto
{
    public DateTime Timestamp { get; set; } // maps from Task.StatusLastModified 
    public StatusDto Status { get; set; } // maps from Task.Status
}

public struct StatusDto
{
    public string Name { get; set; }
    // and all the other properties from the Status entity
}

这里的目标是,只有当实际状态存在时,我的TaskDto才有一个基础的TaskStatusDto,否则它应为null。这很有帮助,因为它消除了不断检查状态对象和上次修改日期的需要。
然而,我在配置Automapper中遇到了困难。我已经创建了StatusStatusDto之间的映射:
configuration
     .CreateMap<Status, StatusDto>();

我的真实映射更加复杂,但已被证明可以正常工作,因此您可以假设这是有效且可行的映射。

我面临的问题是,在创建 TaskTaskDto 之间的自定义映射时,我如何引用此映射。目前我已尝试以下方法:

configuration
    .CreateMap<Task, TaskDto>()
    .ForMember(
            dto => dto.Status,
            opt => opt.MapFrom(entity => 
                entity.Status == null
                    ? null as TaskStatusDto?
                    : new TaskStatusDto()
                    {
                        TimeStamp = entity.StatusLastModified.Value,
                        Status = // ??? make me a StatusDto from entity.Status
                    }
        ));

我不知道如何填写评论部分。我可以访问原始实体 (entity.Status),但我不知道如何告诉Automapper将此对象转换为StatusDto,并按照它应该已知的映射进行转换。
我的方法感觉比必须的还要矫揉造作,但如果在源数据中没有实际实体支持,我不知道如何引入这个中介的TaskStatusDto对象。

一个小注释,不确定是否相关:我正在使用Heroic.Automapper,这意味着TaskDtoStatusDto映射被配置在不同的位置(即在TaskDtoStatusDto类内部),Heroic框架将在运行时组合所有这些映射。

public class TaskDto : IMapFrom<Task>, IHaveCustomMappings
{
     public void CreateMappings(IMapperConfigurationExpression configuration)
     {
         // mapping from Task to TaskDto
     }
}

public class StatusDto : IMapFrom<Status>, IHaveCustomMappings
{
     public void CreateMappings(IMapperConfigurationExpression configuration)
     {
         // mapping from Status to StatusDto
     }
}

然而据我所知,设置地图本身并不是英雄特定的,应该纯粹使用Automapper语法。

研究context.Mapper - Lucian Bargaoanu
@LucianBargaoanu:你所指的“context”具体是什么? - Flater
没关系 :) 只需创建一个从 StatusTaskStatusDto? 的映射,并删除 MapFrom - Lucian Bargaoanu
@LucianBargaoanu:那么我该如何访问Task.StatusLastModified,因为这是TaskStatusDto.Timestamp的源数据? - Flater
你可以将它传递到 context.Items 中。 - Lucian Bargaoanu
@LucianBargaoanu:回到我的最初问题:你所提到的“上下文”具体是什么?一个带有解释的实际答案会更好,而不是在评论区里玩20个问题。我怀疑你知道解决方案并且应该得到积分,但是(无意中)滴水般地透露它并不是有效的。 - Flater
1个回答

2

我认为更好的解决方案是创建自己的CustomResolver

public class TaskStatusDtoResolver : IValueResolver<Task, TaskDto, TaskStatusDto?>
{
    public TaskStatusDto? Resolve(Task source, TaskDto destination, TaskStatusDto? member, ResolutionContext context)
    {
        if (source.Status == null)
        {
            return null;
        }

        return new TaskStatusDto
        {
            Status = context.Mapper.Map<StatusDto>(source.Status),
            Timestamp = source.StatusLastModified.Value
        };
    }
}

同时您可以使用以下配置:

cfg.CreateMap<Status, StatusDto>();
cfg.CreateMap<Task, TaskDto>()
    .ForMember(dest => dest.Status, opt => opt.MapFrom<TaskStatusDtoResolver>());

在此处查看工作示例


或者,您可以在配置中定义所有映射:

var configuration = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Status, StatusDto>();
    cfg.CreateMap<Task, TaskDto>().ForMember(
         dest => dest.Status,
         src => src.MapFrom((task, taskDto, member, context) =>
         {
             return task.Status == null ? null as TaskStatusDto? : new TaskStatusDto()
             {
                 Timestamp = task.StatusLastModified.Value,
                 Status = context.Mapper.Map<StatusDto>(task.Status)
             };
         }
     ));
});

那个最后的代码片段正是我一直在寻找的。感谢你的帮助! :) (当我能够分配时,奖金就会到来) - Flater
谢谢,很高兴能帮助到您。 - Hooman Bahreini

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接