我是MVC的新手,我正在使用Web API 2的代码优先方法。这是我的模型:
儿童模特.cs
[DataContract(IsReference = true)]
public class ChildModel
{
//other fields definition here
//relationship definition
[DataMember]
public int ParentModelID {get; set;}
[DataMember]
public virtual ParentModel ParentModel {get; set;}
}
那么这是我的控制器操作:
public IEnumerable<ChildModel> Get()
{
return db.ChildModels.ToList();
}
所以数据库生成没有问题。它创建表。我可以保存和编辑。只有一个问题,即"获取控制器"操作。当我从前端访问它时,返回的 JSON 如下所示:
[
{$id: 1, Field1: "", Field2: ""},
{$ref: 3}
...
{$id: 10, Field1: "", Field2: ""},
{$ref: 12}
]
它就这样交替进行。第一个 JSON 已完成,然后下一个只是一个$ref字段。可能是什么原因造成的?我还注意到,当我在子模型中删除父模型的导航属性时,除了父模型没有预先加载到子模型的 JSON 中之外,一切似乎都很好。
希望有人能帮助我。
谢谢!
首先,您并不急于在任何地方加载父模型,因为查询中没有Include
。当您尝试将数据库对象直接返回到 API 时,该 API 将尝试将它们序列化为 Json 或 XML,从而对您的对象进行全面扫描。
要解决您的问题,您可以这样做:
public IEnumerable<ChildModel> Get()
{
db.Configuration.LazyLoadingEnabled = false;
try
{
return db.ChildModels.Include("ParentModel").ToList();
}
finally
{
db.Configuration.LazyLoadingEnabled = true;
}
}
我负责禁用和重新启用延迟加载,因为您似乎正在与控制器构造函数中的依赖项注入共享
db
。
序列化程序通常在没有IsReference = true
的情况下执行的操作是:
- 序列化子 ID 1
- 序列化子
ParentModel
(访问它会触发延迟加载) - 序列化父模型(延迟加载)
- 序列化父模型的
ChildModel
集合 - 最终将找到子项 ID 1
- 再次序列化子 ID 1
无限循环。通过应用IsReference = true
可以避免这种情况,因为一旦序列化程序检测到以前序列化的对象,它就不会再次序列化它,而是填充如下内容:
$ref: 3
。表示该对象引用 Id 3 中的另一个对象。
考虑到 ID 为 3 的孩子与 ID 1 具有相同的父级,那么就会发生这种情况:
- 序列化子 ID 1
- 序列化子
ParentModel
(访问它会触发延迟加载) - 序列化父模型(延迟加载)
- 序列化父模型的
ChildModel
集合(触发延迟加载) - ChildModel 的集合是延迟加载的(包含两个子项,ID 1 和 3)
- 将
ref
添加到子项 1(以前已序列化) - 序列化子 ID 3
- 完成的父模型子序列化
- 完成的父模型序列化
完成的子级 ID 1 序列化
将
ref
添加到子项 3(以前已序列化)- 序列化子 ID 10
- 。继续序列化其他对象
这就是为什么我禁用了延迟加载并使用Include()
加载父模型。
现在,发生这种情况只是因为您尝试将数据库对象直接返回到 API。这就是为什么人们通常使用投影来返回DTO/ViewModel对象。
如果我没记错的话,EF 使用延迟加载。 在调用列表之前,请尝试将.Include(c => c.ParentModel)
添加到控制器操作中。
这应该告诉 EF 解析引用并提取完整的对象。