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

Ole*_*ers 4 c# serialization json entity-framework asp.net-web-api

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

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

public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

现在我尝试使用 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);              
     }
   }
}
Run Code Online (Sandbox Code Playgroud)

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

“ObjectContent`1”类型无法序列化内容类型“application/json”的响应主体;字符集=utf-8

InnerException:“从‘System.Data.Entity.DynamicProxies.Customer’上的‘Children’获取值时出错”

InnerException(2):“ObjectContext 实例已被释放,不能再用于需要连接的操作。”

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

我猜这个问题的发生是因为 Children 与 Customer 属于同一类型,导致“无限序列化循环”。(我没有更好的词来形容)

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

oct*_*ccl 5

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

public class YourContext : DbContext 
{ 
    public YourContext() 
    { 
        this.Configuration.LazyLoadingEnabled = false; 
    } 
}
Run Code Online (Sandbox Code Playgroud)

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

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

List<Customer> allCustomers = tc.Customers.Include(c=>c.Children).ToList();
Run Code Online (Sandbox Code Playgroud)

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

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