サード パーティの Web サービス用に操作する必要があるかなり斬新なクラス構造を持つプロジェクトを継承しました。
基本的に、ソース オブジェクトの構造は次のとおりですparent->collection->collection member object
(つまり、コレクション メンバーには、単純なプロパティとクラス メンバー オブジェクトがあります。例で説明する方が適切です)。Dto はより単純で、ただparent->collection
.
問題は、TrackInfoDto
オブジェクト (これらは Dto のコレクション メンバーを構成する)がオブジェクトからプロパティを取得する必要TrackDefinition.TrackType
があるという事実にあります。これは source に属するオブジェクトです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
}
}
}
コードを理解するのがそれほど難しくないことを願っています。実際のシナリオから大幅に単純化しましたが、重要な注意事項はそのままにしておきました。