Можно ли сопоставить несколько объектов DTO одной модели ViewModel с помощью Automapper?

Мне было интересно, можно ли сопоставить несколько объектов DTO одному объекту ViewModel с помощью Automapper?

По сути, у меня есть несколько объектов DTO, и я хотел бы отображать информацию с каждого на одном экране в ASP.NET MVC 2.0. Для этого я хотел бы сгладить объекты DTO (или их части ...) в Viewmodel и передать указанную viewmodel в представление. Если бы у меня был один DTO, это было бы легко, но я никогда не видел, чтобы это делалось с несколькими. Очевидно, что есть несколько обходных способов сделать это (помимо автомата), но я бы хотел использовать этот подход, если это возможно.


person JP.    schedule 24.01.2010    source источник
comment
вы можете с помощью ValueInjecter valueinjecter.codeplex.com/documentation   -  person Omu    schedule 05.05.2010


Ответы (6)


Вы можете создать составной DTO, содержащий два или более объекта DTO, и сопоставить составной DTO с моделью представления вывода.

person Alexander Groß    schedule 24.01.2010
comment
Это то, что мы сделали, и я обнаружил, что это работает лучше всего. Затем этот составной объект представления начинает содержать поведение и т. Д., Относящееся к составным объектам. - person Jimmy Bogard; 25.01.2010
comment
Я начал идти по этому пути, но затем пришел к выводу, что если мне придется вручную сопоставить составной объект, прежде чем я смогу Map ‹› (), то что это за дополнительный шаг дает мне? Следуя этому подходу, вы гарантируете, что все ваши сопоставления объектов управляются через AutoMapper, но затем работают, за исключением ручных сопоставлений для композитов. Я не совсем против этой идеи, просто скажу ... - person tbehunin; 19.10.2010
comment
Соглашаться. Решение, только что опубликованное Гордоном Нонгкинрихом, мне кажется гораздо более элегантным. - person Jens H; 19.01.2012

Проверьте следующую ссылку по вашему запросу

http://consultingblogs.emc.com/owainwragg/archive/2010/12/22/automapper-mapping-from-multiple-objects.aspx

person Bahdeng    schedule 19.01.2012
comment
Хотя очень поздно в этой теме ;-), большое спасибо за эту ссылку. Я только начинаю работать с AutoMapper и очень скоро столкнусь с этой проблемой! Голосовать за! :-) - person Jens H; 19.01.2012
comment
Спасибо. Я знал, что это довольно поздно, но недавно столкнулся с той же проблемой. Я понимаю, что никогда не поздно обновить даже старую ветку :) - person Bahdeng; 22.01.2012

Если у вас есть 2 класса DTO и 1 модель плоского представления:

public class Dto1
{
    public string Property1 { get; set; }
}
public class Dto2
{
    public string Property2 { get; set; }
}
public class FlattenedViewModel
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }
}

И вы создаете сопоставления для обоих DTO для просмотра модели:

CreateMap<Dto1, FlattenedViewModel>();
CreateMap<Dto2, FlattenedViewModel>();

Вы можете сопоставить 1-й DTO с моделью, а затем просто «добавить» 2-й DTO:

var dto1 = new Dto1 { Property1 = "Value1"; }
var dto2 = new Dto2 { Property2 = "Value2"; }

var model = Mapper.Map<FlattenedViewModel>(dto1); // map dto1 properties
Mapper.Map(dto2, model); // append dto2 properties
person Nenad    schedule 21.05.2015
comment
var model = Mapper.Map ‹FlattenedViewModel› (dto1); Mapper.Map (dto2, модель); ------------------------------------ Куда идет эта часть кода? Это в контроллере или в службе? Если его служба, то это Service1 или Service2? - person Ayyappa; 13.06.2019
comment
@Ayyappa: В простых случаях этот код обычно находится в контроллере, но его можно извлечь в другом месте. Зависит от сложности вашего кода и структуры ваших сервисов. - person Nenad; 13.06.2019
comment
Спасибо за ответ. Поэтому, если на FlattenedViewModel ссылаются несколько контроллеров, лучше всего включить его на уровне обслуживания. Правильно? - person Ayyappa; 13.06.2019
comment
@Ayyappa: Конечно, вы можете обернуть это повторно используемым методом. Является ли это методом оказания услуги, это упрямый вопрос. Если у вас есть отдельный проект только для сервисов, загружающих dtos из базы данных, то я бы не стал делать этот проект зависимым от MVC FlattenedViewModel. Лучше сохраните этот код в проекте MVC. - person Nenad; 13.06.2019
comment
Имеет смысл! Большое спасибо за ответы :) - person Ayyappa; 13.06.2019

Вы можете добавить метод расширения переопределения карты для IMappingEngine, который принимает массив параметров. Что-то типа:

public static class AutoMapperExtensions
{
    public static T Map<T>(this IMappingEngine engine, params object[] sources) where T : class
    {
        if (sources == null || sources.Length == 0)
            return default(T);

        var destinationType = typeof (T);
        var result = engine.Map(sources[0], sources[0].GetType(), destinationType) as T;
        for (int i = 1; i < sources.Length; i++)
        {
            engine.Map(sources[i], result, sources[i].GetType(), destinationType);
        }

        return result;
    }
}

Тогда вы могли бы назвать это так:

var result = Mapper.Engine.Map<MyViewModel>(dto1, dto2, dto3);
person bkaid    schedule 14.11.2012

Это информация из ссылки с истекшим сроком действия в этом ответе: https://stackoverflow.com/a/8923063/2005596

При использовании AutoMapper (http://automapper.codeplex.com) у меня часто возникает сценарий, в котором мне нужно сопоставить несколько сущностей в одно целое. Обычно это происходит при отображении сущностей числового домена в единую модель представления (ASP.NET MVC). К сожалению, AutoMapper API не предоставляет функциональные возможности для сопоставления нескольких сущностей в одну сущность; однако относительно просто создать вспомогательный метод для этого. Ниже я проиллюстрирую свой подход.

В этом примере у меня есть следующие объекты в моей модели предметной области

public class Person

{

    public int Id { get; set; }



    public string Firstname { get; set; }



    public string Surname { get; set; }

}



public class Address

{

    public int Id { get; set; }



    public string AddressLine1 { get; set; }



    public string AddressLine2 { get; set; }



    public string Country { get; set; }

}



public class Comment

{

    public string Text { get; set; }



    public DateTime Created { get; set; }

}

В дополнение к этому у меня есть требование отображать подробные сведения об адресе человека и любые связанные комментарии на одной странице (с использованием ASP.NET MVC). Чтобы реализовать это, я создал модель представления, показанную ниже, которая включает данные из всех трех сущностей предметной области, показанных выше.

public class PersonViewModel

{

    public int Id { get; set; }



    [DisplayName("Firstname")]

    public string Firstname { get; set; }



    [DisplayName("Surname")]

    public string Surname { get; set; }



    [DisplayName("Address Line 1")]

    public string AddressLine1 { get; set; }



    [DisplayName("Address Line 2")]

    public string AddressLine2 { get; set; }



    [DisplayName("Country Of Residence")]

    public string Country { get; set; }



    [DisplayName("Admin Comment")]

    public string Comment { get; set; }



}

В методе действия контроллера я делаю три отдельных вызова на уровне домена для извлечения требуемых сущностей, но при этом остается проблема, заключающаяся в том, что мне нужно сопоставить несколько исходных сущностей с одной целевой сущностью. Для выполнения этого сопоставления я создал вспомогательный класс, который инкапсулирует AutoMapper и предоставляет функциональные возможности, позволяющие сопоставить несколько исходных объектов с одним целевым объектом. Этот класс показан ниже

public static class EntityMapper

{

    public static T Map<T>(params object[] sources) where T : class

    {

        if (!sources.Any())

        {

            return default(T);

        }



        var initialSource = sources[0];



        var mappingResult = Map<T>(initialSource);



        // Now map the remaining source objects

        if (sources.Count() > 1)

        {

            Map(mappingResult, sources.Skip(1).ToArray());

        }



        return mappingResult;

    }



    private static void Map(object destination, params object[] sources)

    {

        if (!sources.Any())

        {

            return;

        }



        var destinationType = destination.GetType();



        foreach (var source in sources)

        {

            var sourceType = source.GetType();

            Mapper.Map(source, destination, sourceType, destinationType);

        }

    }



    private static T Map<T>(object source) where T : class

    {

        var destinationType = typeof(T);

        var sourceType = source.GetType();



        var mappingResult = Mapper.Map(source, sourceType, destinationType);



        return mappingResult as T;

    }

}

Чтобы сопоставить несколько исходных объектов с одним местом назначения, я использовал функциональность, предоставляемую AutoMapper, которая позволяет вам выполнять сопоставление между исходным объектом и целевым объектом, который уже существует.

Наконец, ниже приведен код контроллера, который извлекает три объекта и выполняет сопоставление с одной моделью представления.

    public ActionResult Index()

    {



        // Retrieve the person, address and comment entities and

        // map them on to a person view model entity

        var personId = 23;



        var person = _personTasks.GetPerson(personId);

        var address = _personTasks.GetAddress(personId);

        var comment = _personTasks.GetComment(personId);



        var personViewModel = EntityMapper.Map<PersonViewModel>(person, address, comment);



        return this.View(personViewModel);

    }
person aoakeson    schedule 11.04.2019

Я сам с этим разобрался, и у меня есть отличное решение. Скорее всего, ваши два представления действительно как-то связаны в вашей системе (особенно если вы используете Entity Framework). Проверьте свои модели, и вы должны увидеть что-то, что отображает отношения, если вы этого не сделаете, просто добавьте это. (virtual)

Ваши модели

    public class Dto1
    {
        public int id { get; set; }
        public string Property2 { get; set; }
        public string Property3 { get; set; }
        public string Property4 { get; set; }
        public string Property5 { get; set; }

        public virtual Dto2 dto2{ get; set; }

    }

    public class Dto2
    {
        public int id { get; set; }
        public string PropertyB { get; set; }
        public string PropertyC { get; set; }
        public string PropertyD { get; set; }
        public string PropertyE { get; set; }
    }

Ваши модели просмотра

    public class Dto1ViewModel
    {
        public string Property1 { get; set; }
        public string Property2 { get; set; }

        public virtual Dto2VMForDto1 dto2{ get; set; }
    }

//Special ViewModel just for sliding into the above
    public class Dto2VMForDto1 
    {
        public int id { get; set; }
        public string PropertyB { get; set; }
        public string PropertyC { get; set; }
    }

Automapper выглядит так:

        cfg.CreateMap< Dto1, Dto1ViewModel>();
        cfg.CreateMap< Dto2, Dto2VMForDto1 >();

Я предполагаю, что вы получаете свои данные с помощью LinQ:

Dto1ViewModel thePageVM = (from entry in context.Dto1 where...).ProjectTo<Dto1ViewModel>();

Виола, все будет работать. На ваш взгляд, просто получить доступ с помощью model.dto2.PropertyB

person Worthy7    schedule 08.07.2016