您的问题的简单答案是 - 使用.Include()。
例如:假设您有一个 Customer 对象,该对象具有关联的 ReferredBy 对象,该对象引用另一个客户。在您的应用程序中,您希望返回 Customers 的列表以及相关联的 Customer 是谁推荐了每个人。
您的 WebAPI 方法可能类似于:
[HttpGet]
public IQueryable<Customer> Customers() {
return db.Customers.OrderBy(c => c.LastName).Top(10);
}
当序列化程序掌握这一点时,您可能会遇到各种错误,您可以阅读有关in this article 的更多信息。但从本质上讲,它来自两件事:
- ProxyCreation / LazyLoading - EF 表示“仅在我请求时才按需加载我的对象图的关联对象”,并且
- 循环序列化——这意味着——
A 指的是B,B 指的是A——所以每次我序列化一个循环时,我都会再次序列化另一个循环作为它的子节点。这会创建一个无限循环。
我不会详细介绍所有细节或其他问题 - 我已经给您提供了一篇深入探讨它的文章。相反,这是我在应用程序中解决问题的方法:
- 使用 JSON.net 作为您的序列化程序。您可以参考this link,了解如何在 Visual Studio 中将其设置为项目的默认序列化程序(假设尚未设置)
-
在您的Global.asax 或作为配置的一部分加载的.cs 文件之一中,使用以下设置:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
这告诉 JSON.net 不要序列化引用循环。
-
将DataContext 中的MergeOption 及其每个Collection 属性设置为MergeOption.NoTracking。在我的应用程序中,我通过编辑创建 DataContext 的 .tt 文件来做到这一点:
首先,找到创建构造函数的那一行,改成:
MergeOption _defaultMergeOption = MergeOption.AppendOnly;
public <#=code.Escape(container)#>() : this("name=<#=container.Name#>") { }
public <#=code.Escape(container)#>(String connectionString) : base(connectionString) {
<# if (!loader.IsLazyLoadingEnabled(container)) { #>
this.Configuration.LazyLoadingEnabled = false;
<# } #>
this.Configuration.ProxyCreationEnabled = false;
_defaultMergeOption = MergeOption.NoTracking;
}
找到以:开头的行
<# foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>()) { #>
现在编辑接下来的几行说:
<# foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>()) { #>
<#= Accessibility.ForReadOnlyProperty(entitySet)#> ObjectQuery<<#=typeMapper.GetTypeName(entitySet.ElementType)#>> <#=code.Escape(entitySet)#> {
get {
var set = (((IObjectContextAdapter)this).ObjectContext).CreateObjectSet<<#=typeMapper.GetTypeName(entitySet.ElementType)#>>();
set.MergeOption = _defaultMergeOption;
return set;
}
}
<# }
这样做的目的是为您的DataContext 提供一个构造函数,它可以将所有集合默认为MergeOption.NoTracking,并自动禁用ProxyCreation 和LazyLoading。当您创建 DataContext 的实例以从中提取时,您现在可以简单地说:
var db = new MyDataContext("ConnectionStringGoesHere");
鉴于上述情况,您的新 WebAPI 方法变得如此简单:
[HttpGet]
public IQueryable<Customer> Customers() {
return db.Customers.Include("ReferredBy")
.OrderBy(c => c.LastName).Top(10);
}
.Include() 将加载子记录作为初始 SQL 语句的一部分(总共对数据库进行一次命中),并且序列化程序将忽略反向引用,从而允许您生成类似于以下内容的 JSON:
[{
Id: 1,
FirstName: 'Frank',
LastName: 'Abba',
ReferredBy: {
Id: 4,
FirstName: 'Bob',
LastName: 'Jones',
ReferredBy: null
}
}, {
Id: 4,
FirstName: 'Bob',
LastName: 'Jones',
ReferredBy: null
}
}]