Подтвердить что ты не робот

Как вы сопоставляете 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; }
}

Как настроить сопоставление с помощью AutoMapper, что позволит мне обновить существующий экземпляр Entity со значениями из Dto.

Я использую Mapper.Map(dto, entity) для обновления существующего объекта, но когда я пытаюсь сопоставить Dto.SubProperty с Entity.Sub.SubProperty, я получаю исключение для "должен быть разрешен для члена верхнего уровня. Имя параметра: lambdaExpression".

Если я создаю сопоставление от Dto до SubEntity с помощью FromMember, то Entity.Sub заменяется новым экземпляром SubEntity, но это не то, что я хочу. Я просто хочу, чтобы он обновил свойства существующего экземпляра SubEntity в свойстве Sub Entity.

Как я могу достичь этого?

4b9b3361

Ответ 1

Я решил это, используя комбинацию метода 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));

        }
    }
}

Если вы запустите пример, который использует 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 заключается в том, что ResolveUsing используется вместе с MyResolver для сохранения значения entity.Sub.