Visual Studio 2008 和 .NET Framework 3.5 多了不少新玩意兒,例如:multi-targeting更強的 Web UI designerC# 3.0VB 9.0單步除錯時可直接 step into .NET Framework 原始碼JavaScript IntellisenseLINQ......,好多東西。不過,除了這些功能,更令我感興趣的,是目前還在 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)

步驟

  1. 開啟 VS2008,從主選單點 File > New > Web Site,範本選擇「Dynamic Data WebSite」,然後輸入網站的位置,並選擇程式語言要用 C# 或 VB,按 OK 即可建立新的 Web 專案。以此專案範本所建立的網站,會提供基本的資料維護功能(增刪改查)。你可以開啟 Default.aspx 的設計頁面,並且大致瀏覽一下 Solution Explorer 裡面的檔案和資料夾,看看增加了哪些東西(根目錄下的三個 .css 檔案,以及 App_Shared 資料夾底下的樣板檔)。
  2. 接著要指定你希望這個網站應用程式所要維護的資料表有哪些。先在 Solution Explorer 中的網站名稱上點右鍵,選 Add New Item,接著選擇「LINQ to SQL Classes」,檔案名稱輸入 "Northwind.dbml"。按 OK 後會彈出一個對話窗,問你是否要將這個檔案放在新建立的 App_Code 資料夾下,選 Yes。
  3. 此時編輯區會開啟 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。

  4. 最後一步是修改一下 Web.config 的設定,找到這行:

    <dynamicData dataContextType="" enableTemplates="false">

    並設定 dataContextType 和 enableTemplates 屬性,如下所示:

    <dynamicData dataContextType="NorthwindDataContext" enableTemplates="true">
     
  5. 在瀏覽器中檢視 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 的客戶編號欄位為例,做法如下:

  1. 在 App_Code 資料夾中加入一個新類別: Customer.cs。
  2. 將 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(支架;意指建立網站架構)的部份,而非全貌。正如本文開頭所說的,要學的東西還很多啊...... @_@