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

发布于 2024-09-17 22:35:39 字数 899 浏览 0 评论 0原文

我有以下 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< /code> 我收到一个异常“必须解析为顶级成员。参数名称:lambdaExpression”。

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

我怎样才能实现这一目标?

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; }
}

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.

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".

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.

How can I achieve this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

二智少女猫性小仙女 2024-09-24 22:35:39

我通过结合使用 ResolveUsing() 方法并实现 IValueResolverConvertUsing() 方法解决了这个问题,实现ITypeConverter

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


编辑

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

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.Sub被替换为 SubEntity 的新实例,当您想要更新将保留到数据库的 ORM 实体时,这没有什么好处。

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

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

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

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.


EDIT

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));

        }
    }
}

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

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.

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文