Как вы сопоставляете Dto с существующим экземпляром объекта с вложенными объектами, используя AutoMapper? - PullRequest
12 голосов
/ 09 сентября 2010

У меня есть следующие 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.

Как мне этого добиться?

1 Ответ

20 голосов
/ 15 сентября 2010

Я решил это с помощью комбинации метода ResolveUsing<T>() и реализации IValueResolver и метода ConvertUsing<T>() и реализации ITypeConverter<TSource,TDestination>.

Некоторые из моих сценариев отображения сложнее, чем обычно, включая двунаправленное отображение, вложенные классы и вложенные коллекции. Вышесказанное помогло мне их решить.


EDIT

По запросу я включил пример решения. Этот пример намного проще, чем реальные типы, с которыми я имел дело.

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...