Mapping 使用AutoMapper解除DTO的平台

Mapping 使用AutoMapper解除DTO的平台,mapping,automapper,Mapping,Automapper,我一直在尝试使用AutoMapper来节省从DTO到域对象的时间,但是我在配置映射以使其工作时遇到了问题,我开始怀疑AutoMapper是否是用于此工作的错误工具 考虑以下域对象示例(一个实体和一个值): 我的DTO(从Linq到SQL对象)大致如下所示: public class PersonDTO { public string Name { get; set; } public string Address { get; set; } public string C

我一直在尝试使用AutoMapper来节省从DTO到域对象的时间,但是我在配置映射以使其工作时遇到了问题,我开始怀疑AutoMapper是否是用于此工作的错误工具

考虑以下域对象示例(一个实体和一个值):

我的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; }
}
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 }; }))
我希望能够在我的存储库中执行此操作:

return Mapper.Map<PersonDTO, Person>(result);
返回Mapper.Map(结果);
我尝试了各种方法来配置AutoMapper,但我一直遇到通用的缺少类型映射配置或不支持映射的错误,没有详细信息告诉我失败的地方

我尝试了许多不同的配置,但这里有一些:

Mapper.CreateMap<PersonDTO, Person>()
    .ForMember(dest => dest.Address, opt => opt.MapFrom(Mapper.Map<Person, Domain.StreetAddress>));
Mapper.CreateMap()
.ForMember(dest=>dest.Address,opt=>opt.MapFrom(Mapper.Map));

Mapper.CreateMap()
.FormMember(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展平对象很容易,但展平它们并不容易……甚至不可能。有谁能告诉我我是否在尝试做不可能的事,如果没有,我做错了什么


请注意,我的实际对象稍微复杂一些,因此我可能遗漏了导致错误的关键信息…如果我所做的看起来是正确的,我可以提供更多信息或开始简化我的对象以进行测试。

使用,它可以进行展平取消展平,以及您需要的任何其他操作,下载中有一个asp.net mvc示例应用程序,其中演示了所有功能(以及单元测试)

这可能很晚,但您可以通过使用lambda表达式创建如下对象来解决此问题:

public class PersonDTO
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}
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 }; }))
Mapper.CreateMap()
.FormMember(dest=>dest.Address,opt=>opt.MapFrom(src=>{return new Address(){Address1=src.Address,City=src.City,State=src.State};}))

这似乎也适用于我:

Mapper.CreateMap<PersonDto, Address>();
Mapper.CreateMap<PersonDto, Person>()
        .ForMember(dest => dest.Address, opt => opt.MapFrom( src => src )));
Mapper.CreateMap();
Mapper.CreateMap()
.ForMember(dest=>dest.Address,opt=>opt.MapFrom(src=>src));

基本上,创建一个从dto到两个对象的映射,然后将其用作子对象的源。

无法发布注释,因此发布答案。我猜AutoMapper实现中有一些变化,所以HansoS提出的答案不再是可编译的。尽管在此类场景中可以使用另一种方法-
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 }; }))
Mapper.CreateMap()
.FormMember(dest=>dest.Address,opt=>opt.ResolveUsing(src=>{return new Address(){Address1=src.Address,City=src.City,State=src.State};}))

根据特雷弗·德·科克科克(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 )));
我在用这个

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());
}
遵循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; }
}

可能需要很多改进,但它能工作…

我有另一个解决方案。主要思想是自动映射如何在展平对象中正确命名属性时展平嵌套对象:添加嵌套对象属性名称作为前缀。您的病例地址为前缀:

public class PersonDTO
{
    public string Name { get; set; }
    public string AddressCity { get; set; }
    public string AddressState { get; set; }
    ...
}
因此,创建熟悉的从嵌套到展平和的映射,然后使用ReverseMap方法可以让AutomMapper了解如何取消嵌套对象的展平

CreateMap<Person, PersonDTO>()
   .ReverseMap();
CreateMap()
.ReverseMap();

就这些

嗯,您的第二个配置看起来很不错(除了它缺少的名称),不是吗?可能需要付费检查域名。Person和PersonDTO是上述课程的正确参考资料相关:此时来自Ruben的链接和81959186的公认答案(目前只有一个答案)返回到此帖子。我看不到此页上的链接注释的值。Ruben在另一个页面上发布了一个指向这个问题的链接,这个链接很有意义。我一直在尝试ValueInjector,但遇到了一些问题,您在CodePlex上帮助我解决了这些问题。valueInjector的工作方式比Automapper更优雅,它不支持列表,尽管这个项目dead@Yacov它被移动到Github这很好用-不确定它将如何处理目标对象上已经存在地址的情况。这会导致一个错误:“一个带有无法将语句体转换为表达式树'I get the same error@Kwal Sindements'。然而,根据Ivan Samygin的回答,当我将
opt.MapFrom
替换为
opt.ResolveUsing
时,它确实起了作用。这正是我想要的。ValueInjector可能是选择的答案,但使用它,AutoMapper似乎更容易使用。这将是一个很好的答案,但在我的DTO中,地址属性是扁平的:“AddressCity”,而在address对象中,它们只是“City”,因此除非我显式映射每个字段,否则映射不起作用。这也是您必须要做的吗?这很好地工作,但它不允许实际设置空值。它忽略空值并保留目标的原始值。是否有可能写入、接受并写入空值?@TrevordeKoekkoek:你找到解决方案了吗?我现在也有同样的情况。对不起,科迪,我真的记不清了。我知道我已经使用了Omu ValueInjector,它在某些场景中对我更有效。这是可行的,但有点违背了AutoMapper的整个原则。它不再是基于约定的“自动”映射,而是手动映射。更糟糕的是,这是一个在不透明的立面后面模糊的手动贴图。From:>目前,AutoMapper面向模型投影场景,以展平复杂对象
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; }
}
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; }
}
public class PersonDTO
{
    public string Name { get; set; }
    public string AddressCity { get; set; }
    public string AddressState { get; set; }
    ...
}
CreateMap<Person, PersonDTO>()
   .ReverseMap();