我的数据库实体如下所示(已编辑为相关信息):
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 也为空),一旦分配了第一个状态,任务将始终具有状态(并且 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 中配置此映射。我已经创建了 Status
和 StatusDto
之间的映射:
configuration
.CreateMap<Status, StatusDto>();
我的真实映射更复杂,但它也被证明是有效的,因此您可以假设这是一个有效且有效的映射。
我面临的问题是在 Task
和 TaskDto
之间创建自定义映射时如何引用此映射。我目前已尝试以下操作:
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 ,这意味着 TaskDto
和 StatusDto
映射被配置在不同的位置(即在 TaskDto
和 StatusDto
内部)类),Heroic 框架将在运行时组合所有这些 map 。
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
}
}
但是,据我了解, map 本身的设置并不是英雄特定的,应该是纯粹的 Automapper 语法。
最佳答案
我认为更干净的解决方案是创建您自己的 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)
};
}
));
});
关于c# - Automapper引用自定义成员映射中现有的子类型映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60225687/