Циклическая ссылка, вызывающая переполнение стека с помощью 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+ свяжитесь с командой автопроизводителя.