Циклическая ссылка, вызывающая переполнение стека с помощью Automapper

Я использую Automapper для сопоставления моих прокси-объектов NHibernate (DTO) с моими бизнес-объектами CSLA.

Я использую Fluent NHibernate для создания сопоставлений - все работает нормально

У меня проблема в том, что Order имеет набор OrderLines, и каждый из них имеет ссылку на Order.

public class OrderMapping : ClassMap<OrderDTO>
{
    public OrderMapping()
    {
        // Standard properties
        Id(x => x.OrderId);
        Map(x => x.OrderDate);
        Map(x => x.Address);

        HasMany<OrderLineDTO>(x => x.OrderLines).KeyColumn("OrderId").Inverse();

        Table("`Order`");
    }
}

public class OrderDTO
{
    // Standard properties
    public virtual int OrderId { get; set; }
    public virtual DateTime OrderDate { get; set; }
    public virtual string Address { get; set; }

    // Child collection properties
    public virtual IList<OrderLineDTO> OrderLines { get; set; } <-- this refs the lines
}

а также:

public class OrderLineMapping : ClassMap<OrderLineDTO>
{
    public OrderLineMapping()
    {
        // Standard properties
        Id(x => x.OrderLineId);
        References<OrderDTO>(x => x.Order).Column("OrderId");
        Map(x => x.Description);
        Map(x => x.Amount);

        Table("`OrderLine`");
    }
}

public class OrderLineDTO
{
    // Standard properties
    public virtual int OrderLineId { get; set; }
    public virtual string Description { get; set; }
    public virtual decimal Amount { get; set; }

    public virtual OrderDTO Order { get; set; } // <-- this refs the order
}

Эти объекты DTO сопоставляются с объектами Order и OrderLines CSLA соответственно.

При автоматическом сопоставлении с OrderLines сопоставляется список OrderLinesDTO. Затем автоматический сопоставитель сопоставляет свойство "Order" строк, которое сопоставляется обратно с Order, которое затем циклически сопоставляется с OrderLine, затем с Order и так далее.

Кто-нибудь знает, может ли Automapper избежать этой циклической ссылки?


person Charleh    schedule 16.07.2012    source источник
comment
Подождите, чертов клавиатурный сосок запостил это до того, как я закончил, дурацкий ноутбук!   -  person Charleh    schedule 16.07.2012
comment
исключение? куча? ....??   -  person    schedule 16.07.2012
comment
Без контекста, так сложно дать полный ответ... может быть, просто [IgnoreMap] свойство, которое вызывает круг?   -  person Marc Gravell    schedule 16.07.2012
comment
Итак, у моего ноутбука один из этих синих сосков, и мышь зависла над кнопкой «Задать вопрос» — любое действие рядом с центром клавиатуры может вызвать случайный «щелчок»! Не знал об атрибуте [IgnoreMap]. Я создаю код для некоторых классов, поэтому я посмотрю, смогу ли я подключить это к генератору, если он работает.   -  person Charleh    schedule 16.07.2012
comment
На самом деле похоже, что [IgnoreMap] всегда игнорирует сопоставление со свойством - я хочу иметь возможность сопоставлять свойство Order с моим OrderLines, но это свойство Order содержит ссылку на родителя строк, поэтому существует циклическая ссылка   -  person Charleh    schedule 16.07.2012
comment
Насколько я вижу, это не сработает - я вызываю Map() более одного раза (дочерние объекты берутся из IList для заполнения типов коллекций CSLA). Automapper отслеживает ссылки, когда делается один вызов Map, но не будет работать, когда несколько вызовов Map выполняются в одном и том же прогоне сопоставления. Я посмотрю, смогу ли я переделать код   -  person Charleh    schedule 16.07.2012
comment
В настоящее время (AM 6.1.1) правильный ответ это.   -  person Lucian Bargaoanu    schedule 27.07.2017


Ответы (5)


У меня была такая же проблема с использованием EF 6 и AutoMapper 6. Очевидно, то, что опубликовал Кенни Лусеро, привело меня к решению. Вот выдержка с сайта AM:

// Circular references between users and groups
cfg.CreateMap<User, UserDto>().PreserveReferences();

Добавление PreserveReferences() к обеим моделям заставило его работать.

person kurdemol94    schedule 21.07.2019
comment
Как сказано в документах, в более новых версиях это работает по умолчанию без каких-либо настроек. - person Lucian Bargaoanu; 21.07.2019
comment
Поскольку это лучший результат поиска в Google по этой проблеме, я переместил принятый ответ на этот, поскольку он более правильный в данный момент времени, и я бы не хотел, чтобы люди сразу переходили к исходному ответу, который работал в то время без учитывая новую информацию (в этом постоянно развивающемся ландшафте технологий!) - person Charleh; 23.10.2019

В вашей конфигурации Automapper:

Mapper.Map<OrderLine, OrderLineDTO>()
    .ForMember(m => m.Order, opt => opt.Ignore());

Mapper.Map<Order, OrderDTO>()
    .AfterMap((src, dest) => { 
         foreach(var i in dest.OrderLines) 
             i.Order = dest;
         });
person Steve Czetty    schedule 16.07.2012
comment
Спасибо за это, Стив, но я надеялся не привязываться к типу, так как я пытаюсь сопоставить с помощью соглашения через общий тип и свести объем кода сопоставления практически к нулю. Мои дженерики не будут знать никаких имен свойств в производных типах, поэтому, возможно, придется создать виртуальный метод для производного типа для поддержания этой ассоциации. - person Charleh; 16.07.2012
comment
Я приму это как ответ - я использовал этот подход, но мне удалось избежать написания этого вручную, используя code-gen для создания этих отношений. - person Charleh; 16.07.2012
comment
Не могли бы вы уточнить? Что именно для чего нужно? Я хочу, чтобы Invoice включал InvoiceLines, но не включал Invoice снова. Однако, когда я загружаю счет-фактуру, он по-прежнему должен иметь возможность также получать в нем счет-фактуру, но не включать счета-фактуры этого счета снова. У меня отключена ленивая загрузка (EF), поэтому EF должен следить за тем, чтобы загружались только те Я включил. Подобные вещи заставляют меня задуматься о том, чтобы вернуться к ручному управлению. - person CularBytes; 16.04.2019

У меня была такая же проблема, и я решил ее, перейдя на версию 4.2.1. очевидно, проверка циклических ссылок была дорогой, поэтому по умолчанию они не проверяли. Переход на AutoMapper 5 – циклические ссылки

Предположительно, это должны быть методы настройки для v 5+, но это не сработало для моей модели данных, потому что мы выбрали сложные отношения dto вместо одноразового использования dtos для каждого действия.

// Self-referential mapping
cfg.CreateMap<Category, CategoryDto>().MaxDepth(3);

// Circular references between users and groups
cfg.CreateMap<User, UserDto>().PreserveReferences();

http://docs.automapper.org/en/stable/5.0-Upgrade-Guide.html#circular-references

Предполагается, что Automapper может статически определять настройки циклической ссылки в v6.1+. Поэтому, если это не работает автоматически в версии v6.1+, свяжитесь с командой automapper.

person Kenny Lucero    schedule 03.11.2018
comment
Как сказано в документах, в более новых версиях это работает по умолчанию без каких-либо настроек. - person Lucian Bargaoanu; 03.11.2018
comment
Да, и если это не так, они поощряют разработчиков отправлять заявку, потому что программное обеспечение не идеально и может не работать для вашей модели данных по умолчанию. Но если у вас крайний срок, а команда автомаппера не отвечает вовремя, откат может помочь. - person Kenny Lucero; 03.11.2018
comment
Нет известной проблемы AM с циклическими ссылками. Ошибка использования гораздо более вероятна. - person Lucian Bargaoanu; 04.11.2018

Поскольку это результат поиска Google № 1, я думаю, что могут быть некоторые люди, такие как я, которые приходят сюда, которые не получают исключение stackoverflow, но сталкиваются с проблемами при отправке объекта (через ASP.NET) клиенту и таким образом, он сериализуется в формате JSON.

Итак, у меня была такая же структура, Invoices имеет несколько InvoiceLines, когда я загружаю Invoice и использую Linq-to-SQL .Include(x => x.InvoiceLines), я получаю ошибки при попытке загрузить объект из API, потому что каждый InvoiceLine снова содержит один и тот же Invoice.

Чтобы решить эту проблему, выполните следующие действия в классе запуска ASP.NET Core:

services.AddMvc().AddJsonOptions(o =>
{
    o.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    o.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
    o.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
    // ^^ IMPORTANT PART ^^
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Поэтому включите o.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects; в JsonConfiguration при добавлении MVC в ваше приложение.

JSON.Net делает дополнительный шаг для настройки каждой ссылки с дополнительным мета-свойством под названием «$id». Когда JSON.Net встречает тот же экземпляр в другом месте графа объектов, он просто удаляет ссылку на исходный экземпляр вместо дублирования данных и, таким образом, не вызывает проблем с циклическими ссылками!

Источник: https://johnnycode.com/2012/04/10/serializing-circular-references-with-json-net-and-entity-framework/

Так что теперь мне не нужно дополнительно редактировать мою конфигурацию AutoMapper.

person CularBytes    schedule 16.04.2019
comment
Спасибо тебе за это. Ваш ответ заставил меня понять, что ошибка не связана с автомаппером! - person Enrico; 18.12.2019

Не знаю, стоит ли размещать это здесь:

У меня была такая же ошибка после выполнения automapper.map в методе. Ответ CularBytes заставил меня подумать, что проблема связана не с automapper, а с json.

Я сделал:

Return ok(_service.getDataById(id));

вместо

Return ok(await _service.getDataById(id));

(Я забыл дождаться вызова asyc... ошибка новичка, которую я знаю)

person Enrico    schedule 18.12.2019