Entity Framework 6 Recipes 2nd Edition(9-1)譯->用Web Api更新單獨分離的實體

来源:http://www.cnblogs.com/kid1412/archive/2016/01/17/5137078.html
-Advertisement-
Play Games

第九章 在N層結構的應用程式中使用EF不是所有的應用都能完全地寫入到一個單個的過程中(就是駐留在一個單一的物理層中),實際上,在當今不斷發展的網路世界,大量的應用程式的結構包含經典的表現層,應用程,和數據層,並且它們可能分佈在多台電腦上,被分佈到一臺單獨的電腦上的應用程式的某個領域的邏輯層,並不...


第九章 在N層結構的應用程式中使用EF

 

不是所有的應用都能完全地寫入到一個單個的過程中(就是駐留在一個單一的物理層中),實際上,在當今不斷發展的網路世界,大量的應用程式的結構包含經典的表現層,應用程,和數據層,並且它們可能分佈在多台電腦上,被分佈到一臺單獨的電腦上的應用程式的某個領域的邏輯層,並不過多地涉及代理伺服器編碼,序列化,和網路協議,應用程式可以跨越很多設備,從小到一個移動設備到大到一個包含企業所有賬戶信息的數據伺服器。

 

幸運的是,EF可應用於WCF,WEB Api等諸如此類的多層框架中。

在本章,我們將儘量涵蓋EF中多層應用中的使用,N層是指應用程式的表示層,業務邏輯層,和數據層等被分別布暑在不同的伺服器上。這種物理上獨立分佈有助於可擴展性,可維護性,及程式的後期延伸性,但當一個處理需要跨電腦的時候,會帶來性能上的影響。N層架構給EF的狀態跟蹤帶來額外的挑戰。首先,EF的ContextObject獲取數據發送給客戶端後被銷毀,而在客戶端上的數據修改沒有被跟蹤。

在更新前,必鬚根據遞交的數據新建一個Context Object,很明顯,這個新的對象不知道前一個對象的存在,包括實體的原始值。在本章,我們將看到處理這種跟蹤挑戰上的工具方法。

在EF的之前版本中,一個開發者能利用“跟蹤實體”模板,能幫助我們跟蹤被被分離的實體。然後在EF6中,它已經被棄用,但是遺留的ObjectContext將支持跟蹤實體,本章將關註基本的用於N層的創建,讀取,更新和刪除操作。此外,將深入探討實體和代理的序列化,併發,和實體跟蹤的工作方式。

9-1.用Web Api更新單獨分離的實體

問題

你想利用基於Rest的Web服務來插入,刪除,更新到數據存儲層。此外,你想通過EF6的Code First方式來實現對數據訪問的管理。在此例中,我們效仿一個N層的場景,控制台應用的客戶端調用暴露基於REST服務的Web Api應用。每層使用單獨的VS解決方案,這樣更有利於效仿N層的配置和調試。

解決方案

假設有一個如9-1圖所示的模型

 

9-1. 一個訂單模型

我們的模型表示訂單。我們想要把模型和資料庫代碼放到一個Web Api服務後面,以便任何客戶都可以通過HTTP來插入,更新和刪除訂單數據。為了創建這個服務,執行以下操作:

1.新建一個 ASP.NET MVC 4 Web 應用項目,命名為“Recipe1.Service”,併在嚮導中選擇Web API模板。。

2. 向項目中添加一個新的“控制器”,命名為“OrderController”.

3. 添加Order類,代碼如Listing 9-1所示:

Listing 9-1. Order Entity Class

public class Order

{

public int OrderId { get; set; }

public string Product { get; set; }

public int Quantity { get; set; }

public string Status { get; set; }

public byte[] TimeStamp { get; set; }

}

4. 在“Recipe1.Service ”項目中添加EF6的引用。最好是藉助 NuGet 包管理器來添加。在”引用”上右擊,選擇”管理 NuGet 程式包.從“聯機”標簽頁,定位並安裝EF6包。這樣將會下載,安裝並配置好EF6庫到你的項目中。

5. 然後添加一個新的類“Recipe1Context”,鍵入如Listing 9-2的代碼,並確保該類繼承自EF6的DbContext

297

Listing 9-2. Context Class

public class Recipe1Context : DbContext

{

public Recipe1Context() : base("Recipe1ConnectionString") { }

public DbSet<Order> Orders { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)

{

modelBuilder.Entity<Order>().ToTable("Chapter9.Order");

// Following configuration enables timestamp to be concurrency token

modelBuilder.Entity<Order>().Property(x => x.TimeStamp)

.IsConcurrencyToken()

.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);

}

}

6. 在Web.Configk中ConnectionStrings節里插入連接資料庫的配置,如Listing 9-3:

 

Listing 9-3. Connection String for the Recipe1 Web API Service

<connectionStrings>

<add name="Recipe1ConnectionString"

connectionString="Data Source=.;

Initial Catalog=EFRecipes;

Integrated Security=True;

MultipleActiveResultSets=True"

providerName="System.Data.SqlClient" />

</connectionStrings>

7. 把Listing 9-4所示的代碼插入到Global.asax的 Application_Start 方法中

Listing 9-4. Disable the Entity Framework Model Compatibility Check

protected void Application_Start()

{

// Disable Entity Framework Model Compatibilty

Database.SetInitializer<Recipe1Context>(null);

...

}

 

8. 最後,用Listing 9-5所示代碼替換OrderController里的代碼。

Listing 9-5. Code for the OrderController

public class OrderController : ApiController

{

// GET api/order

public IEnumerable<Order> Get()

{

using (var context = new Recipe1Context())

{

return context.Orders.ToList();

}

}

// GET api/order/5

public Order Get(int id)

{

using (var context = new Recipe1Context())

{

return context.Orders.FirstOrDefault(x => x.OrderId == id);

}

}

// POST api/order

public HttpResponseMessage Post(Order order)

{

// Cleanup data from previous requests

Cleanup();

using (var context = new Recipe1Context())

{

context.Orders.Add(order);

context.SaveChanges();

// create HttpResponseMessage to wrap result, assigning Http Status code of 201,

// which informs client that resource created successfully

var response = Request.CreateResponse(HttpStatusCode.Created, order);

// add location of newly-created resource to response header

response.Headers.Location = new Uri(Url.Link("DefaultApi",

new { id = order.OrderId }));

return response;

}

}

// PUT api/order/5

public HttpResponseMessage Put(Order order)

{

using (var context = new Recipe1Context())

{

context.Entry(order).State = EntityState.Modified;

context.SaveChanges();

// return Http Status code of 200, informing client that resouce updated successfully

return Request.CreateResponse(HttpStatusCode.OK, order);

}

}

 

// DELETE api/order/5

public HttpResponseMessage Delete(int id)

{

using (var context = new Recipe1Context())

{

var order = context.Orders.FirstOrDefault(x => x.OrderId == id);

context.Orders.Remove(order);

context.SaveChanges();

// Return Http Status code of 200, informing client that resouce removed successfully

return Request.CreateResponse(HttpStatusCode.OK);

}

}

private void Cleanup()

{

using (var context = new Recipe1Context())

{

context.Database.ExecuteSqlCommand("delete from chapter9.[order]");

}

}

}

需要著重指出的是,我們可以利用大量的工具(比如代碼生成模板)生成可運作的控制器, 保存上述修改。

接下來創建一個調用上述Web API服務的客戶端。

9. 新建一個包含控制台應用程式的解決方案,命名為” Recipe1.Client“.

10. 添加與Listing 9-1同樣的order實體類

最後,用Listing 9-6的代碼替換program.cs里的代碼

Listing 9-6. Our Windows Console Application That Serves as Our Test Client

private HttpClient _client;

private Order _order;

private static void Main()

{

Task t = Run();

t.Wait();

Console.WriteLine("\nPress <enter> to continue...");

Console.ReadLine();

}

private static async Task Run()

{

// create instance of the program class

var program = new Program();

program.ServiceSetup();

program.CreateOrder();

// do not proceed until order is added

await program.PostOrderAsync();

program.ChangeOrder();

// do not proceed until order is changed

await program.PutOrderAsync();

// do not proceed until order is removed

await program.RemoveOrderAsync();

}

private void ServiceSetup()

{

//指定調用Web API的URL

_client = new HttpClient { BaseAddress = new Uri("http://localhost:3237/") };

//接受請求的頭部內容

            //通過JSON格式返回資源

_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

}

private void CreateOrder()

{

//創建新訂單

_order = new Order { Product = "Camping Tent", Quantity = 3, Status = "Received" };

}

private async Task PostOrderAsync()

{

// 利用Web API客戶端API調用服務

var response = await _client.PostAsJsonAsync("api/order", _order);

Uri newOrderUri;

if (response.IsSuccessStatusCode)

{

// 為新的資源捕獲Uri

newOrderUri = response.Headers.Location;

// 獲取從服務端返回的包含資料庫自增Id的訂單

_order = await response.Content.ReadAsAsync<Order>();

Console.WriteLine("Successfully created order. Here is URL to new resource: {0}", newOrderUri);

}

else

Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);

}

private void ChangeOrder()

{

// 更新訂單

_order.Quantity = 10;

}

private async Task PutOrderAsync()

{

//構造HttpPut調用Web API服務中相應的Put方法

var response = await _client.PutAsJsonAsync("api/order", _order);

if (response.IsSuccessStatusCode)

{

// 獲取從服務端返回的更新後包含新的quanity的訂單

_order = await response.Content.ReadAsAsync<Order>();

Console.WriteLine("Successfully updated order: {0}", response.StatusCode);

}

else

Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);

}

private async Task RemoveOrderAsync()

{

// 移除訂單

var uri = "api/order/" + _order.OrderId;

var response = await _client.DeleteAsync(uri);

if (response.IsSuccessStatusCode)

Console.WriteLine("Sucessfully deleted order: {0}", response.StatusCode);

else

Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);

}

客戶端輸出結果如 Listing 9-6:

==========================================================================

Successfully created order:Here is URL to new resource: http://localhost:3237/api/order/1054

Successfully updated order: OK

Sucessfully deleted order: OK

==========================================================================

 

它是如何工作的?

先運行Web API應用程式.這個Web API應用程式包含一個MVC Web Controller, 啟動後會打開首頁。至此網站和服務已經可用。接下來打開控制台應用程式,在program.cs前面設置一個斷點,運行控制台應用程式。首先我們用URI和一些配置建立與Web API服務的管道連接,接受由WEB AP服務端返回的JSON格式的頭部信息,然後我們用PostAsJsonAsync方法發送WEB API請求,並從HttpClient對象里返回信息創建新的Order對象.如果你在WEB AP服務端controller的Post Action方法里添加斷點,你將看到它接收到一個Order對象參數並把它添加到訂單實體的Context中.確保該對象狀態為Added,並讓Context跟蹤它.最後,調用SaveChanges方法把新的訂單插入到資料庫。並封裝一個HTTP狀態碼201和URI定位新創建的資源到HttpResponseMessage對象返回給調用它的應用程式. 當使用 ASP.NET Web API, 要確保我們的客戶端生成一個HTTP Post請求來插入新的數據,該HTTP Post請求調用相應的Web API controller 中的Post action方法。

再查看客戶端,我們執行下一個操作,改變訂單的quantity,用HttpClient 對象的PutAsJsonAsync方法把新的訂單發送給Web API.如果WEB API服務的Web API controller里的 Put Action 方法添加斷點, 可以看到該方法參數接收到一個訂單對象. 接關調用context 對象的Entry 方法傳遞訂單實體的引用,然後設置State為 Modified 狀態. 隨後調用SaveChanges產生一個SQL更新語句.會更新訂單的所有列. 在此小節,我們看到瞭如何只更新想要更新的屬性. 並返回給調用者一個為200 HTTP狀態碼。再看客戶端,我們最後調用移除操作,它會把狀態從資料庫中刪除. 我們通過把訂單的Id附加到URI中,並調用Web API 的DeleteAsync。在服務端,我們從資料庫中獲取目標訂單,並傳給訂單的context 對象的Remove方法,使訂單狀態設置為deleted.

隨後調用SaveChanges產生一個SQL的刪除語句,並把訂單從資料庫中刪除。在此小節,我們將EF數據操作封裝在Web API服務後,客戶端可能通過HttpClient對象調用服務,通過Web API的 HTTP 方法發佈, 利用Post action方法來添加新記錄, Put action方法更新一條記錄, 和Delete action方法刪除一條記錄.同時我們學習到了EF6的Code First方式,當然在實際應用中,我們可能更願意創建一個單獨的層(VS的類庫項目),把EF6資料庫訪問代碼從Web API服務中分離出來。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Homebrew OS X 不可或缺的套件管理器,可以說Homebrew就是mac下的apt-get、yum.1.安裝homebrew brew的安裝很簡單,使用一條ruby命令即可,Mac系統上已經預設安裝了ruby。ruby -e "$(curl -fsSL https://raw.git...
  • 系統信息arch 顯示機器的處理器架構(1)uname -m 顯示機器的處理器架構(2)uname -r 顯示正在使用的內核版本dmidecode -q 顯示硬體系統部件 - (SMBIOS / DMI)hdparm -i /dev/hda 羅列一個磁碟的架構特性hdparm -tT /dev/sd...
  • 一.概述 1.共用記憶體允許多個進程共用物理記憶體的同一塊記憶體區。2.與管道和消息隊列不同,共用記憶體在用戶記憶體空間,不需要內核介入。降低了內核和用戶緩衝區的數據複製開銷。所以這種IPC速度比較快。3.多個進程共用記憶體時需要其他同步機制來控制臨界區,如上一篇...
  • 蝸牛歷險記(二)之Web框架下的Autofac
  • 雖然一般UWP開發還是依賴.Net for UWP,但有時還是需要調用WinRT API。特別是在IO部分,WinRT有著和.Net似曾相識但又不盡相同的介面。在此對經常用到的一些地方進行一下整理。WinRT IOStream和.Net的Stream體系類似,WinRT中也存在一系統的IO流介面。和...
  • 剛剛申請的時候還是非常急切地想知道,什麼時候能收到管理員同意自己開通的通知,沒想到剛一收到的時候,自己開始糾結第一篇文章寫點什麼。我想,不如來分享自己的一小段代碼吧?1 var Response = HttpContext.Current.Response;2 Response.ContentTyp...
  • readonly與const在C#中,readonly 與 const 都是定義常量,但不同之處在於:readonly 是運行時常量,而 const 是編譯時常量。public const int intValue = 100;public void Test(){ Console.Write...
  • 9-2. 用WCF更新單獨分離的實體問題你想通過WCF為一個數據存儲發佈查詢,插入,刪除和修改,並且使這些操作儘可能地簡單此外,你想通過Code First方式實現EF6的數據訪問管理解決方案假設有如Figure 9-2所示模型.Figure 9-2. 博客的posts(博文)和comments(評...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...