摘要:ASP.NET 3.5 Extensions: Dynamic Data 初體驗 (1)
Visual Studio 2008 和 .NET Framework 3.5 多了不少新玩意兒,例如:multi-targeting、更強的 Web UI designer、C# 3.0、VB 9.0、單步除錯時可直接 step into .NET Framework 原始碼、JavaScript Intellisense 、LINQ......,好多東西。不過,除了這些功能,更令我感興趣的,是目前還在 preview 階段的 ASP.NET 3.5 Extensions,我比較想試試其中的 MVC、Dynamic Data、和 ADO.NET Entity Framework,以了解未來 ASP.NET 對於 MVC 和 O/R mapping(物件/關聯對應)架構的實作方式,以及對程式開發有哪些實質幫助。
如官方網站所揭示的,目前 ASP.NET 3.5 Extensions preview 版本的內容包括:
- ASP.NET MVC - 讓開發人員可以採用 MVC (model-view-controller) 的模式開發 ASP.NET 應用程式。
- ASP.NET Dynamic Data - 可讓開發人員快速建立資料庫應用程式。
- ASP.NET AJAX - 對既有 ASP.NET AJAX 的擴充。
- ADO.NET Entity Framework - O/R mapping 架構。
- ADO.NET Data Services - 讓應用程式提供以 URI 的方式供用戶端存取後端資料 (a.k.a. Astoria)。也就是說,用戶端可採用 REST(representational state transfer)的形式,透過網址來存取特定資料,例如:http://www.myweb.com/NorthwindDataService.svc/Products?$skip=10&$top=10 將傳回第 11 至 20 筆的產品資料。
- Silverlight Controls for ASP.NET - 提供 MediaPlayer 以及 Silverlight 伺服器控制項。
除了 IDE 的強化功能,其他屬於 framework 的部分恐怕都得花不少時間學習。官方網站上有提供幾個影片教學,能夠節省一些摸索時間。我先看了 David Ebbo 製作的 ASP.NET Dynamic Data 教學影片, 覺得很有幫助,若要學習 Dynamic Data 技術,個人建議先看過這個影片。跟著影片依樣畫葫蘆,我也做了點小練習,並將練習的步驟整理並貼上,不過,或許是我下載的 ASP.NET 3.5 Extensions 版本跟教學影片中使用的版本不同,若依照影片中的步驟,在瀏覽網頁時會發生錯誤,關鍵的差異在下面的步驟 4。
以下是練習步驟:
環境需求
Visual Studio 2008 + ASP.NET 3.5 Extensions (此範例使用的版本為 3.6.20830.0)
步驟
- 開啟 VS2008,從主選單點 File > New > Web Site,範本選擇「Dynamic Data WebSite」,然後輸入網站的位置,並選擇程式語言要用 C# 或 VB,按 OK 即可建立新的 Web 專案。以此專案範本所建立的網站,會提供基本的資料維護功能(增刪改查)。你可以開啟 Default.aspx 的設計頁面,並且大致瀏覽一下 Solution Explorer 裡面的檔案和資料夾,看看增加了哪些東西(根目錄下的三個 .css 檔案,以及 App_Shared 資料夾底下的樣板檔)。
- 接著要指定你希望這個網站應用程式所要維護的資料表有哪些。先在 Solution Explorer 中的網站名稱上點右鍵,選 Add New Item,接著選擇「LINQ to SQL Classes」,檔案名稱輸入 "Northwind.dbml"。按 OK 後會彈出一個對話窗,問你是否要將這個檔案放在新建立的 App_Code 資料夾下,選 Yes。
- 此時編輯區會開啟 Northwind.dbml,但裡面沒有東西,我們要從 Server Explorer 中拉一些資料表進來。這個動作相信大家都不陌生,簡單描述一下就好:在 Server Explorer 視窗中的 Data Connections 項目上點右鍵,選 Add Connection,設定連線字串,並指定連接 Northwind 資料庫。設定完成後,在 Server Explorer 中把這四個 table 拖到 Northwind.dbml 設計區域: Categories, Customers, Orders, Products,你會看到如下的資料關聯圖(比較正確的說法,應該是 entity class diagram 吧):
這 些類別分別對應至資料庫中的特定 table,請注意原本是複數的 table 名稱,其對應的類別名稱都變成單數了。如果希望在產生這些 entity class 時完全依照實體 table 命名,可以到 Tools > Options > Database Tools > O/R Designer 選項中把 "Pluralization of names" 的 Enable 屬性設為 False。 - 最後一步是修改一下 Web.config 的設定,找到這行:
<dynamicData dataContextType="" enableTemplates="false">
並設定 dataContextType 和 enableTemplates 屬性,如下所示:
<dynamicData dataContextType="NorthwindDataContext" enableTemplates="true">
- 在瀏覽器中檢視 Default.aspx,應該會看到如下畫面:
畫面上列出你之前選擇的四個資料表,試點一下 Categories 連結,會進入資料表的維護頁面,如下圖:
你 會發現,它連資料關聯的部份都幫你處理好了。在上圖中,你可以點每一筆分類資料右邊的 "View Products" 連結,便可檢視在那個分類之下有哪些產品,進入產品資料維護頁面後,還會看到 grid 上方提供了兩個篩選條件的欄位。除了可以在 Grid 中新增、刪除、修改欄位資料,每個資料維護頁面的下方也都有一個編輯區塊,讓你可以修改欄位資料。你可以嘗試把 key field 欄位內容清掉,會發現連 field validation 的處理都幫你做好了。
跟以往一樣,微軟的開發工具總是會提供一些精靈、範本,讓開發人員連一行 code 都不用寫,就完成一個具備基本(甚至不錯)功能的應用程式(很適合用在 demo 的場合,往往令人印象深刻)。而我比較好奇的是,如果不用專案範本(實際開發專案時通常不會用),我要寫多少 code 才能完成類似的東西,以及如何修改預設的範本以符合自己的需求。
加一點客製化
1. 修改欄位名稱
有個 quick & dirty 的方法可將欄位名稱改成中文,就是開啟 Northwind.dbml 設計頁面,點選每一個欄位,並在屬性視窗中修改 Name 屬性即可。如下圖:
不過,此方法有個問題,就是程式裡面的一些變數和函式名稱也會跟著變成中文,例如: On客戶編號Changing,看起來挺怪的。比較好的解法
晚點會補上來請看下一篇:〈ASP.NET Dynamic Data 學習筆記:客製化網頁〉。
2. 自訂欄位驗證
將欄位名稱改成中文後,你會碰到一些問題,例如,欄位驗證失敗的訊息變成中英混雜:
此時你需要的是自訂欄位驗證處理,方法有兩種,第一種是利用驗證特徵項(validation attribute)的方式,以 Customer 的客戶編號欄位為例,做法如下:
在 App_Code 資料夾中加入一個新類別: Customer.cs。 將 Customer 類別宣告之前加上關鍵字 "partial",並將建構函式刪除,然後將 RequiredAttribute 套用至類別。完成後的 Customer 類別看起來會像這樣:
using System;
using System.ComponentModel;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Data.Linq.Mapping;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Reflection;
using System.Web.DynamicData;
[Required("客戶編號", ErrorMessage="客戶編號不可空白!")]
public partial class Customer
{
}
Required 特徵項定義於 System.Web.DynamicData 命名空間,其用途即指定某個欄位不可為空值,以及當欄位為空值時的錯誤訊息。修改完成後,當客戶編號欄位為空字串時,就會顯示 "客戶編號不可空白!",而不是預設的錯誤訊息:
除了 RequiredAttribute,另外還有 RangeAttribute、RegexAttribute 等驗證特徵項可用來檢查欄位值。
對於驗證特徵項無法滿足的需求,就必須採用第二種方法:自行撰寫驗證程式碼。延續前面的例子,Customer 類別可以改成這樣:
public partial class Customer
{
partial void On客戶編號Changing(string value)
{
if (String.IsNullOrEmpty(value))
{
throw new Exception("客戶編號不可空白!");
}
}
}注意 On客戶編號Changing 方法前面的 "partial" 關鍵字,這是 C# 3.0 新增的局部方法(partial methods)語法。本文最後再來進一步說明這個新增的語法。
實驗結果顯示,這種撰寫驗證程式碼的方法,如果欄位原本就有限制規則(constraints),必須先將之去除,以這個例子來說,"客戶編號" 欄位的 Nullable 屬性預設為 False,因此即使你寫了上述程式碼,也沒有任何作用,程式執行時總是會顯示預設的錯誤訊息。若將 "客戶編號" 欄位的 Nullable 屬性值改為 True(開啟 Northwind.dbml,點選 Customer 累別的 "客戶編號",再到屬性視窗修改 Nullable 屬性),則離開欄位時不會觸動驗證檢查,須等到儲存該筆記錄時才會顯示你的驗證程式碼丟出的錯誤訊息;驗證特徵項則是在游標離開欄位時就執行檢查,這是兩 種方法的主要差異。
C# 3.0 Partial Methods 語法
這裡補充說明一下前面提到 partial methods 語法。C# 2.0 的 partial class 可讓你將一個類別的實作程式碼拆開並分別存放在不同的檔案,但 partial method 並不是讓你將一個 method 的實作程式碼拆開分散在多個局部類別,而是讓類別可以預先指定「某個方法在未來可能會有其他局部類別提供其實作」。先看一下 partial methods 的基本寫法應該會比較容易了解:
partial class Foo { partial void OnTextChanged(); // 注意這裡沒有函式本體,只是一個宣告. } // 在 Foo_Partial.cs 的類別定義 partial class Foo { partial void OnTextChanged() { // 實作程式碼 } }
也就是說,當你在撰寫 Foo.cs 時,可以預先宣告 OnTextChanged 局部方法,你甚至可以在類別中呼叫 OnTextChanged 方法(不管是否真的有該方法的實作)。當程式在編譯時,編譯器如果發現沒有其他局部類別實作這個局部方法,那麼 OnTextChanged 的方法宣告以及所有對 OnTextChanged 的方法呼叫都會被編譯器忽略(不會編譯至組件中)。這種機制對一些可有可無的事件處理程序非常方便,因為你不用在類別中定義委派型別,也不需要訂閱事件; 你只要在需要處理該事件時,實作對應的局部方法就行了。局部方法也令我想到虛擬方法(virtual methods),如果我在類別中定義一個空的虛擬方法(不做任何事,直接返回)讓子類別改寫(override),也能達到類似的效果;主要的差異,在 於虛擬方法需要使用類別繼承,而且即使沒有子類別改寫這個虛擬方法,它還是會被編譯到組件裡面。
小結
如果您有看 David Ebbo 的教學影片,或者有嘗試做前面的練習,我想您大概也會同意 ASP.NET Dynamic Data 確實是令人印象深刻的技術。我們看到它如何幫我們在幾分鐘內就完成一個資料驅動的 Web 應用程式,而這樣的網頁程式,純手工打造恐怕至少得要半天甚至一天的時間。然而,從 Scott Hunter 的Breakdown of Dynamic Data Features 一文中可以發現,這個範例僅呈現 Dynamic Data 技術的其中一塊,也就是 scaffolding(支架;意指建立網站架構)的部份,而非全貌。正如本文開頭所說的,要學的東西還很多啊...... @_@