【问题标题】:Automapper: flatten a nested collectionAutomapper:展平嵌套集合
【发布时间】:2023-10-15 18:49:02
【问题描述】:

我在将我的一个具有集合的 DTO 映射到实体模型中的展平版本时遇到问题。

这是 DTO 类:

public class Location {
   public int LocationId {get; set;}
   public string LocationName {get;set;}
   public List<HoursOfOperation> HoursOfOperation {get; set;}
}

public class HoursOfOperation {
      public int WeekNumber {get; set;}
      public int DayNumber {get; set;}
      public List<TimeSlot> OperatingHours {get; set;}
}

public class TimeSlot{
      public TimeSpan StartTime {get; set;}
      public TimeSpan EndTime { get; set;}
}

这是我的实体模型类:

 public partial class HoursOfOperation
    {
        public int HoursOfOperationId { get; set; }
        public int FkLocationId { get; set; }
        public short WeekNumber { get; set; }
        public short DayNumber { get; set; }
        public TimeSpan StartTime { get; set; }
        public TimeSpan EndTime { get; set; }
    }

我已经使用了 Automapper 并做到了这一点:

public OpHoursMappingProfile()
    {
        CreateMap<HoursOfOperation, Models.ClientDb.HoursOfOperation>()
            .ForMember(dest => Enum.GetName(typeof(DayOfWeek), dest.DayNumber), opt => opt.MapFrom(t => t.DayNumber))
            .ForMember(dest => dest.WeekNumber, opt => opt.MapFrom(t => t.WeekNumber));
    }

我不确定如何映射时间段。我尝试按照here 的说明创建自定义扩展,但没有帮助。

【问题讨论】:

  • EndTime 使用Max(),对StartTime 使用Min()。例如获取结束时间:.ForMember(dst =&gt; dst.EndTime, opt =&gt; opt.MapFrom(t =&gt; t.OperatingHours.Max(hours =&gt; hours.EndTime))).
  • @Prolog:实际上,我想要的是为每个时间段创建一个 HoursOfOperation 实体。因此,如果 DTO 在某一天有 3 个时隙,则将在数据库中创建 3 个 HoursOfOperation 记录。

标签: collections automapper flatten


【解决方案1】:

您的评论让我更好地了解您想要实现的目标,在我看来,它正在将单个源对象映射到目标对象的集合。为此,我建议创建自定义类型转换器:

public class HoursOfOperationConverter : ITypeConverter<HoursOfOperation, IEnumerable<Models.ClientDb.HoursOfOperation>>
{
    public IEnumerable<Models.ClientDb.HoursOfOperation> Convert(
        HoursOfOperation source,
        IEnumerable<Models.ClientDb.HoursOfOperation> destination,
        ResolutionContext context)
    {
        foreach (var timeSlot in source.OperatingHours)
        {
            var hours = context.Mapper.Map<Models.ClientDb.HoursOfOperation>(source);
            hours = context.Mapper.Map(timeSlot, hours);

            yield return hours;
        }
    }
}

注意转换器如何在每次迭代中两次映射到HoursOfOperation。第一次从最初的 DTO 营业时间开始,因此可以填充像 FkLocationId 这样的常规属性。从当前时间段开始第二次,所以StartTimeEndTime日期可以从相应的时间段开始填写。理解为什么在第二次使用映射到现有对象也很重要。之所以如此,是因为否则第一次映射的结果会丢失。

映射配置文件:

public class OpHoursMappingProfile : Profile
{
    CreateMap<HoursOfOperation, Models.ClientDb.HoursOfOperation>()
        .ForMember(
            destination => destination.HoursOfOperationId,
            options => options.MapFrom(source => source.Id))
        .ForMember(
            destination => destination.FkLocationId,
            options => options.MapFrom(source => source.LocationId));
    CreateMap<TimeSlot, Models.ClientDb.HoursOfOperation>();
    CreateMap<HoursOfOperation, IEnumerable<Models.ClientDb.HoursOfOperation>>()
        .ConvertUsing<HoursOfOperationConverter>();
}

我不完全了解您的周/日数字映射,所以我将它们留给您实施。

示例用法:

var result = mapper.Map<IEnumerable<Models.ClientDb.HoursOfOperation>>(hours);

【讨论】: