本文基於 ASP.NET 3.5 的預發布版本和 Microsoft AJAX 庫。文中的所有信息均有可能發生變更。
本文將介紹以下內容:
數據服務的含義
公開和使用數據
使用實體數據模型 描述數據
數據安全性
本文使用以下技術:
ADO.NET 數據服務、LINQ 和實體數據模 型
回想一下您上次構建的富 Internet 應用程序 (RIA)。您是如何獲取您的數據的?又是如何區分該 數據與發送至浏覽器的演示文稿和用戶界面 (UI) 信息的?如果有更簡單的方法來實現此操作,會怎樣?
分離演示文稿和數據並不是什麼新構想,但隨著 RIA 技術(如 AJAX 和 Silverlight™)的日 益普及,它已經越來越流行了。這些技術基於分離演示文稿和數據的思路構建,其目的在於開發出交互性 更強且響應更快的應用程序。
例如,基於 Silverlight 的 RIA 應用程序可預編譯代碼來驅動演 示文稿,並通過 Web 服務器將該代碼部署到客戶端。然後,到達 Web 浏覽器後,該代碼將回調到 Web 服務器以檢索要在用戶界面中顯示的數據。此類技術通常不需要選擇服務器端呈現過程,因為這會混合數 據和演示文稿代碼。
除了分離演示文稿和數據以使 Web 體驗更豐富、更具交互性之外,Web 還有 公開和使用獨立於任何用戶界面的獨立數據的趨勢。數據驅動的應用程序(如“資源聚合”) 的廣泛應用表明,有意義且便於使用的數據的推廣開創了新的應用程序方案。
基於對這些趨勢的 觀察,ADO.NET 數據服務框架最初旨在幫助那些希望通過自己的 RIA 應用程序中的服務來公開和使用數 據的開發人員。探索該領域時,出現了兩個主要理念:使用現有方法針對以數據為中心的服務構建通用客 戶端庫和工具這一觀念本身,就很有難度;創建和維護這些服務需要投入大量的開發人員。在本文中,我 們將重點介紹什麼是數據服務以及幾個主要功能。
總的來說,ADO.NET 數據服務框架的目標是為 公開和使用以數據為中心的服務創建基於具象狀態傳輸 (REST) 的簡單框架。此類服務使用統一的界面公 開數據,以供整個企業 Intranet 或 Internet 范圍內的所有 Web 客戶端使用。該框架由一個服務器庫 和一組客戶端庫組成,前者用於將數據作為服務安全公開,後者是為一系列 Microsoft 應用程序和技術 (Microsoft® .NET Framework 和 Silverlight 等)能夠使用服務而構建的。圖 1 展示了該體系結 構。
圖 1 ADO.NET 數據服務框架體系結構
使用實體數據模型描述數據
每個 ADO.NET 數據服務 都是使用概念架構定義語言 (CSDL) 借助實體數據模型 (EDM) 術語進行描述的。EDM 使用了兩個主要概 念:實體和關聯(或關系)。實體是“實體類型”的實例(如 Customer 或 Employee),即 含有一個關鍵字的結構化記錄。實體關鍵字基於“實體類型”屬性的子集形成。關鍵字(分別 為 CustomerId 和 OrderId)用於唯一標識和允許實體實例更新,還允許實體實例參與到關系中。實體按 EntitySets 分組(Customers 是 Customer 實例的集合)。關聯用於在兩個或多個實體類型之間形成鏈 接(如 Employee WorksFor Department)。
為什麼選擇 EDM 作為 ADO.NET 數據服務的數據描述 語言?事實證明,EDM 可以很好地映射到資源和鏈接的主要 Web 概念,因此也就成了理想的候選項(資 源的實體和鏈接的關聯)。
EDM 的開發目標是成為一組開發人員和服務器技術的核心數據模型。 通過僅使用一個數據模型在大量應用程序之間進行維護,就能夠簡化應用程序維護工作。EDM 不僅可用於 為基於 ADO.NET 數據服務構建的自定義應用程序定義模型,還可以將該模型用作報告和可視化應用程序 、Intranet 門戶應用程序或工作流應用程序的輸入內容。ADO.NET 數據服務是要基於 EDM 概念構建的第 二種 Microsoft 技術(第一種技術當然是 ADO.NET 實體框架)。有關 EDM 和 ADO.NET 實體框架的詳細 信息,請參閱 msdn.microsoft.com/data 和 MSDN® 雜志 2008 年 7 月刊中的“ADO.NET:使 用實體框架靈活地為數據建模”,網址為 msdn.microsoft.com/magazine/700331。
關系數據
默認情況下,ADO.NET 數據服務會與 ADO.NET 實體框架密切合作,以針對基於在 SQL Server® 或其他第三方數據庫(Oracle、DB2 和 MySQL,僅舉幾個示例)中存儲的數據的數據模 型,簡化連接和公開此類數據模型的過程。
實體框架增加了開發人員處理數據時的抽象級別,即 不是針對行和列編碼,而是基於關系數據定義一個更高級別的概念模型(如實體數據模型),然後依據此 模型對應用程序進行編程。應用程序只需理解應用程序可讀懂的形狀中的數據,這些形狀都是使用包括概 念(如繼承、復雜類型和顯式關系)在內的豐富詞匯來表達的。
總的來說,ADO.NET 數據服務的 工作原理是:將通過資源執行某個操作的請求(例如,通過 URI 執行的 HTTP 請求操作)轉換為該資源 代表的數據模型上的等效操作。在這種情況下,由於數據模型由關系數據庫支持,所以 URI 將轉換為實 體框架對象服務方法調用。
只需幾個步驟,便可以在 Visual Studio® 2008 SP1 ASP.NET Web 應用程序項目中公開使用實體框架創建的現有數據模型。(同樣支持其他項目類型和承載機制,如 Windows® Communication Foundation (WCF) 或 WebServiceHost。)首先在 ASP.NET Web 應用程序 中創建或導入一個 ADO.NET 實體數據模型。在該模型可用的情況下,添加新的 ADO.NET 數據服務。 “添加新項”向導將生成一項基本服務,並將其顯示在解決方案資源管理器中。然後,我們可 以通過更改類定義將該服務掛接到此模型,如下所示:
using NorthwindModel; public class NorthwindService : DataService <NorthwindEntities> { public static void InitializeService(IDataServiceConfiguration config) { } }
此外,由於在默認情況下,該服務最初會處於鎖定狀態,我們必須設法設置服務范圍的安全策 略,以使用戶能夠使用該服務。以下代碼可在初始化該服務時設置 EntitySet 級別的安全性:
public static void InitializeService(IDataServiceConfiguration config) { config.SetEntitySetAccessRule("*", EntitySetRights.All); }
您可以看到,這是設置 ADO.NET 數據服務安全性的一個非常簡單的示例。稍後將討論其他安 全方法和注意事項。
此時,我們便擁有了一個可創建和運行的工作 ADO.NET 數據服務。
在沒有客戶端應用程序的情況下測試服務的簡單方法是,您只需將 Web 浏覽器指向該服務的入口點,如 <host>/<vdir>/service.svc。該服務的入口點將返回 XML 格式的響應,其中包含數據服務 公開的 EntitySets 的列表。若要在 Internet Explorer® 中查看 Atom(ADO.NET 數據服務返回的 默認格式),必須首先確保已關閉 Internet Explorer 中的“源閱讀視圖”。可以通過 “工具”|“Internet 選項”的“內容”選項卡來完成此操作。
關於非關系數據
為了獲得所有其他數據源或使用其他數據庫訪問技術(如 LINQ to SQL) ,現提供了一個機制,該機制可使用插件模型將任何數據源作為 ADO.NET 數據服務公開。
在這種 情況下,URI 將轉換為 LINQ 查詢。數據服務可將實現 IQueryable 接口的對象映射到 EntitySets,從 而啟用此方法。這意味著只要為數據源寫入了 IQueryable 提供程序,ADO.NET 數據服務就能夠公開該數 據源。為實現此功能,ADO.NET 數據服務定義了 CLR 對象與基於 EDM 的數據模型的產物之間的映射。
稍後,我們將探討如何根據簡單的 CLR 對象圖創建 ADO.NET 數據服務。我們即將介紹的步驟會 生成一個數據服務,此服務是基於內存中的對象集合創建的。不過,在典型的生產部署中,所顯示的代表 EntitySets 的 IQueryable 屬性不會公開內存中的數據,但會將 IQueryable 表達式樹轉換為特定於數 據源的查詢。例如,LINQ to Entities 可將 IQueryable 表達式樹轉換為 SQL 語句。
在針對非 關系存儲創建數據服務時,會同時在 ASP.NET Web 應用程序項目中創建該數據服務。但是,此時請注意 ,我們將跳過用於創建 ADO.NET 實體數據模型的步驟,而從添加新的 ADO.NET 數據服務開始。“ 添加新項”向導將生成一項基本服務,此服務將在解決方案資源管理器中列出。若要繼續介紹此簡 單示例,現在必須創建計劃通過我們的服務公開的內存中數據。為此,我們創建三個類,其中兩個類分別 是 User 和 Contact,每個 User 都包含一組 Contact。第三個類是 MyDataService 類,該類將兩個公 共屬性(Contacts 和 Users)作為 IQueryable<Contact> 和 IQueryable<User> 提供,如 圖 2 所示:
圖 2 創建內存中的 IQueryable 數據源
public class User { public int ID { get; set; } public string Name { get; set; } public IList<Contact> Contacts { get; set; } } public class Contact { public int ID { get; set; } public string Name { get; set; } public string Email { get; set; } } public class MyDataService { static User[] _users; static Contact[] _contacts; static MyDataService() { _users = new User[]{ new User{ID=1, Name="Mike"}, new User{ID=2, Name="Elisa"} }; _contacts = new Contact[]{ new Contact{ID=1, Name="Joe", Email="[email protected]"}, new Contact{ID=2, Name="Bob", Email="[email protected]"}, new Contact{ID=3, Name="Sam", Email="[email protected]"}, new Contact{ID=4, Name="Carl", Email="[email protected]"}, new Contact{ID=5, Name="Abby", Email="[email protected]"}, new Contact{ID=6, Name="Annie", Email="[email protected]"}, }; _users[0].Contacts = new List<Contact>(); _users[0].Contacts.Add(_contacts[0]); _users[0].Contacts.Add(_contacts[1]); _users[0].Contacts.Add(_contacts[4]); _users[1].Contacts = new List<Contact>(); _users[1].Contacts.Add(_contacts[2]); _users[1].Contacts.Add(_contacts[3]); _users[1].Contacts.Add(_contacts[5]); } public IQueryable<User> Users { get { return _users.AsQueryable<User>();} } public IQueryable<Contact> Contacts { get { return _contacts.AsQueryable<Contact>(); } } }
既然已確定了數據源,我們現在返回熟悉的領域,即更改類定義使其指向我們的數據源,如下 所示:
public class contacts : DataService<ADONETDataServiceNonRelSample.MyDataService> { public static void InitializeService(IDataServiceConfiguration config) { config.SetEntitySetAccessRule("*", EntitySetRights.All); } }
同樣,我們必須做一些初始工作來設置安全策略,因為默認情況下初始服務處於鎖定狀態。我 們在前面已經了解到,此代碼顯示 EntitySet 級別的相同安全策略設置。此時,我們便再次擁有了一個 可以創建和運行的工作 ADO.NET 數據服務。只要使用 Internet Explorer 浏覽至服務端點即可完成對該 服務的測試。
統一 URI 格式
統一的統一資源標識符 (URI) 是組成 ADO.NET 數據服務的 主要概念之一,它定義了一個相對簡單、但富有表現力的 URI 格式,該格式允許應用程序查詢實體組或 單個實體,以及遍歷這些實例之間存在的關系。此 URI 的結構使代理和控件能夠輕松了解如何導航由服 務提供的數據。
當開始測試初始服務時,我們可以看到指向數據服務本身的基本 URI 格式。根據 此基本 URI 格式,我們能夠使用以下格式查詢服務,如下所示:
http://<host>/<vdir>/<service.svc>/<EntitySet> [(<Key>) [/<NavigationProperty>[(<Key>)/...]]]
例如,繼續討論之前的 Northwind 示 例,如果我們將 /Customers 添加到數據服務 URI 的末尾,即 <host>/<vdir>/NorthwindService.svc/Customers,我們將返回 Customers 實體集中的所 有客戶,在本例中,將返回 Northwind 數據庫中的所有 Customers。另一方面,如果希望返回 Northwind 服務中的單個客戶實體,我們可以使用該實體的鍵值,因為 Customer 是一個單鍵實體。通過 請求 URI <host>/<vdir>/NorthwindService.svc/Customers('ALFKI'),可以返回 帶有“ALFKI”鍵的單個 Customer。以類似的方式將導航屬性附加到單個實體查詢 URI。通過 追加 /Customers('ALFKI')/Orders,我們可以導航模型中 Customer 和 Order 之間的關系,從 而返回與使用“ALFKI”鍵標識的 Customer 相關聯的所有 Order。
上述 URI 格式允許在我們的存儲中進行基本查詢以及遍歷實體,但是不負責進一步控制查詢的輸入或 對其加以約束。為了實現這些目標,我們將使用一組受 ADO.NET 數據服務支持的可選查詢字符串參數, 包括 $filter、$expand、$orderby、$skip 和 $top。查詢字符串參數追加在 URI 中 ?字符後方。
例如,要查詢倫敦的所有客戶,我們可以對服務 URI 追加 /Customers?$filter=City eq 'London'。要查詢特定客戶(具有鍵“ALFKI”)並檢索相關的銷售訂單,我們可以 對服務 URI 追加 /Customers('ALFKI')?$expand=Orders。若要按 City 對結果進行升序 (asc) 排序,我們可以對服務 URI 追加 /Customers?$orderby=City asc(若要進行降序排序,則追加 /Customers?$orderby=City desc)。
若要獲得受 ADO.NET 數據服務支持的 URI 格式的完整列表 ,請查看數據服務文檔,可從 go.microsoft.com/fwlink/?LinkId=120539 中訪問該文檔。
數據 服務安全性
當討論基於 Web 的技術,特別是提供對關鍵數據的訪問和操作的技術時,安全性始終 是首要關注的問題。因此,在數據服務的整個開發周期中,安全性是主要的考慮因素。
為了提供 身份驗證,ADO.NET 數據服務將利用大量的現有 ASP.NET 和 WCF 身份驗證基礎結構,以便用戶在應用程 序之間和整個服務中獲得集成的身份驗證體驗。如果您擁有一個使用身份驗證(可能是某個身份驗證提供 程序,也可能是適當地設置了 HTTP 上下文主體的自定義提供程序)的 ASP.NET 站點,ADO.NET 數據服 務就可以利用您選擇的機制為任一給定的請求建立當前身份標識(主體)。同樣,如果您在 ASP.NET 之 外(WCF 或通過自定義主機)托管數據服務,主機也可以選擇使用任何身份驗證機制,但前提是它能夠為 數據服務作者提供訪問請求主體所需的 API。
默認情況下,任何新 ADO.NET 數據服務都處於完全 鎖定狀態,此時,對於沒有讀寫訪問權限即無法訪問的任何實體、服務操作和元數據,都將無法訪問。盡 管如此,仍為您的數據服務提供了多種機制來控制授權策略和按請求執行驗證。
數據服務開發人 員首先要執行的步驟之一是針對由數據服務提供的資源提供訪問權限。在我們的示例中,我們已展示了一 個簡單案例,介紹如何設置整個服務的讀/寫訪問策略。此操作是使用 InitializeService 方法針對每項 服務執行的。策略集是 EntitySet 策略,用於允許或限制對模型中實體集的訪問,以及設置從服務的元 數據終結點可以獲得哪些實體集和相關的關聯。盡管這些示例中顯示的這兩個策略都針對整個模型開放了 讀取和寫入訪問權限,但我們可能要特別得多,即為不同的 EntitySets 設置不同的策略並在必要時包含 復合策略,如圖 3 所示。
圖 3 設置服務范圍內的訪問策略
public class MyService : DataService<NorthwindEntities> { public static void InitializeService( IDataServiceConfiguration config) { // '/Customers' entities are enabled for all read and write // operations config.SetEntitySetAccessRule("Customers", EntitySetRights.All); // URI '/Orders' is disabled, but '/Orders(1)' is enabled for read // only config.SetEntitySetAccessRule("Orders", EntitySetRights.ReadSingle); // Can insert and update, but not delete Products config.SetEntitySetAccessRule("Products", EntitySetRights.WriteInsert | EntitySetRights.WriteUpdate); } }
在許多情況下,還要求數據服務在實體進入數據服務(進行插入、更新或刪除)時運行驗證邏 輯,並可限制每個請求對實體的訪問。對於這些情況,您可以利用偵聽器,使數據服務開發人員能夠將自 定義驗證或訪問策略邏輯插入數據服務的請求/響應處理管道。例如,如果我們希望使客戶只能檢索自己 的訂單,而不能檢索其他客戶發出的訂單,我們將需要實現一個查詢偵聽器,如圖 4 所示。查詢偵聽器 將返回一個要附加到查詢的謂詞,然後,該查詢將被推入對應的數據存儲,無需對該數據存儲執行其他操 作即可檢索訪問控制信息。
圖 4 查詢偵聽器方法
public class nw : DataService<NorthwindModel.NorthwindEntities> { public static void InitializeService(IDataServiceConfiguration config) { config.SetEntitySetAccessRule("Orders", EntitySetRights.All); } [QueryInterceptor("Orders")] public Expression<Func<Orders,bool>> OnQueryOrders() { return o => o.Customer.ContactName == HttpContext.Current.User.Identity.Name } }
同樣,還可以為插入、更新和刪除操作添加更改偵聽器。更改偵聽器沒有返回值但有兩個參數 :一個包含在 EntitySet 中的類型對象,一個用於定義資源請求的操作(更新、插入或刪除)的 UpdateOperations 枚舉。該偵聽器可以更改傳遞給它的對象或設置對完全不同的實例的引用。如果該方 法引發異常,將中止操作,數據庫中不會發生任何更改,並為客戶端代理返回一個錯誤。
客戶端 :訪問服務
通常,我們將 HTTP 視為數據服務的 API,因此花費了大量的時間來確保能夠輕松地 在 HTTP 級別上直接使用它。這意味著,數據服務不僅能夠輕松解釋服務負載,而且包含 HTTP 堆棧的任 何平台都可以輕松使用數據服務。盡管如此,如果您使用 Microsoft 平台訪問數據服務,則使用 ADO.NET 數據服務客戶端庫便可進一步簡化客戶端開發。這為使用 .NET Framework、Silverlight 和 ASP.NET AJAX 編寫的應用程序提供了更為自然的編程模型,可定位目標服務、基於功能集操作結果,以 及處理某些方面(如關聯遍歷)。客戶端庫可抽象出 HTTP 請求和響應的詳細信息,還可確保在各個客戶 端堆棧之間提供更為統一的體驗。
.NET Framework 和 Silverlight 客戶端庫包含兩個主要類型 :DataServiceContext 類和 DataServiceQuery 類。DataServiceContext 代表給定數據服務的運行時上 下文。數據服務本身是無狀態的,但開發人員進行交互時所處的上下文卻並非如此,並且為支持標識解析 和開放式並發等功能,客戶端的狀態還會在各交互之間得以保持。DataServiceQuery 對象代表針對使用 URI 語法定義的存儲執行的特定查詢。若要執行查詢並獲得 .NET 對象形式的結果,只需枚舉該查詢對象 即可。例如,您可以通過在 C# 中使用標准的“foreach”結構或在 Visual Basic® 中使 用標准的“For Each”來實現此操作。
為了將數據服務中定義的實體用作客戶端上的 .NET 對象,需要為客戶端應用程序定義相應的類。要完成此操作,主要有三種方法可供選擇。一種選擇 是手動定義它們。圖 5 顯示了 Region 類的簡單手寫定義和一小段代碼,這段代碼可針對區域執行查詢 並在輸出中顯示區域的 ID 和描述。
圖 5 從 .NET 應用程序中訪問 ADO.NET 數據服務
namespace TestApplication { public class Region { public int RegionID { get; set; } public string RegionDescription { get; set; } } class Program { static void Main(string[] args) { DataServiceContext ctx = new DataServiceContext("http://localhost:25115/NorthwindService.svc"); DataServiceQuery<Region> regions = ctx.CreateQuery<Region>("/Region?$orderby=RegionID"); foreach (Region r in regions) { Console.WriteLine(r.RegionID + ", " + r.RegionDescription); } } } }
更為常用的方法是使用由 Visual Studio 生成的代碼。如果當服務只擁有少量的類型時,可 以手動編寫類,那麼隨著數據服務架構復雜程度的提高,必須手動創建和維護的類的數量和大小也會大幅 度增加。
生成所需類的最常用的方法是使用 Visual Studio 中的“添加服務引用”向 導。與針對 WCF 服務使用“添加服務引用”的方法類似,只需右鍵單擊“項目” ,然後選擇“添加服務引用”(如圖 6 所示)。
圖 6 添加服務引用
在“添加服務應用”對話框中的“地址”中輸入服務入口點的 URI。在本例中,我們將輸入 <host>/<vdir>/NorthwindService.svc。這將生成基於數據服務定義的類並將它們添加到項 目中。
另一種選擇是使用“datasvcutil.exe”命令行工具生成基於數據服務定義的類。此工具隨 ADO.NET 數據服務的發布版本提供,位於 \Windows\Microsoft.Net\Framework\V3.5 目錄下。該工具的使用形式 為:參數,後跟數據服務入口點的 URI,最後是將要生成的輸出文件的名稱和位置,如下所示:
"\Windows\Microsoft.Net\Framework\V3.5\datasvcutil.exe"
/out:C:\NorthwindSample\northwind.cs /uri:"http://<host>/<vdir>/
NorthwindService.svc"
該命令行工具的默認輸出是一個 C# 文件,其中包含適用於 數據服務中描述的每個實體類型的類(Visual Basic 類型可以使用 /language:VB 開關生成)。生成的 每個類中都包含一些成員,用以代表在服務中描述的原始值和關聯。這樣,開發人員就可以直接使用對象 模型來浏覽關聯實體的圖形。
.NET 客戶端庫
ADO.NET 數據服務 .NET 客戶端庫提供了一個編程模型,使用 .NET Framework 和數據服務編寫應用 程序的開發人員都很熟悉此模型。事實上,客戶端庫使用 HTTP 和 AtomPub 格式,所以,它在公司網絡 和 Internet 環境中均可正常運行,只需與數據服務之間建立簡單的 HTTP 級連接,直接連接或間接連接 (例如通過代理)均可。
Windows 窗體、Windows Presentation Foundation (WPF) 和 Web 項目等所有項目類型都可以使用客 戶端庫。為了使用上述客戶端庫,您需要添加對 System.Data.Services.Client.dll 程序集的引用。
Silverlight 客戶端庫
Silverlight 客戶端不是作為 ADO.NET 數據服務的一部分提供,而是作為 Silverlight 2 SDK 的一 部分提供,這樣就可以在開發 Silverlight 應用程序時獲得集成更加完全的體驗。Silverlight 客戶端 庫與 .NET 和 AJAX 客戶端庫之間的一個區別在於,Silverlight 2 不支持同步開發。因此,使用 Silverlight 客戶端庫開發時必須利用異步 API,遵循通用的 begin/end 異步模式。這也就意味著,您 可以在另外兩個客戶端庫中使用的某些 API 尚未在 Silverlight 客戶端庫中啟用。
AJAX 客戶端庫
目前,ASP.NET AJAX 庫可從 codeplex.com/aspnet/Release/ProjectReleases.aspx? ReleaseId=13357 獲得。與前面講的 .NET 客戶端庫類似,AJAX 客戶端庫將提取 HTTP 的詳細信息,以 便應用程序開發人員可以直接使用 JavaScript 對象,而不需要手動分析和創建 HTTP 請求與響應。 CodePlex 站點還提供了許多介紹如何針對 AJAX 應用程序使用數據服務庫的網頁(請參見 codeplex.com )。
查詢數據服務
在圖 5 中,對服務執行的查詢是通過調用 DataServiceQuery.CreateQuery 方法並傳入 URI 查詢而 構建的。此外,該庫還允許您使用 LINQ 以公式形式表示數據服務查詢。客戶端庫將 LINQ 語句映射到目 標數據服務中的 URI,並將指定的資源作為 .NET 對象進行檢索。以下代碼顯示如何檢索倫敦城市內的所 有客戶,並將檢索結果按公司名稱進行排序:
var q = from c in ctx.Customers where c.City == "London" orderby c.CompanyName select c; foreach (var cust in q) { Console.WriteLine(cust.CompanyName); }
在 LINQ to ADO.NET 數據服務時,可以使用 LINQ 語法表達的查詢集要比通過數據服務的基於 REST 的 URI 語法啟用的查詢集廣泛。如果查詢無法映射到目標數據服務中的有效 URI,則會引發異常。
了解能夠和不能夠映射到 URI 的查詢類型的一種方法是考慮分層遍歷。需要兩個或多個數據透視表的 任何查詢(例如,使用 Any 等子句的聯接或子查詢)目前無法映射到 URI。但一般情況下,使用一個數 據透視表和關聯遍歷的查詢都可以正常工作。
到目前為止,我們查詢了簡單的實體。對象之間的關聯也由 DataServiceContext 跟蹤和管理,並且 可以使用“統一 URI 格式”部分介紹的 URL 格式自願或根據需要加載關聯的對象。若要根據需要加載關 聯實體(延遲加載),請對 DataServiceContext 類使用 LoadProperty 方法,如圖 7 所示。
圖 7 使用 LoadProperty 延遲加載相關實體
1.// get a single category
2.DataServiceQuery<Categories> categories =
3. context.CreateQuery<Categories>("/Categories(1)");
4.
5.foreach (Categories c in categories)
6.{
7. Console.WriteLine(c.CategoryName);
8.
9. context.LoadProperty(c, "Products");
10.
11. foreach (Products p in c.Products)
12. {
13. Console.WriteLine("\t" + p.ProductName);
14. }
15.}
在某些情況下,我們可能還希望獲取實體時可以避免線路上的額外往返行程,更喜歡與原始查詢一起 加載實體。在本示例中,在 URI 上指定了 Expand 選項。客戶端庫認為結果中包括頂層實體和關聯實體 ,並將這些實體全部具體化為單個對象圖表。圖 8 自願將一次往返行程中的相關產品加載到數據服務。
圖 8 使用 Expand 自願加載相關實體
public IList<Products> GetProducts(string productName, Categories category) { int categoryId = category.CategoryID; string uri = serviceUri + "/Categories(" + categoryId + ")/Products? "; if (!String.IsNullOrEmpty(productName)) { uri = uri + "$filter=ProductName eq '" + productName + "'&"; } uri = uri + "$expand=Categories"; Uri queryUri = new Uri(uri); try { IEnumerable<Products> products = context.Execute<Products>(queryUri); return products.ToList(); } catch (Exception) { return null; } }
命令映射
使用 HTTP Get 請求映射通過 ADO.NET 數據服務執行的查詢後,如何執行 Create、Update 和 Delete 請求?四個 CRUD 操作(Create、Retrieve、Update 和 Delete)中的每個操作都映射到一個不 同的 HTTP 動詞:Retrieve 映射到 GET,Create 映射到 POST,Update 映射到 PUT,Delete 映射到 DELETE。與對待查詢一樣,使用的客戶端庫(本例中為 .NET 客戶端庫)將提取詳細信息並使用相應的 HTTP 動詞,這樣開發人員就可以非常輕松地使用提供的 API。
若要為數據服務中的某個實體創建實例,只需創建一個 .NET 對象,並使用所需數據進行填充,然後 在 DataServiceContext 對象上調用 AddObject,這就會傳入新對象以及這個對象要添加到的 EntitySet 的名稱,如下所示:
public void AddProduct(Products product)
{
context.AddObject("Products", product);
context.AttachTo("Categories", product.Categories);
context.SetLink(product, "Categories", product.Categories);
DataServiceResponse r = context.SaveChanges();
}
還可以添加或更改 .NET 對象之間的關聯,並使客戶端庫將這些更改反映為關聯創建/刪 除操作。例如,上一段代碼在 Northwind 數據庫中創建了 Product,並將其與一個現有 Category 關聯 起來。類別和產品之間是一對多關聯;因此,一個給定產品對應一個特定類別。
創建實體後,數據服務將返回該實體的全新副本,其中包括在數據庫中觸發某個操作後更新的值,以 及自動生成的關鍵字等。然後,客戶端庫將自動使用新值更新 .NET 對象。
若要修改現有的實體實例,首先,客戶需要獲取該對象並使用 DataServiceContext 對其進行跟蹤。 開發人員首先會查詢該對象或將該對象附加到上下文,對其屬性進行必要的更改,然後調用 UpdateObject 方法。UpdateObject 方法指示客戶端庫對該對象進行更新:
public void UpdateProduct(Products product)
{
Categories newCategory = product.Categories;
context.AttachTo("Products", product);
context.AttachTo("Categories", newCategory);
context.UpdateObject(product);
context.SetLink(product, "Categories", newCategory);
context.SaveChanges();
}
若要刪除實體實例,客戶端必須再次獲取該對象並使用 DataServiceContext 對其進行 跟蹤。跟蹤完畢後,我們可以直接在上下文實例上調用 DeleteObject 方法,標記該對象以進行刪除:
public string DeleteProduct(Products product)
{
context.AttachTo("Products", product);
context.DeleteObject(product);
context.SaveChanges();
}
在上述所有示例中,在上下文中最後一次執行的方法調用是對 SaveChanges 方法的調用 。所做的更改是在 DataServiceContext 實例中進行跟蹤的,但並未立即發送到服務器。完成給定活動所 需的所有更改後,調用 SaveChanges 即可將更改提交到數據服務。
到目前為止,每個示例都生成了一個 HTTP 和針對每個客戶端操作的服務器請求。這種方法(每個 HTTP 請求執行一個操作)在某些情況下很適用,但是通常最好對多個操作進行批處理,然後通過一個 HTTP 請求將它們發送到數據服務。這將減少數據服務的往返次數,並為操作集合啟用原子性的邏輯范圍 。為了支持這些要求,客戶端支持通過一個 HTTP 請求將 CUD(Create、Update 和 Delete)操作組以及 Query 操作組發送到數據服務。以下代碼顯示了如何將兩個或多個查詢作為一個批次發送到數據服務:
var q = (DataServiceRequest)from o in context.SalesOrder select o; // send two queries in one batch request to the data service DataServiceResponse r = service.ExecuteBatch( new DataServiceRequest<Customer>(new Uri("http://localhost:25115/NorthwindService.svc/Customers")), q);
要將一組 CUD 操作作為一個批次發送給數據服務,可直接調用 SaveChanges 方法並將 SaveChangesOptions.Batch 作為唯一的參數傳遞。此參數值指示客戶端將所有掛起的更改操作組合為一 個批次,然後將其作為一個原子團發送到數據服務。當使用 SaveChangesOptions.Batch 將更改操作作為 一個批次進行發送時,要麼會成功完成所有更改,要麼不會應用任何更改。
了解更多
盡管本文所含內容已足夠讓您入門 ADO.NET 數據服務,但您感興趣的主題可能仍然有很多。要了解詳 細信息,請訪問數據服務團隊博客(網址為 blogs.msdn.com/astoriateam),也可以訪問 MSDN 數據開 發中心(網址為 msdn.microsoft.com/data)下的 ADO.NET 數據服務主題。
Elisa Flasko 是 Microsoft 數據可編程性團隊的一名項目經理,主要從事 ADO.NET 技術、XML 技術 和 SQL Server 連接性技術的研究。您可以通過 blogs.msdn.com/elisaj 與她聯系。
Mike Flasko 是 Microsoft SQL 數據可編程性團隊的一名項目經理。您可以通過 Mike 的博客 blogs.msdn.com/mflasko 與他聯系。
本文配套源碼:http://www.bianceng.net/dotnet/201212/809.htm