使用 EF 和 WebApi 将 Parent/Child 对象序列化为
Serializing Parent/Child Object to with EF and WebApi
我在 entity framework 中有以下模型:
public class Customer
{
[XmlIgnore]
public virtual ICollection<Customer> Children { get; set; }
public string Name { get; set; }
}
现在我尝试使用网络将其序列化 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 调用方法时
我收到以下错误:
The 'ObjectContent`1' type failed to serialize the response body for
content type 'application/json; charset=utf-8
InnerException: "Error
getting value from 'Children' on
'System.Data.Entity.DynamicProxies.Customer"
InnerException(2): "The
ObjectContext instance has been disposed and can no longer be used for
operations that require a connection."
customers.Children 当前是一个空列表。
我猜这个问题的发生是因为 Children 与 Customer 的类型相同,导致“无限序列化循环”。 (我找不到更好的词来形容了)
我已经尝试过 XmlIgnore 来阻止 属性 被序列化,但没有效果。
不要将导航 属性 声明为 virtual
或禁用 [=28=] 延迟加载 行为。默认情况下启用延迟加载,它是通过创建派生代理类型的实例然后覆盖 virtual
属性以添加加载挂钩来实现的。所以,如果你想使用 XML 序列化程序,我建议你关闭延迟加载:
public class YourContext : DbContext
{
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
如果您想加载相关实体 (Children
),您可以使用 Include
扩展方法作为查询的一部分。此行为称为 Eager Loading。
using System.Data.Entity; // For extension method `Include`
List<Customer> allCustomers = tc.Customers.Include(c=>c.Children).ToList();
这些 link 可以帮助您更好地理解我在回答中解释的内容:
如果从导航属性中删除 virtual
关键字,POCO 实体将不满足第二个 link 中描述的要求,因此,EF 不会创建代理 class 延迟加载您的导航属性。但是如果你禁用延迟加载,即使你的导航属性是 virtual
,它们也不会被加载到任何实体中。使用序列化器时禁用延迟加载是个好主意。大多数序列化程序通过访问类型实例上的每个 属性 来工作。
仅供参考:
以下也适用:
public class Customer
{
[XmlIgnore,JSonIgnore]
public virtual ICollection<Customer> Children { get; set; }
public string Name { get; set; }
}
魔术就在"JsonIgnore"那里。
尽管如此:@octavioccl 的答案是一个更好的解决方案,它需要更多的工作,但可以创建更好的代码。
我在 entity framework 中有以下模型:
public class Customer
{
[XmlIgnore]
public virtual ICollection<Customer> Children { get; set; }
public string Name { get; set; }
}
现在我尝试使用网络将其序列化 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 调用方法时 我收到以下错误:
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8
InnerException: "Error getting value from 'Children' on 'System.Data.Entity.DynamicProxies.Customer"
InnerException(2): "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."
customers.Children 当前是一个空列表。
我猜这个问题的发生是因为 Children 与 Customer 的类型相同,导致“无限序列化循环”。 (我找不到更好的词来形容了)
我已经尝试过 XmlIgnore 来阻止 属性 被序列化,但没有效果。
不要将导航 属性 声明为 virtual
或禁用 [=28=] 延迟加载 行为。默认情况下启用延迟加载,它是通过创建派生代理类型的实例然后覆盖 virtual
属性以添加加载挂钩来实现的。所以,如果你想使用 XML 序列化程序,我建议你关闭延迟加载:
public class YourContext : DbContext
{
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
如果您想加载相关实体 (Children
),您可以使用 Include
扩展方法作为查询的一部分。此行为称为 Eager Loading。
using System.Data.Entity; // For extension method `Include`
List<Customer> allCustomers = tc.Customers.Include(c=>c.Children).ToList();
这些 link 可以帮助您更好地理解我在回答中解释的内容:
如果从导航属性中删除 virtual
关键字,POCO 实体将不满足第二个 link 中描述的要求,因此,EF 不会创建代理 class 延迟加载您的导航属性。但是如果你禁用延迟加载,即使你的导航属性是 virtual
,它们也不会被加载到任何实体中。使用序列化器时禁用延迟加载是个好主意。大多数序列化程序通过访问类型实例上的每个 属性 来工作。
仅供参考: 以下也适用:
public class Customer
{
[XmlIgnore,JSonIgnore]
public virtual ICollection<Customer> Children { get; set; }
public string Name { get; set; }
}
魔术就在"JsonIgnore"那里。
尽管如此:@octavioccl 的答案是一个更好的解决方案,它需要更多的工作,但可以创建更好的代码。