Automapper:分层中间对象问题

时间:2013-11-08 10:34:33

标签: c# automapper hierarchy

我继承了一个具有相当新颖的类结构的项目,我需要为第三方Web服务进行操作。

基本上,源对象的结构为parent->collection->collection member object(即集合成员具有简单属性和类成员对象 - 示例应该更好地解释它)。 Dto更简单,只是parent->collection

问题在于TrackInfoDto对象(这些形成Dto中的集合成员)必须TrackDefinition.TrackType对象获取属性。这是属于源parent.collection的对象。

我已经尝试了很多方法在AutoMapper中映射这种关系并绘制了一个空白。

我的主要问题是:

  • 如果源对象和目标对象具有要导航的中间对象,我如何将源结构映射到目标结构。

为了提供帮助,下面是我迄今为止尝试过的代码示例。

using System;
using System.Collections.Generic;
using AutoMapper;

namespace AutoMapperTest
{
    /*
     *  requires Automapper  PM>
     *  Install-Package AutoMapper -Version 3.0.0
    */

    /* source class objects */
    public class Track
    {
        public string Id { get; set; }
        public string FileId { get; set; }
        public string MediaName { get; set; }

        public List<TrackDefinition> TrackDefinitions { get; set; }

        public Track()
        {
            TrackDefinitions = new List<TrackDefinition>();
        }
    }

    public class TrackDefinition
    {
        public string Id { get; set; }
        public string TrackTypeName { get; set; }
        public TrackType TrackType { get; set; }

        public TrackDefinition()
        {
            TrackType = new TrackType();
        }
    }

    public class TrackType
    {
        public int Id { get; set; }
        public string FileTag { get; set; }
        public string Name { get; set; }
    }

    /* destination mapped object model */
    public class TrackDto
    {
        public string Id { get; set; }
        public string MediaName { get; set; }
        public string FileId { get; set; }

        public List<TrackInfoDto> TrackInfo { get; set; }

        public TrackDto()
        {
            TrackInfo = new List<TrackInfoDto>();
        }
    }

    public class TrackInfoDto
    {
        public string FileTag { get; set; }
        public string Name { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            #region setup
            // create small test bed of source data
            var sourceTracks = new List<Track>();
            // add a few tracks (index start 1 loops)
            for (int i = 1; i < 4; i++)
            {
                var newTrack = new Track
                {
                    Id = string.Format("{0}", i),
                    MediaName = string.Format("media track {0}", i),
                    FileId = string.Format("file reference {0}", i)
                };
                for (int j = 1; j < 3; j++)
                {
                    var trackDefinition = new TrackDefinition
                    {
                        Id = string.Format("TD Id {0}", i),
                        TrackTypeName = "a track type",
                        TrackType =
                        {
                            Id = j*i,
                            FileTag = string.Format("file tag {0}", j*i),
                            Name = string.Format("name # {0}", j*i)
                        }
                    };
                    newTrack.TrackDefinitions.Add(trackDefinition);
                }
                sourceTracks.Add(newTrack);
            }
            #endregion

            #region map
            // now for the problem - how to map the fact that the hierarchy
            // in Track->TrackDefinition->TrackType
            // needs to *miss out* the middle object (TrackDefinition) when
            // we map down to the Dto's
            Mapper.CreateMap<Track, TrackDto>()
                .ForMember(x => x.TrackInfo, opt => opt.Ignore());
            Mapper.CreateMap<TrackDefinition, TrackInfoDto>()
                .ForMember(dest => dest.FileTag, func => func.MapFrom(src => src.TrackType.FileTag));
            Mapper.CreateMap<TrackDefinition, TrackInfoDto>()
                .ForMember(dest => dest.Name, func => func.MapFrom(src => src.TrackType.Name));
            // first thing -make sure no mapping issues
            Mapper.AssertConfigurationIsValid();

            // i've even tried to see if that would coerce - nope!!
            //Mapper.CreateMap<TrackType, TrackInfoDto>();

            // map our source objects down to our dtos
            var trackDtos = Mapper.Map<ICollection<Track>, ICollection<TrackDto>>(sourceTracks);

            // by inspecting the trackDtos members, we SHOULD see the TrackInfoDto as being
            // populated, but alas not. This is the missing puzzle piece
            #endregion
        }
    }
}

希望代码不难理解,我已经从现实生活场景中简化了很多,但保留了关键的警告。

1 个答案:

答案 0 :(得分:2)

我已经解决了,我正在寻找映射中间对象的错误方法,我只需要ResolveUsing()它。这是一个单一的变化:

Mapper.CreateMap<Track, TrackDto>()
    .ForMember(dest => dest.TrackInfo, func => func
        .ResolveUsing(src => src.TrackDefinitions));