如何使用AutoMapper将Dto映射到具有嵌套对象的现有对象实例?

Gid*_*sey 12 .net c# nested dto automapper

我有以下Dto和实体与嵌套的子实体.

public class Dto
{
    public string Property { get; set; }
    public string SubProperty { get; set; }
}

public class Entity
{
    public string Property { get; set; }
    public SubEntity Sub { get; set; }
}

public class SubEntity
{
    public string SubProperty { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

如何使用AutoMapper设置映射,这将允许我使用Dto中的值更新现有的Entity实例.

我正在使用Mapper.Map(dto, entity)更新现有实体但是当我尝试映射Dto.SubProperty到时,Entity.Sub.SubProperty我得到一个例外,"必须解析为顶级成员.参数名称:lambdaExpression".

如果我从创建一个映射DtoSubEntity使用FromMember,然后Entity.Sub被用的新实例所取代SubEntity,但是这不是我想要的.我只是想让它更新属性的现有实例SubEntitySub属性Entity.

我怎样才能做到这一点?

Gid*_*sey 20

我通过结合使用ResolveUsing<T>()方法和实现IValueResolver以及ConvertUsing<T>()方法和实现来解决它ITypeConverter<TSource,TDestination>.

我的一些映射场景比正常情况更复杂,包括双向映射和嵌套类以及嵌套集合.以上帮助我解决了这些问题.


编辑

根据要求,我已经包含了一个示例解决方案.这个例子比我正在处理的实际类型简单得多.

using System;
using AutoMapper;

namespace TestAutoMapperComplex
{
    public class Dto
    {
        public string Property { get; set; }
        public string SubProperty { get; set; }
    }

    public class Entity
    {
        public string Property { get; set; }
        public SubEntity Sub { get; set; }
    }

    public class SubEntity
    {
        public string SubProperty { get; set; }
    }

    static class MapperConfig
    {
        public static void Initialize()
        {
            Mapper.CreateMap<Dto, Entity>()
                .ForMember(entity => entity.Sub, memberOptions =>
                    memberOptions.MapFrom(dto => dto));
            Mapper.CreateMap<Dto, SubEntity>();
        }
    }

    static class MapperConfig2
    {
        private class MyResolver : IValueResolver
        {

            public ResolutionResult Resolve(ResolutionResult source)
            {
                var destinationSubEntity = ((Entity)source.Context.DestinationValue).Sub;

                Mapper.Map((Dto)source.Value, destinationSubEntity);

                return source.New(destinationSubEntity, typeof(SubEntity));
            }
        }

        public static void Initialize()
        {
            Mapper.CreateMap<Dto, Entity>()
                .ForMember(entity => entity.Sub, memberOptions =>
                    memberOptions.ResolveUsing<MyResolver>());
            Mapper.CreateMap<Dto, SubEntity>();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MapperConfig.Initialize();

            var dto = new Dto {Property = "Hello", SubProperty = "World"};
            var subEntity = new SubEntity {SubProperty = "Universe"};
            var entity = new Entity {Property = "Good bye", Sub = subEntity};

            Mapper.Map(dto, entity);

            Console.WriteLine(string.Format("entity.Property == {0}, entity.Sub.SubProperty == {1}",
                entity.Property, entity.Sub.SubProperty));
            Console.WriteLine(string.Format("entity.Sub == subEntity: {0}", 
                entity.Sub == subEntity));

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您运行正在使用的示例,MapperConfig您将获得以下输出:

entity.Property == Hello, entity.Sub.SubProperty == World
entity.Sub == subEntity: False
Run Code Online (Sandbox Code Playgroud)

字符串属性都会按照人们希望的那样进行更新,但是当您想要更新将持久保存到数据库的ORM的实体时entity.Sub,SubEntity它会替换为新的实例.

如果修改Main,这样MapperConfig2来代替,你仍然有串属性之前更新,但是,entity.sub仍然有同样的实例SubEntity,它收到了.运行示例MapperConfig2提供此输出:

entity.Property == Hello, entity.Sub.SubProperty == World
entity.Sub == subEntity: True
Run Code Online (Sandbox Code Playgroud)

在关键的区别MapperConfig2ResolveUsing用来一起MyResolver保存的价值entity.Sub.