简体   繁体   中英

How to map a single object from multiple ones, using AutoMapper?

AssetAvailability

public class AssetAvailabilityDto
{
    public int Id { get; set; }
    public int AssetId { get; set; }
    public int CalendarId { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}

Asset

public class AssetDto
{
    public int Id { get; set; }
    public int CategoryId { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public bool IsActive { get; set; }
    public int OwnedBy { get; set; }
    public DateTime LastModifiedAt { get; set; }
}

BookingSchedule

public class BookingScheduleDto
{
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }

}

The destination to hold them all

public class AssetInformationDto
{
    public int AssetId { get; set; }
    public int CategoryId { get; set; }
    public string AssetName { get; set; }
    public string AssetDescription { get; set; }
    public bool AssetIsActive { get; set; }
    public int AssetOwnedBy { get; set; }
    public DateTime AssetLastModifiedAt { get; set; }
    // End of Assets
    public int AvailabilityId { get; set; }
    public DateTime AvailabilityStartDate { get; set; }
    public DateTime AvailabilityEndDate { get; set; }
    // End of Availability

    public List<DateTime> BookingStartDate { get; set; } = new List<DateTime>();
    public List<DateTime> BookingEndDate { get; set; } = new List<DateTime>();
    // End of Booking schedule
}

Where the mapping takes place

        AssetInformationDto information = new AssetInformationDto();
        AssetDto asset = _assetService.GetAssetById(id);
        AssetAvailabilityDto assetAvailability = _assetService.GetAssetAvailabilityByAssetId(id);
        IEnumerable<BookingScheduleDto> schedule = _assetService.GetAssetBookingSchedule(id, true);
        information = _mapper.Map<AssetInformationDto>(asset);
        information = _mapper.Map<AssetInformationDto>(assetAvailability);
        information = _mapper.Map<AssetInformationDto>(schedule);

The configuration of AutoMapper

 CreateMap<AssetDto, AssetInformationDto>()
            .ForMember(dest => dest.AssetName, opt => opt.MapFrom(src => src.Name))
            .ForMember(dest => dest.AssetDescription, opt => opt.MapFrom(src => src.Description))
            .ForMember(dest => dest.AssetLastModifiedAt, opt => opt.MapFrom(src => src.LastModifiedAt))
            .ForMember(dest => dest.AssetIsActive, opt => opt.MapFrom(src => src.IsActive))
            .ForMember(dest => dest.AssetOwnedBy, opt => opt.MapFrom(src => src.OwnedBy))
            .ForMember(dest => dest.AssetId, opt => opt.MapFrom(src => src.Id))
            .ForAllOtherMembers(m => m.Ignore());
        CreateMap<AssetAvailabilityDto, AssetInformationDto>()
            .ForMember(dest => dest.AvailabilityStartDate, opt => opt.MapFrom(src => src.StartDate))
            .ForMember(dest => dest.AvailabilityEndDate, opt => opt.MapFrom(src => src.EndDate))
            .ForMember(dest => dest.AvailabilityId, opt => opt.MapFrom(src => src.Id))
            .ForAllOtherMembers(m => m.Ignore());
        CreateMap<IEnumerable<BookingScheduleDto>, AssetInformationDto>().AfterMap((s, d) =>
        {
            foreach (var item in s)
            {
                d.BookingStartDate.Add(item.StartDate);
                d.BookingEndDate.Add(item.EndDate);
            }
        }).ForAllOtherMembers(m => m.Ignore());

This is being done on an ASP.NET Core 2.2 project, hence why AutoMapper is always injected with dependency injection (_mapper is the injected object)

The post shared here https://stackoverflow.com/a/38257912/11272124 is a solution to the above problem, by making a function "EntityMapper" and by slightly modifying it to accept the injected _mapper object, but I'd like to learn an alternative solution.

Isn't this possible with simply configuring AutoMapper to do so? Maybe the reason for it not to work, is due to my poorly written out configuration.

The problem here is that after each mapped object, (After every information = ...) the previously assigned properties are reverted to their type's default values. That's why I included ForAllOtherMembers.Ignore() in the respective class where I configure how the mapping is done.

What I've been looking for was the usage of Mapper.Map(TSource, TDestination) (Mapper in my case would be _mapper, due to dependency injection.)

It executes a mapping from the source object to the existing destination object.

The method I previously used ( Mapper.Map<TDestination>(TSource) ) executes a mapping from the source object to a new destination object.


The code before:

    AssetInformationDto information = new AssetInformationDto();
    AssetDto asset = _assetService.GetAssetById(id);
    AssetAvailabilityDto assetAvailability = _assetService.GetAssetAvailabilityByAssetId(id);
    IEnumerable<BookingScheduleDto> schedule = _assetService.GetAssetBookingSchedule(id, true);
    information = _mapper.Map<AssetInformationDto>(asset);
    information = _mapper.Map<AssetInformationDto>(assetAvailability);
    information = _mapper.Map<AssetInformationDto>(schedule);

The code that fixed my issue

    AssetInformationDto information;
    AssetDto asset = _assetService.GetAssetById(id);
    AssetAvailabilityDto assetAvailability =
    _assetService.GetAssetAvailabilityByAssetId(id);
    IEnumerable<BookingScheduleDto> schedule =
    _assetService.GetAssetBookingSchedule(id, true);
    information = _mapper.Map<AssetInformationDto>(asset);
    _mapper.Map(assetAvailability, information);
    _mapper.Map(schedule, information);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM