Циклическая ссылка, вызывающая переполнение стека с помощью 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. Затем Auto mapper сопоставляет свойство "Order" по строкам, которое возвращается обратно к Order, которое затем кругово отображает обратно на OrderLine, затем на Order и т.д.

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

Ответ 1

В вашей конфигурации 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;
         });

Ответ 2

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

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

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

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.

Ответ 3

что, если круговые ссылки - это List или Collection? или как один навигационный объект? что-то вроде n до n или от 1 до 1;

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

или обе ссылки для типа коллекции

OrderDTO.cs
public virtual IList<OrderLineDTO> OrderLineDTOs { get; set; } <-- this refs the lines
OrderLineDTO.cs
public virtual IList<OrderDTO> OrderDTOs { get; set; } <-- this refs the lines

Ответ 4

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

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

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

Ответ 5

У меня возникла та же проблема, и я решил ее путем понижения до версии 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+ свяжитесь с командой автопроизводителя.