鍍金池/ 教程/ C#/ 前言
前言
在本節(jié),你將開(kāi)始為app定義HTML,并在HTML和視圖模型間添加數(shù)據(jù)綁定。
在本節(jié),你將使用HTML、JavaScript和Knockout.js庫(kù)為應(yīng)用程序創(chuàng)建客戶端。我們將按如下步驟建立客戶端應(yīng)用:
前言
前言
http://www.asp.net/web-api/overview/formats-and-model-binding/mo
在本節(jié),你將添加讓用戶可以創(chuàng)建新book的功能。在app.js中,添加如下代碼到視圖模型:
在本節(jié),你將添加查看每本書的詳細(xì)信息的功能。在app.js中,添加以下代碼到視圖模型:
前言
這篇文章描述了ASP.NET Web API如何將HTTP請(qǐng)求發(fā)送(路由)到控制器。
前言
前言
這篇文章描述了ASP.NET Web API如何將HTTP請(qǐng)求路由到控制器上的特定動(dòng)作。
前言
在這最后一節(jié)中,你將把應(yīng)用程序發(fā)布到Azure。在Solution Explorer中,右擊項(xiàng)目并選擇Publish。
前言
總結(jié)

前言

前言

本部分描述了EF如何加載相關(guān)實(shí)體的細(xì)節(jié),并且如何在你的模型類中處理環(huán)形導(dǎo)航屬性。(本部分預(yù)備了背景知識(shí),而這不是完成這個(gè)教程所必須的。你也可以跳到第五節(jié))

預(yù)加載和延遲加載

預(yù)加載和延遲加載的英文名稱分別是Eager Loading和Lazy Loading。

當(dāng)EF與關(guān)系數(shù)據(jù)庫(kù)一同使用時(shí),了解EF是如何加載相關(guān)數(shù)據(jù)是非常重要的。

去查看EF生成的SQL查詢也是很有幫助的。為了追蹤SQL,添加下列代碼到BookServiceContext構(gòu)造器中:

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

如果發(fā)送一個(gè)GET請(qǐng)求到/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。那是因?yàn)镋F沒(méi)有在加載相關(guān)的Author實(shí)體。關(guān)于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窗口中顯示?!g者注

SELECT語(yǔ)句從Books表中獲取數(shù)據(jù),但并沒(méi)有引用Author表。 作為參考,這里是在BooksController類中的方法,它返回books的列表。

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

來(lái)看看我們?nèi)绾尾拍茏孉uthor作為返回的JSON數(shù)據(jù)的一部分。在Entity Framework中有三種方式加載相關(guān)數(shù)據(jù):預(yù)加載(eager loading)、延遲加載(lazy loading)和顯式加載(explicit loading)。我們應(yīng)該在這三種技術(shù)中有所取舍,所以了解它們是如何工作的就非常重要了。

Eager Loading(預(yù)加載)

在預(yù)加載中,EF加載相關(guān)數(shù)據(jù)作為初始化數(shù)據(jù)庫(kù)查詢的一部分。為了執(zhí)行預(yù)加載,使用System.Data.Entity.Include擴(kuò)展方法。

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

這會(huì)告訴EF將Author數(shù)據(jù)包含在查詢中。如果你做了這個(gè)改變并運(yùn)行了app,現(xiàn)在JSON數(shù)據(jù)會(huì)是如下所示:

[
  {
    "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表中執(zhí)行了一個(gè)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(延遲加載)

在延遲加載中,當(dāng)實(shí)體的導(dǎo)航屬性是非關(guān)聯(lián)時(shí),EF會(huì)自動(dòng)加載一個(gè)相關(guān)的實(shí)體。為了使用延遲加載,使導(dǎo)航屬性變成虛擬的。例如,在Book類中:

public class Book
{
    // (Other properties)

    // Virtual navigation property
    public virtual Author Author { get; set; }
}

現(xiàn)在考慮如下代碼:

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

當(dāng)延遲加載開(kāi)啟時(shí),在books[0]上訪問(wèn)Author屬性會(huì)使EF為author查詢數(shù)據(jù)庫(kù)。

延遲加載需要多段數(shù)據(jù)庫(kù)操作過(guò)程,因?yàn)槊看蜤F發(fā)送一個(gè)查詢它都會(huì)取出一次相關(guān)實(shí)體。通常,你希望為序列化的對(duì)象禁用延遲加載。序列化已經(jīng)在模型上讀取了所有可能觸發(fā)加載相關(guān)實(shí)體的屬性。例如,下面是當(dāng)延遲加載開(kāi)啟后EF序列化books列表時(shí)的SQL查詢。你可以看到EF對(duì)于三個(gè)作者做了三次不同的查詢。

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

但還有很多時(shí)候你可能想要使用延遲加載。預(yù)加載會(huì)造成EF生成非常復(fù)雜的聯(lián)接?;蛘吣憧赡苄枰獙?duì)于小的數(shù)據(jù)集合的相關(guān)實(shí)體,延遲加載會(huì)更加有效。

避免序列化問(wèn)題的一種方式是序列化數(shù)據(jù)傳輸對(duì)象(DTOs)而不是實(shí)體對(duì)象。我將會(huì)在后面的文章中展示這種實(shí)現(xiàn)。

顯式加載(Explicit Loading)

顯式加載和延遲加載非常類似,除了你在代碼中顯式地獲取相關(guān)數(shù)據(jù);當(dāng)你訪問(wèn)導(dǎo)航屬性時(shí)它不會(huì)自動(dòng)發(fā)生。顯示加載會(huì)在加載相關(guān)數(shù)據(jù)時(shí)給你更多的控制權(quán),但也需要額外的代碼。關(guān)于顯示加載的更多信息,請(qǐng)查看Loading Related Entities。 http://msdn.microsoft.com/en-us/data/jj574232#explicit

導(dǎo)航屬性和環(huán)形引用(Navigation Properties and Circular References)

當(dāng)我定義Book和Author模型時(shí),我在Book類中為Book-Author關(guān)系定義了導(dǎo)航屬性,但我沒(méi)有在其他方向定義導(dǎo)航屬性。

如果你在Author類中也定義相應(yīng)的導(dǎo)航屬性會(huì)怎樣呢?

public class Author
{
    public int AuthorId { get; set; }
    [Required]
    public string Name { get; set; }

    public ICollection<Book> Books { get; set; }
}

不幸的是,當(dāng)你在序列化模型時(shí)這會(huì)產(chǎn)生一個(gè)問(wèn)題。如果你加載相關(guān)數(shù)據(jù),它會(huì)產(chǎn)生環(huán)形對(duì)象圖。

這里寫圖片描述

當(dāng)JSON或XML格式試圖序列化圖時(shí),它將會(huì)拋出一個(gè)異常。這兩個(gè)格式拋出不同異常信息。這里是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>

一個(gè)解決方案是使用DTO,我將會(huì)在下一節(jié)中描述它。你可以配置JSON或XML格式化程序來(lái)處理圖循環(huán)。關(guān)于更多信息,請(qǐng)查看Handling Circular Object References. (http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization#handling_circular_object_references)

對(duì)于本教程,你不需要Author.Book導(dǎo)航熟悉,所以你可以去掉它。