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

时间:2023-01-05
本文介绍了如何使用 AutoMapper 将 Dto 映射到具有嵌套对象的现有对象实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

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

I have the following Dto and entity with a nested sub entity.

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 的现有实例.

How can I set up a mapping with AutoMapper that will allow me to update an existing instance of Entity with the values from a Dto.

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

I'm using Mapper.Map(dto, entity) to update an existing entity but when I try to map Dto.SubProperty to Entity.Sub.SubProperty I get an exception for "must resolve to top-level member. Parameter name: lambdaExpression".

如果我使用 FromMember 创建从 DtoSubEntity 的映射,则 Entity.Sub 被替换为SubEntity 的新实例,但这不是我想要的.我只希望它更新 EntitySub 属性上的 SubEntity 现有实例的属性.

If I create a mapping from Dto to SubEntity using FromMember then Entity.Sub gets replaced with a new instance of SubEntity but that's not what I want. I just want it to update the properties of the existing instance of SubEntity on the Sub property of Entity.

我怎样才能做到这一点?

推荐答案

我通过使用 ResolveUsing() 方法和实现 IValueResolver 的组合解决了它和 ConvertUsing() 方法并实现 ITypeConverter.

I solved it by using a combination of the ResolveUsing<T>() method and implementing IValueResolver and the ConvertUsing<T>() method and implementing ITypeConverter<TSource,TDestination>.

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

Some of my mapping scenarios are more complicated than normal including bidirectional mapping and nested classes and nested collections. The above helped me to solve them.

根据要求,我提供了一个示例解决方案.这个例子比我处理的实际类型简单得多.

As requested, I've included an example solution. This example is much simpler than the actual types I was dealing with.

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 的示例,您将获得以下输出:

If you run the example, which is using MapperConfig, you'll get the following output:

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

字符串属性都按照人们希望的方式更新,但是 entity.Sub 被替换为 SubEntity 的新实例想要更新将持久化到数据库的 ORM 的实体.

The string properties all get updated as one would want them to, but entity.Sub gets replaced with a new instance of SubEntity which is no good for when you are wanting to update entities for an ORM that will be persisted to a database.

如果您修改 Main 以便使用 MapperConfig2,您仍然会像以前一样更新字符串属性,但是entity.sub 仍然具有与之前相同的 SubEntity 实例.使用 MapperConfig2 运行示例给出以下输出:

If you modify Main so that MapperConfig2 is used instead, you'll still have the string properties updated as before, but, entity.sub still has the same instance of SubEntity that it had before. Running the example with MapperConfig2 gives this output:

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

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

The key difference in MapperConfig2 is that ResolveUsing is used along with MyResolver to preserve the value of entity.Sub.

这篇关于如何使用 AutoMapper 将 Dto 映射到具有嵌套对象的现有对象实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!