使用Automapper导致堆栈溢出的循环引用

时间:2012-07-16 13:15:05

标签: c# circular-reference csla automapper-2

我正在使用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对象分别映射到OrderOrderLines CSLA对象

自动映射到OrderLines时会映射OrderLinesDTO列表。然后,自动映射器会映射行上的"Order"属性,然后映射回Order,然后循环映射回OrderLine,然后回到Order,依此类推

有人知道Automapper是否可以避免此循环引用吗?

5 个答案:

答案 0 :(得分:23)

在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;
         });

答案 1 :(得分:1)

由于这是Google搜索结果的第一名,我认为可能会有像我这样的人来到这里,他们没有遇到stackoverflow异常,但是在将对象(通过ASP.NET)发送到客户端,因此将其序列化为JSON。

因此,我具有相同的结构,Invoice具有多个InvoiceLines,当我加载Invoice并使用Linq-to-SQL .Include(x => x.InvoiceLines)时,尝试从Api加载对象时发生错误,因为每个InvoiceLine再次包含相同的Invoice

要解决此问题,请在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);

因此,在将MVC添加到应用程序时,请在o.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;中包含JsonConfiguration

  

JSON.Net正在采取额外的步骤,以使用称为“ $ id”的附加元属性来设置每个引用。当JSON.Net在对象图中的另一个位置遇到相同的实例时,它只是删除对原始实例的引用,而不是复制数据,因此不会引起循环引用问题!

来源:https://johnnycode.com/2012/04/10/serializing-circular-references-with-json-net-and-entity-framework/

所以现在我不必进一步编辑AutoMapper的配置。

答案 2 :(得分:1)

我在使用EF 6和AutoMapper 6时遇到了同样的问题。显然,肯尼·卢塞罗(Kenny Lucero)发布的内容使我找到了解决方案。这是AM网站的摘录:

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

在两个模型中添加PreserveReferences()使其起作用。

答案 3 :(得分:0)

不确定是否应该在此处发布:

在方法中执行automapper.map后,我遇到了相同的错误。 CularBytes的答案让我想到这个问题与automapper不相关,而与json有关。

我做到了:

Return ok(_service.getDataById(id));

代替

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

(我忘记等待asyc通话...我知道是菜鸟错误)

答案 4 :(得分:-1)

我遇到了同样的问题,并通过降级到4.2.1解决了该问题。 循环引用的检查显然很昂贵,因此默认情况下不进行检查。 Migrating to AutoMapper 5 - Circular references

假设这些应该是v 5+的设置方法,但不适用于我的数据模型,因为我们选择了复杂的dto关系,而不是每个动作都使用一次dto。

// 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团队。