Denormalise object hierarchy with automapper
I know various questions have been asked that resembles this question, but as far as I can tell (and test), none of the provided solutions seems to fit, so here goes.
I am wondering if it is possible to flatten/denormalise an object hierarchy so that an instance with a list of nested properties is mapped to a list of some destination type using AutoMapper.
I have a source class that looks something like
Sources:
public class DistributionInformation
{
public string Streetname;
public RouteInformation[] Routes;
}
public class RouteInformation
{
public int RouteNumber;
public string RouteDescription;
}
Destination:
public class DenormDistributionInfo
{
public string Streetname;
public int RouteNumber;
public string RouteDescription;
}
So I want to map the two sources to a list of the denormalised destination DenormDistributionInfo.
Ie:
IEnumerable<DenormDistributionInfo> result = Mapper.Map(distributionInformationInstance);
Is that possible/feasible using AutoMapper, or should I give in and denormalise it "manually"?
The main thing is that you want to avoid having to "look up" data in your mapping that isn't implicit in the source. "Magical" mappings cause serious maintenance problems down the line.
Conceptually, however, this mapping is pretty simple. The only complicated factor is that you need two source objects (both a DistributionInformation
and a RouteInformation
) in order to construct your target object. If you follow that train of thought, we can create a non-magical mapping that clearly preserves our intent - here's how I'd do it:-
// We need both source objects in order to perform our map
Mapper.CreateMap<Tuple<DistributionInformation, RouteInformation>, DenormDistributionInfo>()
.ForMember(d => d.Streetname, o => o.MapFrom(s => s.Item1.Streetname))
.ForMember(d => d.RouteDescription, o => o.MapFrom(s => s.Item2.RouteDescription))
.ForMember(d => d.RouteNumber, o => o.MapFrom(s => s.Item2.RouteNumber));
// We can use ConstructUsing to pass both our source objects to our map
Mapper.CreateMap<DistributionInformation, IEnumerable<DenormDistributionInfo>>()
.ConstructUsing(
x => x.Routes
.Select(y => Mapper.Map<DenormDistributionInfo>(Tuple.Create(x, y)))
.ToList());
And to invoke it:-
var flattened = Mapper.Map<IEnumerable<DenormDistributionInfo>>(source);
You can avoid a little of the Tuple horror if you like by creating a DTO to hold both source objects. I especially strongly recommend this if your real code is even slightly more involved than the example you've presented in your question.
Whether or not using AutoMapper to perform this mapping is more or less complicated than just doing it by hand is up to you to decide. In this case, I don't think I'd bother, but in a more involved scenario that gets repeated often I might consider it.
I dug into it a bit, and while I choose to solve the problem by mapping "manually" there is another way (aside from the answer posted by Iain. It does feel rather hacky though.
The idea is to use a type converter and map it twice
public class DistributionInfoConverter : ITypeConverter<DistributionInformation, IEnumerable<DenormDistributionInfo>>
{
public IEnumerable<DenormDistributionInfo> Convert(ResolutionContext context)
{
var result = new List<DenormDistributionInfo>();
var source = (DistributionInformation)context.SourceValue;
foreach (var routeDetail in source.Routes)
{
var model = new DenormDistributionInfo();
Mapper.Map(routeDetail, model);
Mapper.Map(source, model);
result.Add(model);
}
return result;
}
}
Mapper.CreateMap<RouteInformation, DenormDistributionInfo>();
Mapper.CreateMap<DistributionInformation, DenormDistributionInfo>()
Mapper.CreateMap<DistributionInformation, IEnumerable<DenormDistributionInfo>>().ConvertUsing<DistributionInfoConverter>();
The only issue is that for collections of DistributionInformation you have to loop/select each item and map instead of letting automapper figure out how to map a collection to a collection like you normally would.
链接地址: http://www.djcxy.com/p/37354.html上一篇: 自动映射器自定义映射或忽略