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

时间:2010-09-08 22:42:08

标签: c# .net nested automapper dto

我有以下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; }
}

如何使用 AutoMapper 设置映射,以便我使用 Dto 中的值更新 Entity 的现有实例

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

如果我使用Dto创建从SubEntityFromMember的映射,那么Entity.Sub将替换为SubEntity的新实例,但这不是我想要的。我只是希望它在SubEntity的{​​{1}}属性上更新现有Sub实例的属性。

我如何实现这一目标?

1 个答案:

答案 0 :(得分:20)

我通过结合使用ResolveUsing<T>()方法并实施IValueResolverConvertUsing<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));

        }
    }
}

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

entity.Property == Hello, entity.Sub.SubProperty == World
entity.Sub == subEntity: False

字符串属性全部按照人们的意愿更新,但entity.SubSubEntity的新实例替换,这对于想要更新ORM的实体时没有用处将被保存到数据库中。

如果您修改Main以便改为使用MapperConfig2,那么您仍然会像以前一样更新字符串属性,entity.sub仍然有与之前相同的SubEntity实例。使用MapperConfig2运行示例会得到以下结果:

entity.Property == Hello, entity.Sub.SubProperty == World
entity.Sub == subEntity: True

MapperConfig2的主要区别在于ResolveUsingMyResolver一起使用以保留entity.Sub的值。