前言

本部分描写叙述了EF怎样载入相关实体的细节,而且怎样在你的模型类中处理环形导航属性。(本部分预备了背景知识,而这不是完毕这个教程所必须的。你也能够跳到第五节)

预载入和延迟载入

预载入和延迟载入的英文名称各自是Eager Loading和Lazy Loading。

当EF与关系数据库一同使用时。了解EF是怎样载入相关数据是很重要的。

去查看EF生成的SQL查询也是很有帮助的。

为了追踪SQL,加入下列代码到BookServiceContext构造器中:

public BookServiceContext() : base("name=BookServiceContext")
{
// New code:
this.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
}

假设发送一个GET请求到/api/books,它返回像以下这种JSON:

[
{
"BookId": 1,
"Title": "Pride and Prejudice",
"Year": 1813,
"Price": 9.99,
"Genre": "Comedy of manners",
"AuthorId": 1,
"Author": null
},
...

你能看到Author属性是空的,即便book包括有效的AuthorId。

那是由于EF没有在载入相关的Author实体。关于SQL查询的跟踪日志例如以下:

SELECT
[Extent1].[BookId] AS [BookId],
[Extent1].[Title] AS [Title],
[Extent1].[Year] AS [Year],
[Extent1].[Price] AS [Price],
[Extent1].[Genre] AS [Genre],
[Extent1].[AuthorId] AS [AuthorId]
FROM [dbo].[Books] AS [Extent1]

该SQL跟踪在Visual Studio的Output窗体中显示。——译者注

SELECT语句从Books表中获取数据,但并没有引用Author表。

作为參考,这里是在BooksController类中的方法。它返回books的列表。

public IQueryable<Book> GetBooks()
{
return db.Books;
}

来看看我们怎样才干让Author作为返回的JSON数据的一部分。在Entity Framework中有三种方式载入相关数据:预载入(eager loading)、延迟载入(lazy loading)和显式载入(explicit loading)。我们应该在这三种技术中有所取舍,所以了解它们是怎样工作的就很重要了。

Eager Loading(预载入)

在预载入中,EF载入相关数据作为初始化数据库查询的一部分。

为了运行预载入,使用System.Data.Entity.Include扩展方法。

public IQueryable<Book> GetBooks()
{
return db.Books
// new code:
.Include(b => b.Author);
}

这会告诉EF将Author数据包括在查询中。假设你做了这个改变并运行了app。如今JSON数据会是例如以下所看到的:

[
{
"BookId": 1,
"Title": "Pride and Prejudice",
"Year": 1813,
"Price": 9.99,
"Genre": "Comedy of manners",
"AuthorId": 1,
"Author": {
"AuthorId": 1,
"Name": "Jane Austen"
}
},
...

其跟踪日志显示EF在Book和Author表中运行了一个join操作。

SELECT
[Extent1].[BookId] AS [BookId],
[Extent1].[Title] AS [Title],
[Extent1].[Year] AS [Year],
[Extent1].[Price] AS [Price],
[Extent1].[Genre] AS [Genre],
[Extent1].[AuthorId] AS [AuthorId],
[Extent2].[AuthorId] AS [AuthorId1],
[Extent2].[Name] AS [Name]
FROM [dbo].[Books] AS [Extent1]
INNER JOIN [dbo].[Authors] AS [Extent2] ON [Extent1].[AuthorId] = [Extent2].[AuthorId]

Lazy Loading(延迟载入)

在延迟载入中,当实体的导航属性是非关联时,EF会自己主动载入一个相关的实体。为了使用延迟载入。使导航属性变成虚拟的。

比如。在Book类中:

public class Book
{
// (Other properties) // Virtual navigation property
public virtual Author Author { get; set; }
}

如今考虑例如以下代码:

var books = db.Books.ToList();  // Does not load authors
var author = books[0].Author; // Loads the author for books[0]

当延迟载入开启时。在books[0]上訪问Author属性会使EF为author查询数据库。

延迟载入须要多段数据库操作过程。由于每次EF发送一个查询它都会取出一次相关实体。

通常。你希望为序列化的对象禁用延迟载入。序列化已经在模型上读取了全部可能触发载入相关实体的属性。比如,以下是当延迟载入开启后EF序列化books列表时的SQL查询。

你能够看到EF对于三个作者做了三次不同的查询。

SELECT
[Extent1].[BookId] AS [BookId],
[Extent1].[Title] AS [Title],
[Extent1].[Year] AS [Year],
[Extent1].[Price] AS [Price],
[Extent1].[Genre] AS [Genre],
[Extent1].[AuthorId] AS [AuthorId]
FROM [dbo].[Books] AS [Extent1] SELECT
[Extent1].[AuthorId] AS [AuthorId],
[Extent1].[Name] AS [Name]
FROM [dbo].[Authors] AS [Extent1]
WHERE [Extent1].[AuthorId] = @EntityKeyValue1 SELECT
[Extent1].[AuthorId] AS [AuthorId],
[Extent1].[Name] AS [Name]
FROM [dbo].[Authors] AS [Extent1]
WHERE [Extent1].[AuthorId] = @EntityKeyValue1 SELECT
[Extent1].[AuthorId] AS [AuthorId],
[Extent1].[Name] AS [Name]
FROM [dbo].[Authors] AS [Extent1]
WHERE [Extent1].[AuthorId] = @EntityKeyValue1

但还有很多时候你可能想要使用延迟载入。

预载入会造成EF生成很复杂的联接。或者你可能须要对于小的数据集合的相关实体。延迟载入会更加有效。

避免序列化问题的一种方式是序列化传输数据对象(DTOs)而不是实体对象。我将会在后面的文章中展示这种实现。

显式载入(Explicit Loading)

显式载入和延迟载入很相似,除了你在代码中显式地获取相关数据;当你訪问导航属性时它不会自己主动发生。

显示载入会在载入相关数据时给你很多其它的控制权,但也须要额外的代码。关于显示载入的很多其它信息,请查看Loading Related Entities。

http://msdn.microsoft.com/en-us/data/jj574232#explicit

导航属性和环形引用(Navigation Properties and Circular References)

当我定义Book和Author模型时。我在Book类中为Book-Author关系定义了导航属性。但我没有在其它方向定义导航属性。

假设你在Author类中也定义对应的导航属性会怎样呢?

public class Author
{
public int AuthorId { get; set; }
[Required]
public string Name { get; set; } public ICollection<Book> Books { get; set; }
}

不幸的是。当你在序列化模型时这会产生一个问题。

假设你载入相关数据,它会产生环形对象图。

当JSON或XML格式试图序列化图时。它将会抛出一个异常。

这两个格式抛出不同异常信息。这里是JSON格式的演示样例:

{
"Message": "An error has occurred.",
"ExceptionMessage": "The 'ObjectContent`1' type failed to serialize the response body for content type
'application/json; charset=utf-8'.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": null,
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "Self referencing loop detected with type 'BookService.Models.Book'.
Path '[0].Author.Books'.",
"ExceptionType": "Newtonsoft.Json.JsonSerializationException",
"StackTrace": "...”
}
}

这里是XML格式的演示样例:

<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>The 'ObjectContent`1' type failed to serialize the response body for content type
'application/xml; charset=utf-8'.</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace />
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>Object graph for type 'BookService.Models.Author' contains cycles and cannot be
serialized if reference tracking is disabled.</ExceptionMessage>
<ExceptionType>System.Runtime.Serialization.SerializationException</ExceptionType>
<StackTrace> ... </StackTrace>
</InnerException>
</Error>

一个解决方式是使用DTO,我将会在下一节中描写叙述它。

你能够配置JSON或XML格式化程序来处理图循环。关于很多其它信息,请查看Handling Circular Object References. (http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization#handling_circular_object_references)

对于本教程。你不须要Author.Book导航熟悉,所以你能够去掉它。

【Web API系列教程】3.4 — 实战:处理数据(处理实体关系)的更多相关文章

  1. ASP.NET Web API系列教程目录

    ASP.NET Web API系列教程目录 Introduction:What's This New Web API?引子:新的Web API是什么? Chapter 1: Getting Start ...

  2. ASP.NET Web API系列教程(目录)(转)

    注:微软随ASP.NET MVC 4一起还发布了一个框架,叫做ASP.NET Web API.这是一个用来在.NET平台上建立HTTP服务的Web API框架,是微软的又一项令人振奋的技术.目前,国内 ...

  3. [转]ASP.NET Web API系列教程(目录)

    本文转自:http://www.cnblogs.com/r01cn/archive/2012/11/11/2765432.html 注:微软随ASP.NET MVC 4一起还发布了一个框架,叫做ASP ...

  4. 【Web API系列教程】3.3 — 实战:处理数据(建立数据库)

    前言 在本部分中,你将在EF上使用Code First Migration来用測试数据建立数据库. 在Tools文件夹下选择Library Package Manager,然后选择Package Ma ...

  5. Web Api系列教程第2季(OData篇)(二)——使用Web Api创建只读的OData服务

    前言 很久没更新了,之前有很多事情,所以拖了很久,非常抱歉.好了,废话不多说,下面开始正题.本篇仍然使用上一季的的项目背景(系列地址http://www.cnblogs.com/fzrain/p/34 ...

  6. Web Api系列教程第2季(OData篇)(一)——OData简介和一个小应用

    第一季的链接以及系列导航:http://www.cnblogs.com/fzrain/p/3490137.html 在这里,首先要感谢Taiseer Joudeh不断的为我们带来最新的技术分享,楼主对 ...

  7. 【转】ASP.NET WEB API系列教程

    from: 西瓜小强 http://www.cnblogs.com/risk/category/406988.html ASP.NET Web API教程(六) 安全与身份认证 摘要: 在实际的项目应 ...

  8. [转]Web Api系列教程第2季(OData篇)(二)——使用Web Api创建只读的OData服务

    本文转自:http://www.cnblogs.com/fzrain/p/3923727.html 前言 很久没更新了,之前有很多事情,所以拖了很久,非常抱歉.好了,废话不多说,下面开始正题.本篇仍然 ...

  9. 【Web API系列教程】1.1 — ASP.NET Web API入门

    前言 HTTP不仅仅服务于web页面.同一时候也是构建暴露服务和数据的API的强大平台.HTTP有着简单.灵活和无处不在的特点.你能想到的差点儿全部平台都包括有一个HTTP库.所以HTTP服务能够遍及 ...

  10. Hello Web API系列教程——Web API与国际化

    软件国际化是在软件设计和文档开发过程中,使得功能和代码设计能处理多种语言和文化习俗,在创建不同语言版本时,不需要重新设计源程序代码的软件工程方法.这在很多成熟的软件开发平台中非常常见.对于.net开发 ...

随机推荐

  1. session 的工作原理

    session.cookie_domain session.cookie_path session.name session.save_path session.use_cokies session_ ...

  2. Working with macro signatures

    https://docs.kentico.com/k11/macro-expressions/troubleshooting-macros/working-with-macro-signatures ...

  3. 利用SharedPreferences完成记住账号密码的功能

    利用SharedPreferences完成记住账号密码的功能 效果图: 记住密码后,再次登录就会出现账号密码,否则没有. 分析: SharedPreferences可将数据存储到本地的配置文件中 Sh ...

  4. 名词解释 —— 抄送(cc)、银弹(silver bullet)

    抄送(Carbon Copy,carbon copy 本身的含义是复写本,副本),又简称为 CC. 在现代汉语中,含有抄写与送达的双重意思. 在网络术语中,抄送就是将邮件同时发送给收信人以外的人, 用 ...

  5. Tool-Java:Eclipse

    ylbtech-Tool-Java:Eclipse Eclipse 是一个开放源代码的.基于Java的可扩展开发平台.就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境.幸运的是,E ...

  6. H3C交换机DHCP&nbsp;Server配置的六个方面

    H3C交换机DHCP Server配置的六个方面 在交换机上面配置DHCP内容是司空见惯的了.那么这里我们就讲解一下H3C交换机DHCP Server配置内容.之后的文章中,我们还对针对其他方面的配置 ...

  7. POJ-3061 Subsequence 二分或尺取

    题面 题意:给你一个长度为n(n<100000)的数组,让你找到一个最短的连续子序列,使得子序列的和>=m  (m<1e9) 题解: 1 显然我们我们可以二分答案,然后利用前缀和判断 ...

  8. 最详细的CentOS 6与7对比(一):常见设置对比

    本主题将从3个角度进行对比 常见设置(CentOS 6 vs CentOS 7) 服务管理(Sysvinit vs Upstart vs Systemd) 性能测试(cpu/mem/io/oltp) ...

  9. 禁止button标签提交form表单,变成普通按钮

    button有个type属性,属性值可为button.submit.reset button=普通按钮,直接点击不会提交表单submit=提交按钮,点击后会提交表单reset=表单复位 当button ...

  10. Java基础10一面向对象

    抽象 概念:当一个类中没有足够的信息描述一个现实生活中具体存在的事物,那么这个类就是抽象类. 抽象类一般是对概念领域中的描述. 语法: [访问修饰符] abstract class 类名{ } 如: ...