C# 自动映射:分层中间对象问题

C# 自动映射:分层中间对象问题,c#,automapper,hierarchy,C#,Automapper,Hierarchy,我继承了一个项目,它有一个相当新颖的类结构,我需要为第三方Web服务操纵它 基本上,源对象的结构是parent->collection->collection member object(也就是说,collection成员有简单的属性加上一个class成员对象,这个示例应该更好地解释它)。Dto更简单,只是parent->collection 问题在于TrackInfoDto对象(它们构成Dto中的集合成员)必须从TrackDefinition.TrackType对象中获取属性。这是属于源父集合

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

基本上,源对象的结构是
parent->collection->collection member object
(也就是说,collection成员有简单的属性加上一个class成员对象,这个示例应该更好地解释它)。Dto更简单,只是
parent->collection

问题在于
TrackInfoDto
对象(它们构成Dto中的集合成员)必须从
TrackDefinition.TrackType
对象中获取属性。这是属于源
父集合的对象

我尝试了很多方法在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
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用自动制版机;
命名空间自动映射测试
{
/*
*需要Automapper PM>
*安装软件包AutoMapper-版本3.0.0
*/
/*源类对象*/
公共类轨道
{
公共字符串Id{get;set;}
公共字符串FileId{get;set;}
公共字符串MediaName{get;set;}
公共列表TrackDefinitions{get;set;}
公共轨道()
{
TrackDefinitions=新列表();
}
}
公共类轨道定义
{
公共字符串Id{get;set;}
公共字符串TrackTypeName{get;set;}
公共TrackType TrackType{get;set;}
公共轨道定义()
{
TrackType=新的TrackType();
}
}
公共类轨道类型
{
公共int Id{get;set;}
公共字符串文件标记{get;set;}
公共字符串名称{get;set;}
}
/*目标映射对象模型*/
公共类跟踪
{
公共字符串Id{get;set;}
公共字符串MediaName{get;set;}
公共字符串FileId{get;set;}
公共列表TrackInfo{get;set;}
公共轨道
{
TrackInfo=新列表();
}
}
公共类TrackInfoDto
{
公共字符串文件标记{get;set;}
公共字符串名称{get;set;}
}
班级计划
{
静态void Main(字符串[]参数)
{
#区域设置
//创建源数据的小型测试平台
var sourceTracks=新列表();
//添加一些曲目(索引开始1循环)
对于(int i=1;i<4;i++)
{
var newTrack=新轨道
{
Id=string.Format(“{0}”,i),
MediaName=string.Format(“媒体曲目{0}”,i),
FileId=string.Format(“文件引用{0}”,i)
};
对于(int j=1;j<3;j++)
{
var trackDefinition=新的trackDefinition
{
Id=string.Format(“TD Id{0}”,i),
TrackTypeName=“一种曲目类型”,
轨道类型=
{
Id=j*i,
FileTag=string.Format(“文件标记{0}”,j*i),
Name=string.Format(“Name{0}”,j*i)
}
};
newTrack.TrackDefinitions.Add(trackDefinition);
}
sourceTracks.Add(newTrack);
}
#端区
#区域地图
//现在来看问题-如何映射层次结构
//在轨迹->轨迹定义->轨迹类型中
//需要在以下情况下*遗漏*中间对象(轨迹定义)
//我们向下映射到Dto的
Mapper.CreateMap()
.ForMember(x=>x.TrackInfo,opt=>opt.Ignore());
Mapper.CreateMap()
.ForMember(dest=>dest.FileTag,func=>func.MapFrom(src=>src.TrackType.FileTag));
Mapper.CreateMap()
.ForMember(dest=>dest.Name,func=>func.MapFrom(src=>src.TrackType.Name));
//第一件事-确保没有映射问题
assertConfigurationsValid();
//我甚至试着看看这是否会强迫-不!!
//CreateMap();
//将源对象映射到DTO
var trackDtos=Mapper.Map(sourceTracks);
//通过检查trackDtos成员,我们应该看到TrackInfoDto
//填充,但可惜没有。这是丢失的拼图块
#端区
}
}
}

希望代码不要太难理解,我已经从现实场景中简化了很多,但保留了关键的注意事项。

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

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

还不够。现在还不清楚你在问什么。这只是一个代码示例和一堆华夫饼干。您在使用
BeforeMap
之前看过吗?我现在没有时间看这个,但是您可以使用
BeforeMap
,并且在匿名函数声明中,显式地调用您正在努力映射常规levelnis的成员的
Mapper.Map
——感谢您立即理解我的问题并提供了一个很好的建议。我来看看。再次感谢你-我已经编辑了我的问题。如果是