[VS2010] .NET Framework 4.0: ADO.NET Data Services 新功能

[VS2010] .NET Framework 4.0: ADO.NET Data Services 新功能

時間回到 2007 年 …

筆者當時剛好也正在關注 Visual Studio “Orcas” (即 Visual Studio 2008) Beta 的事宜,以及微軟的新一代資料存取技術 Entity Framework 時,意外在 MSDN blogs 中發現一個好玩的東西,這個東西可以直接將資料開放於 HTTP 協定中,並且可以以類似於 SQL 指令的方式存取,它也是作為 Entity Framework 的輕量級前端應用,代號為 Astoria,後來命名為 ADO.NET Data Services,它是由 WCF + Object Services 方式將 Entity Framework 的 Model 發布到網路上的一種管道,它可以簡化開發人員開放資料到網路上所需要的工作,而且對於 Silverlight (當時 2.0 也接近 Release) 特別設計一個 .NET Client Library 以簡化 Silverlight 的資料應用程式的開發。而非 Silverlight 的應用程式也可以使用 HTTP-based 的指令來存取,回傳的格式支援 JSON 以及 ATOM,適合 Mashup 類型的應用程式來存取。

 

筆者覺得這個工具很有可能會改變用戶端應用程式以及 Mashup 應用程式的資料存取方式,因此特別替 MSDN 撰寫了這篇文章:http://www.microsoft.com/taiwan/msdn/columns/jhu_ming_jhong/Astoria_Web_Data_Service.htm,後來在 Visual Studio 2008 推出那年的 Tech.Ed (TechEd 2008),筆者也受邀主講 ADO.NET Data Services 這個主題 (WUX305: 隨手玩資料 – ADO.NET Data Services),這個技術是少數我由一開始在關注,並且看著它長大的一項技術。它也確實獲得了 Silverlight 以及輕量級開發人員的歡迎與採用。

 

現在 Visual Studio 2010 以及 .NET Framework 4.0 即將推出之際,ADO.NET Data Services 也即將推出 1.5 版,筆者仍然對它很有期待,因為它確實是一個很有趣的輕量級管道,它也是一個 WCF 的設計案例,開發人員不必再自行開發 HTTP Handler 或是 Web Services 來處理資料溝通的工作,而 1.5 的一些新功能,也大幅擴充了它的彈性。

 

1. Data Binding Collection

 

在 ADO.NET Data Services 1.0 中,如果要做資料更新的,大多都是要手動去做非同步的更新,但大家也都知道,WPF 和 Silverlight 可以支援資料變更時的自動更新,對於 Data Services 來說,控制項的資料更新會自動放在暫存的資料中,然後由使用者去下 SaveChanges() 方法來提交結果到資料庫中,這個動作已經可以在 1.5 中實現,它所提供的 DataServiceCollection 類別不但繼承自 ObservableCollection 集合類別,也實作了 WPF (以及 Silverlight) 中的 INotifyCollectionChanged 介面以提供資料異動時的更新通知,而所有使用這個集合類別的物件都必須要實作 INotifyPropertyChanged 介面,以接收來自 DataServiceCollection 集合所發出的更新事件。

 

2. Count Property

 

Data Services 1.5 中新提供了兩個動詞,一個是路徑指示詞 $count,一個是查詢指示詞 $inlinecount,$count 的用意是直接設定取得這個查詢結果會回傳的資料列數,而 $inlinecount 則是設定資料集的回傳數量。

 

$count 會用在查詢的路徑表示中,它的用法是:

http://myserver/Northwind.svc/Customers/$count (回傳 Customers Entity 有多少資料列)

http://myserver/Northwind.svc/Customers/$count?expand=Orders&filter=Country eq 'Germany' (回傳客戶的國家是在德國的客戶數)

其實 $count 是為了要支援 .NET Client Library 中對 LINQ 的 count() 指令。

 

$inlinecount 的用法則是:

 
 
$inlinecount 可支援的值只有 allpages 和 none 兩種,但其實這兩個值的差別只有在 allpages 會輸出 count 在回應訊息中,但 none 不會,這可以讓可以被外界收集的資料縮小,也就是說外界不會知道你的 Entity 中有多少筆的資料。

 

3. Server-driven paging

 

這是 1.5 版的新增功能,它可以由伺服器決定每一次分頁的大小,在 DataServiceConfiguration 中開放了一個新的 SetEntitySetPageSize(name, count) 的方法,它可以設定指定的 (或用 “*” 來表示全部的) Entity Set 每一頁的資料列個數,如下面的程式:

 

config.SetEntitySetPageSize("Customers", 10);
config.SetEntitySetPageSize("Orders", 5);

 

4. Query Projection

 

查詢投影 (query projection) 是 1.5 的新功能,只要有用過 1.0 版的開發人員基本上都會問:為什麼沒有可以指定查詢欄位的功能呢?也就是說,1.0 版只支援 SELECT *,但為了效能以及資料量的考量,更多開發人員希望可以有 SELECT f1, f2, … 的方法,因此微軟特別在 1.5 中加入了一個新的查詢選項子 $select,它可以設定要回傳的 entity 的欄位有哪些,這樣開發人員就不必再以 SELECT * 為主,它在特定案例中也可以防止過多關鍵性的資料外流。

 

http://myserver/Northwind.svc/Orders(10643)?$select=OrderID,Order_Details

 

同樣的,在 .NET Client Library 中的 LINQ select 指令 (VB 是 Select) 也終於可以支援設定要擷取的欄位的功能了。

 

5. Custom Data Service Provider

 

以往 1.0 版只支援 Entity Framework 的 Data Model,在 1.5 版時將會有所改變,微軟重寫了使用在 Data Services 的 Provider 基底類別,並提供了一組新的 Provider:Reflection Provider,它可以讓現有已建立的程式化資料模型 (簡單的說,就是現有的類別啦~) 可以直接套用在 Data Service 上作為資料模型,筆者認為這是一個很大的突破,因為已經自己開發 DAL 或是 BAL 的企業可以利用這個方式直接將現有的元件開放給 Data Services,而不必再繞經 Entity Framework 再使用 Data Services。例如下列的程式碼就是一個使用 Reflection Provider 開放模型給 Data Service 的範例:

 

using System;
using System.Collections.Generic;
using System.Data.Services;
using System.Data.Services.Common;
using System.Linq;

namespace CustomDataServiceClient
{
    [DataServiceKeyAttribute("OrderId")]
    public class Order
    {
        public int OrderId { get; set; }
        public string Customer { get; set; }
        public IList<Item> Items { get; set; }
    }
   
[DataServiceKeyAttribute("Product")]
    public class Item
    {
        public string Product { get; set; }
        public int Quantity { get; set; }
    }
    public partial class OrderItemData
    {
        #region Populate Service Data
        static IList<Order> _orders;
        static IList<Item> _items;
        static OrderItemData()
        {
            _orders = new Order[]{
              new Order(){ OrderId=0, Customer = "Peter Franken", Items = new List<Item>()},
              new Order(){ OrderId=1, Customer = "Ana Trujillo", Items = new List<Item>()}};
            _items = new Item[]{
              new Item(){ Product="Chai", Quantity=10 },
              new Item(){ Product="Chang", Quantity=25 },
              new Item(){ Product="Aniseed Syrup", Quantity = 5 },
              new Item(){ Product="Chef Anton's Cajun Seasoning", Quantity=30}};
            _orders[0].Items.Add(_items[0]);
            _orders[0].Items.Add(_items[1]);
            _orders[1].Items.Add(_items[2]);
            _orders[1].Items.Add(_items[3]);
        }
        #endregion
        public IQueryable<Order> Orders
        {
            get { return _orders.AsQueryable<Order>(); }
        }
        public IQueryable<Item> Items
        {
            get { return _items.AsQueryable<Item>(); }
        }
    }
    public class OrderItems : DataService<OrderItemData>
    {
        // This method is called only once to initialize
        //service-wide policies.
        public static void InitializeService(IDataServiceConfiguration config)
        {
            config.SetEntitySetAccessRule("Orders", EntitySetRights.All);
            config.SetEntitySetAccessRule("Items", EntitySetRights.All);
        }
    }
}

 

6. Streaming of binary resource

 

1.5 版的 Data Services 利用 ATOM 提供的 Media Link 屬性,實作了一個可以由遠端下載二進位資料流的功能,此功能以 IDataServiceStreamProvider 提供。用戶端可以使用 DataServiceContext 的 GetReadStream() 取得讀取的資料流,而使用 SetSaveStream() 將要更新的二進位資料流設定給 DataServiceContext 以更新二進位資料。

 

參考資料:

What’s new of ADO.NET Data Services

http://msdn.microsoft.com/en-us/library/ee373845(VS.100).aspx