使用EF和WebApi将父/子对象序列化

时间:2015-05-21 13:55:20

标签: c# json entity-framework serialization asp.net-web-api

我在实体框架中有以下模型:

public class Customer
{
[XmlIgnore]
public virtual ICollection<Customer> Children { get; set; }

public string Name { get; set; }
}

现在我尝试使用web api序列化它:

public class CustomerController:ApiController {
   public HttpResponseMessage GetAll()
   {
     using (var tc = new DataContext())
     {
        List<Customer> allCustomers = tc.Customers.ToList();
        return Request.CreateResponse(HttpStatusCode.OK, allCustomers);              
     }
   }
}

当我这样做并使用POST调用该方法时 我收到以下错误:

  

&#39; ObjectContent`1&#39;类型无法序列化响应正文   内容类型&#39; application / json;字符集= UTF-8

     

InnerException:&#34;错误   从儿童中获取价值&#39;上   &#39; System.Data.Entity.DynamicProxies.Customer&#34;

     

InnerException(2):&#34; The   ObjectContext实例已被释放,不能再使用   需要连接的操作。&#34;

customers.Children目前是一个空列表。

我猜这个问题的出现是因为儿童与客户的类型相同,导致了一个&#34;无限序列化循环&#34;。 (我没有更好的词来描述)

我已经尝试过XmlIgnore来阻止该属性被序列化但没有效果。

2 个答案:

答案 0 :(得分:4)

不要将导航属性声明为virtual或禁用延迟加载行为。默认情况下启用延迟加载,通过创建派生代理类型的实例,然后覆盖virtual属性以添加加载挂钩来实现。因此,如果您想使用XML序列化程序,我建议您关闭延迟加载:

public class YourContext : DbContext 
{ 
    public YourContext() 
    { 
        this.Configuration.LazyLoadingEnabled = false; 
    } 
}

如果您要加载相关实体(Children),可以使用Include扩展方法作为查询的一部分。此行为称为预先加载

using System.Data.Entity;  // For extension method `Include`

List<Customer> allCustomers = tc.Customers.Include(c=>c.Children).ToList();

这些链接可以帮助您更好地理解我在答案中解释的内容:

如果从导航属性中删除virtual关键字,则POCO实体不符合第二个链接中描述的要求,因此,EF不会创建代理类来延迟加载导航属性。但是,如果您禁用了延迟加载,即使您的导航属性为virtual,也不会在任何实体中加载它们。使用序列化程序时禁用延迟加载是个好主意。大多数序列化程序通过访问类型实例上的每个属性来工作。

答案 1 :(得分:1)

仅为了知识: 以下也有效:

public class Customer
{
[XmlIgnore,JSonIgnore]
public virtual ICollection<Customer> Children { get; set; }

public string Name { get; set; }
}

神奇的是那里的“JsonIgnore”。

尽管如此:@octavioccl的答案是一个更好的解决方案,需要更多的工作,但创造更好的代码。