使用AutoMapper解开DTO
我一直在尝试使用AutoMapper来保存从我的DTO到我的域对象的一些时间,但是我在configuration地图时遇到了麻烦,所以它工作,我开始怀疑AutoMapper可能是错误的工具工作。
考虑这个域对象的例子(一个实体和一个值):
public class Person { public string Name { get; set; } public StreetAddress Address { get; set; } } public class StreetAddress { public string Address { get; set; } public string City { get; set; } public string State { get; set; } }
我的DTO(从Linq到SQL对象)现在看起来大致如下所示:
public class PersonDTO { public string Name { get; set; } public string Address { get; set; } public string City { get; set; } public string State { get; set; } }
我希望能够在我的存储库中做到这一点:
return Mapper.Map<PersonDTO, Person>(result);
我已经尝试过用AutoMapper来configurationAutoMapper,但是我总是得到一般的Missingtypes映射configuration或不支持的映射错误,没有详细的信息告诉我我失败的地方。
我已经尝试了许多不同的configuration,但是这里有一些:
Mapper.CreateMap<PersonDTO, Person>() .ForMember(dest => dest.Address, opt => opt.MapFrom(Mapper.Map<Person, Domain.StreetAddress>));
和
Mapper.CreateMap<Person, Domain.Person>() .ForMember(dest => dest.Address.Address1, opt => opt.MapFrom(src => src.Address)) .ForMember(dest => dest.Address.City, opt => opt.MapFrom(src => src.City)) .ForMember(dest => dest.Address.State, opt => opt.MapFrom(src => src.State));
我读过使用AutoMapper 平坦化对象很容易,但解开它们并不容易…甚至可能。 任何人都可以告诉我,我是否试图做不可能的事情,如果不是我做错了什么?
请注意,我的实际对象有点复杂,所以我可能会遗漏信息,这是错误的关键…如果我在做的事情看起来正确,我可以提供更多的信息或开始简化我的对象进行testing。
使用https://github.com/omuleanu/ValueInjecter ,它会变 平 和解压缩 ,以及其他任何你需要的东西, 在下载的地方有一个asp.net mvc示例应用程序,其中展示了所有的function(也包括unit testing)
这似乎也适用于我:
Mapper.CreateMap<PersonDto, Address>(); Mapper.CreateMap<PersonDto, Person>() .ForMember(dest => dest.Address, opt => opt.MapFrom( src => src )));
基本上,创build从dto到两个对象的映射,然后将其用作子对象的源。
无法发表评论,所以张贴答案。 我想在AutoMapper实现中有一些变化,所以由HansoS提出的回答https://stackoverflow.com/a/5154321/2164198不再是可编译的。; 虽然在这种情况下可以使用另一种方法 – ResolveUsing
:
Mapper.CreateMap<Person, Domain.Person>() .ForMember(dest => dest.Address, opt => opt.ResolveUsing( src => { return new Address() {Address1 = src.Address, City = src.City, State = src.State }; }))
除了sydneyos的答案,根据Trevor de Koekkoek评论,双向映射是可能的这种方式
public class Person { public string Name { get; set; } public Address Address { get; set; } } public class Address { public string Street { get; set; } public string City { get; set; } public string State { get; set; } } public class PersonViewModel { public string Name { get; set; } public string AddressStreet { get; set; } public string AddressCity { get; set; } public string AddressState { get; set; } }
自动映射器映射
Mapper.Initialize(cfg => cfg.RecognizePrefixes("Address")); Mapper.CreateMap<Person, PersonViewModel>(); Mapper.CreateMap<PersonViewModel, Address>(); Mapper.CreateMap<PersonViewModel, Person>() .ForMember(dest => dest.Address, opt => opt.MapFrom( src => src )));
如果你实现了NameOf类,你可以去掉前缀魔术string
Mapper.Initialize(cfg => cfg.RecognizePrefixes(Nameof<Person>.Property(x => x.Address)));
编辑:在C#6
Mapper.Initialize(cfg => cfg.RecognizePrefixes(nameof(Person.Address)));
这可能是迟了,但你可以通过使用lambdaexpression式来创build对象来解决这个问题:
Mapper.CreateMap<Person, Domain.Person>() .ForMember(dest => dest.Address, opt => opt.MapFrom( src => { return new Address() {Address1 = src.Address, City = src.City, State = src.State }; }))
我正在使用这个
public static void Unflatten<TSource, TDestination, TMember>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt) { var prefix = opt.DestinationMember.Name; var memberProps = typeof(TMember).GetProperties(); var props = typeof(TSource).GetProperties().Where(p => p.Name.StartsWith(prefix)) .Select(sourceProp => new { SourceProp = sourceProp, MemberProp = memberProps.FirstOrDefault(memberProp => prefix + memberProp.Name == sourceProp.Name) }) .Where(x => x.MemberProp != null); var parameter = Expression.Parameter(typeof(TSource)); var bindings = props.Select(prop => Expression.Bind(prop.MemberProp, Expression.Property(parameter, prop.SourceProp))); var resolver = Expression.Lambda<Func<TSource, TMember>>( Expression.MemberInit(Expression.New(typeof(TMember)), bindings), parameter); opt.ResolveUsing(resolver.Compile()); }
组态
new MapperConfiguration(cfg => { cfg.CreateMap<Person, PersonDTO>(); cfg.CreateMap<PersonDTO, Person>().ForMember(x => x.HomeAddress, opt => opt.Unflatten()); });
楷模
public class Person { public string Name { get; set; } public Address HomeAddress { get; set; } } public class Address { public string Line1 { get; set; } public string Line2 { get; set; } public string City { get; set; } public string State { get; set; } public string ZipCode { get; set; } }
遵循AutoMapper展平惯例
public class PersonDTO { public string Name { get; set; } public string HomeAddressLine1 { get; set; } public string HomeAddressLine2 { get; set; } public string HomeAddressCity { get; set; } public string HomeAddressState { get; set; } public string HomeAddressZipCode { get; set; } }
可能需要许多改进,但它的工作原理…