Automapper 引用自定义成员映射中的现有子类型映射
Automapper refer to existing subtype mapping in custom member mapping
我的数据库实体如下所示(已编辑相关信息):
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。这很有用,因为它不需要不断空检查 both 状态对象和上次修改日期。
但是,我很难在 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
class,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
}
}
然而,据我所知,地图本身的设置并不是 Heroic 特有的,应该是纯粹的 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)
};
}
));
});
我的数据库实体如下所示(已编辑相关信息):
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。这很有用,因为它不需要不断空检查 both 状态对象和上次修改日期。
但是,我很难在 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
class,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
}
}
然而,据我所知,地图本身的设置并不是 Heroic 特有的,应该是纯粹的 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)
};
}
));
});