ASP.NET 2.0 中的新增服務、控件與功能概述

ASP.NET 2.0 中的新增服務、控件與功能概述

作者:Jeff Prosise
相關技術:ASP.NET 2.0、數據訪問、安全
難度:★★☆☆☆
讀者類型:Web開發人員

    [導讀]全文介紹了ASP.NET 2.0中新增的控件和功能,包括Master Pages,Data Source控件、Skin的支持、GridView和Details View的引入,在此基礎上,ASP.NET 2.0的安全模型得到了極大的提高,包括登錄控件、角色管理器、個性化等等方面的支持,在類庫方面也增加了許多新特性,如全新的代碼分隔模型、客戶端回調等等,讓讀者對於ASP.NET的新特性有一個全面的瞭解。

    在首次公諸於衆以來的短短四年中,ASP.NET業已成爲在Windows傾力支持的服務器上運行的Web應用程序的金科玉律,並且將runat="server"添加到供世界各地Web開發人員使用的詞彙表中。它還爲人們所認識的Web編程的前景提供了一些啓示:Web編程將以能夠呈現HTML和腳本以及激發事件的服務器端控件爲中心。

    在Microsoft .NET Framework的下一個主要版本中,ASP.NET 2.0將擺脫某些不完善的狀況並發展爲一種完全成熟的產品。它的目標是將完成常見Web編程任務所需的代碼數量減少70%或更多。儘管該目標非常宏大,但是由於多種多樣的新增服務、控件和功能(它們一定會使ASP.NET 2.0像ASP.NET 1.x之於ASP一樣,使ASP.NET 1.x顯著提高),該目標是可以實現的。

    這裏,我將爲您粗略介紹一些期望在ASP.NET 2.0中看到的內容,對於幾個精選的領域進行深入討論並且提供一些示例程序來強調關鍵的功能。所有代碼示例均針對ASP.NET 2.0的預測試版本生成和測試的,可能需要對某些代碼示例進行修改,以便用於第一個測試版本。

母版頁(Master Pages)

    ASP.NET 1.x中最突出的缺點之一是它缺少對頁面模板的支持。欠缺的是定義其他頁面可以繼承的“母版頁”能力。開發人員通過使用用戶控件(它們可以容易地在頁面之間複製)創建頁面來彌補這一缺陷。在ASP.NET 2.0中,由於加入了一個稱作“母版頁”的新增功能,所以不再需要這種技巧。想一想“可視化繼承”,您就會理解母版頁是幹什麼用的了。首先,您需要定義一個母版頁(其中包含您希望在其他頁面上顯示的內容),並且使用ContentPlaceHolder控件來定義子頁面可以將其內容插入的位置。然後,您需要生成子頁面—SPX文件,它使用(類似於)如下所示的指令來引用母版頁:

<%@ Page MasterPageFile="~/Foo.master" %>

    在子頁面中,您使用Content控件來填寫母版頁中的佔位符。在瀏覽器中呈現子頁面,出現的內容將是在母版頁和子頁面中所定義內容的完美組合。

<%@ Master %>

<html>
    <body leftmargin="0" topmargin="0" rightmargin="0"
        bottommargin="0" marginheight="0" marginwidth="0"
    >
        <!-- Banner -->
        <table cellspacing="0" cellpadding="0"
            style="background-image: url('images/stripes.gif');
            background-repeat: repeat-x, repeat-y" width="100%"
        >
            <tr><td align="center">
                <span style="font-family: verdana; font-size: 36pt;
                    font-weight: bold; color: white"
                >
                    Master Pages
                </span><br>
                <span style="font-family: verdana; font-size: 10pt;
                    font-weight: normal; color: white"
                >
                    This banner provided by Master.master
                </span>
            </td></tr>
        </table>

        <!-- Placeholder for content between banner and footer -->
        <form id="Content">
            <asp:ContentPlaceHolder ID="Main" RunAt="server" />
        </form>

        <!-- Footer -->
        <table width="100%"><tr><td align="center">
            <span style="font-family: verdana; font-size: 8pt; color: red">
                Copyright (c) 2004 by Me Inc. All rights reserved<br>
                This footer provided by Master.master
            </span>
        </td></tr></table>
    </body>
</html>

Master.master

<%@ Page MasterPageFile="~/Master.master" %>

<asp:Content ContentPlaceHolderID="Main" RunAt="server">
    <table width="100%" height="256px"><tr><td align="center">
        <h2>This content provided by Subpage.aspx</h2>
    </td></tr></table>
</asp:Content>

圖1 Subpage.aspx

    圖1中所示的應用程序使用母版頁來定義出現在每個頁面上的頁眉和頁腳。子頁面通過將Content控件插入母版頁的ContentPlaceHolder中,從而在頁眉和頁腳之間插入內容。您應該注意匹配的ID和ContentPlaceHolderID,以及母版頁中的@ Master指令。

    母版頁在ASP.NET對象模型中得到了充分支持。System.Web.UI.Page類的特點是具有一個名爲Master的新屬性,該屬性使子頁面可以用編程方式來引用它的母版頁以及在其中定義的控件。母版頁可以嵌套,並且可以包含能夠在子頁面中重寫的默認內容。

<asp:ContentPlaceHolder ID="Main" RunAt="server">
    This is default content that will appear in subpages unless
    explicitly overridden
</asp:ContentPlaceHolder>

    此外,應用程序可以在Web.config中指定默認的母版頁,如下所示:

<configuration>
    <system.web>
        <pages masterPageFile="~/Foo.master" />
    </system.web>
</configuration>

    單個子頁面享有重寫默認母版頁和指定它們自己的母版頁的自由。

    最精彩的部分是Visual Studio® 2005中對母版頁的支持。當加載子頁面時,IDE將顯示母版頁中定義的內容的灰色、只讀版本,以及子頁面中定義的內容的全色、完全可編輯版本。區分這兩者很容易,並且如果要編輯屬於母版頁的內容,那麼您需要做的全部事情只是在IDE中打開母版頁。

    有關母版頁的更多深入內容,請參閱本期雜誌相關的母版面的文章。

數據源控件

    數據綁定在ASP.NET 1.x中佔據着顯著位置。幾行位置恰當的數據綁定代碼可以取代大量查詢數據庫,並使用對Response.Write方法的重複調用,從而將查詢結果轉換爲HTML內容的ASP代碼。

    下面的DataSource1.aspx頁使用ASP.NET 2.0數據綁定來顯示SQL Server Pubs數據庫的一部分:

<html>
    <body>
        <form runat="server">
            <asp:SqlDataSource ID="Titles" RunAt="server"
                ConnectionString="server=localhost;database=pubs;Integrated
                Security=SSPI"
                SelectCommand="select title_id, title, price from titles"
            />
            <asp:DataGrid DataSourceID="Titles" RunAt="server" />
        </form>
    </body>
</html>

    SqlDataSource控件定義了數據源以及對它執行的查詢,並且DataGrid的DataSourceID屬性指向SqlDataSource。當加載該頁時,SqlDataSource控件就會執行查詢,並將結果提供給DataGrid。

    當然,實際操作中的數據綁定很少會如此簡單。假設您希望緩存查詢結果,或者使用在其他控件中選擇的項目來參數化數據庫查詢。圖2 中的頁使用一個SqlDataSource,用Northwind的Customers表中列出的國家/地區來填充一個下拉列表,並使用另一個SqlDataSource,用在該下拉列表中選擇的國家/地區中的一系列客戶來填充一個DataGrid。請注意元素,它指示DataGrid的SqlDataSource從下拉列表中獲取@country的值。還請注意綁定到該下拉列表的SqlDataSource中的EnableCaching和CacheDuration屬性。這些聲明將SELECT DISTINCT查詢的結果緩存60秒。

    上述示例只是粗淺地探討了可以通過數據源控件完成的工作。例如,您可以使用存儲過程,可以使用從查詢字符串、用戶輸入、會話狀態和Cookie中提取的值來參數化查詢,並且可以指定控件是否應該使用DataSet或DataReader。因爲數據源控件包含數據適配器的功能,所以您甚至可以使用數據源控件來更新數據庫。隨着ASP.NET 2.0最終發佈日期的迫近,讀者有望看到大量有關數據源控件的文章。在本期雜誌中,Dino Esposito在其文章中更爲詳盡地討論了這些控件。

    儘管我所討論的是數據綁定主題,但您還應該知道ASP.NET 2.0支持簡化的數據綁定語法。ASP.NET開發人員可以找到與下面這個給人深刻印象的表達式類似的表達式:

<%# DataBinder.Eval (Container.DataItem, "title") %>

    在ASP.NET 2.0中,可以按如下形式寫一個相同的表達式:

<%# Eval("title") %>

    除了Eval運算符以外,ASP.NET 2.0還支持名爲XPath和XPathSelect(它們使用指向XML文檔中數據的XPath表達式)的運算符。

主題與外觀(skins)

    如果您不使用屬性來豐富控件,那麼ASP.NET頁面可能會顯得非常單調。問題在於:迄今爲止,一次只能應用一個屬性,並且您無法通過成組地設置控件的可視屬性來賦予控件主題。ASP.NET 2.0提供了一項新功能,即輸入主題和外觀。該功能簡化了生成精美頁面的任務。

    要查看主題和外觀的工作方式,請將以下指令添加到DataSource2.aspx中,如圖2 所示:

<html>
    <body>
        <form runat="server">
            <asp:SqlDataSource ID="Countries" RunAt="server"
                ConnectionString="server=localhost;database=northwind;
                Integrated Security=SSPI"
                SelectCommand="select distinct country from customers order by country"
                EnableCaching="true" CacheDuration="60"
            />
            <asp:SqlDataSource ID="Customers" RunAt="server"
                ConnectionString="server=localhost;database=northwind;
                Integrated Security=SSPI"
                SelectCommand="select * from customers where country=@country"
            >
                <SelectParameters>
                    <asp:ControlParameter Name="Country"
                        ControlID="MyDropDownList"
                        PropertyName="SelectedValue"
                    />
                </SelectParameters>
            </asp:SqlDataSource>
            <asp:DropDownList ID="MyDropDownList" DataSourceID="Countries"
                DataTextField="country" AutoPostBack="true" RunAt="server"
            />
            <asp:DataGrid DataSourceID="Customers" RunAt="server" />
        </form>
    </body>
</html>

圖2 DataSource2.aspx

    現在刷新該頁面。結果如圖3中所示。


圖3 主題和外觀的工作方式

    接下來,賦予該頁面完全不同的外觀,方法是:添加一個元素,並且更改@ Page指令以指示要使用的頁面主題:

    @ Page指令的新Theme屬性以聲明方式將主題應用到頁面中。還可以使用Page類的Theme屬性以編程方式應用主題。主題是外觀的集合,而外觀是應用於一個控件類型的一組可視屬性。BasicBlue和SmokeAndGlass是ASP.NET 2.0附帶的幾個預定義(或全局)主題中的兩個。您可以在Microsoft.NET/Framework/.../ ASP.NETClientFiles/Themes下的子目錄中找到它們。

    您可以定義自己的主題和外觀,並且自定義地將它們部署到應用程序Themes目錄的子目錄中。每個子目錄構成一個主題,並且主題名稱與子目錄名稱相同。一個主題子目錄包含一個或多個.skin文件以及該主題使用的任何其他資源(例如,圖像文件和樣式表)。實際的外觀定義包含在.skin文件中,並且非常類似於用來在ASPX文件中聲明控件實例的標記。

    爲說明這一點,請在包含DataSource2.aspx的文件夾中創建一個名爲Themes的子目錄。在該Themes目錄中,創建一個名爲ShockingPink的子目錄。將以下內容複製到ShockingPink目錄中的新建的.skin文件中:

<asp:DropDownList runat="server" BackColor="hotpink" ForeColor="white" />
<asp:DataGrid runat="server" BackColor="#CCCCCC" BorderWidth="2pt"
    BorderStyle="Solid" BorderColor="#CCCCCC" GridLines="Vertical"
    HorizontalAlign="Left"
>
    <HeaderStyle ForeColor="white" BackColor="hotpink" />
    <ItemStyle ForeColor="black" BackColor="white" />
    <AlternatingItemStyle BackColor="pink" ForeColor="black" />
</asp:DataGrid>

    接下來,將DataSource2.aspx中的@ Page指令更改爲:

<%@ Page Theme="ShockingPink" %>

    上述操作的結果無疑是曾經創建過的最爲華麗的Web頁之一。

    ShockingPink.skin定義了DropDownList和DataGrid控件的默認外觀。外觀文件不必與它們所屬的主題具有相同的名稱(儘管它們通常如此)。一個主題可以包含多個.skin文件,而單個.skin文件可以爲任意數量的控件類型定義屬性。此外,ASP.NET還可以區分默認外觀和非默認外觀。根據定義,缺少SkinID屬性的外觀是默認外觀。非默認外觀包含可以通過控件標記中的SkinID屬性引用的SkinID。

新增控件

    ASP.NET 2.0將引入大約50種新的控件類型,以便幫助您生成豐富的用戶界面,同時使您無須應付HTML、客戶端腳本和瀏覽器文檔對象模型(DOM)的各種變幻莫測的行爲。“新增控件”提要欄列出了直至本文撰寫時規劃的新控件(Web部件控件除外)。

    DynamicImage控件簡化了在Web頁中顯示動態生成的圖像的任務。在過去,開發人員經常編寫自定義HTTP處理程序來處理動態圖像生成,甚至更糟糕的是,處理ASPX文件中生成的圖像。DynamicImage使得這兩種技術都過時了。圖4 中的代碼使用DynamicImage控件來繪製餅圖。關鍵語句是那個將圖像位分配給控件的ImageBytes數組的語句。

    DynamicImage控件利用了新增的ASP.NET 2.0圖像生成服務。另一種訪問圖像生成服務的方法是,在ASP.NET 2.0的全新ASIX文件中動態生成圖像。本文隨附的示例文件(可從MSDN Magazine Web站點上獲得)包含一個名爲DynamicImage.asix的文件,它展示了ASIX文件的要素。要運行該文件,請將DynamicImage.asix複製到Web服務器的某個虛擬目錄中,然後在瀏覽器中激活該文件。

<%@ Import Namespace="System.Drawing" %>
<%@ Import Namespace="System.Drawing.Imaging" %>
<%@ Import Namespace="System.IO" %>

<html>
    <body>
        <asp:DynamicImage ID="PieChart" DynamicImageType="ImageBytes"
            RunAt="server"
        />
    </body>
</html>

<script language="C#" runat="server">
void Page_Load (Object sender, EventArgs e)
{
    // Create a bitmap and draw a pie chart
    Bitmap bitmap = new Bitmap (240, 180, PixelFormat.Format32bppArgb);
    Graphics g = Graphics.FromImage (bitmap);
    DrawPieChart (g, Color.White, new decimal[]
        { 100.0m, 200.0m, 300.0m, 400.0m }, 240, 180);
    g.Dispose();

    // Attach the image to the DynamicImage control
    MemoryStream stream = new MemoryStream ();
    bitmap.Save (stream, ImageFormat.Gif);
    bitmap.Dispose();
    PieChart.ImageBytes = stream.ToArray ();
}

void DrawPieChart (Graphics g, Color bkgnd, decimal[] vals, int width, int height)
{
    // Erase the background
    SolidBrush br = new SolidBrush (bkgnd);
    g.FillRectangle (br, 0, 0, width, height);
    br.Dispose ();

    // Create an array of brushes
    SolidBrush[] brushes = new SolidBrush[6];
    brushes[0] = new SolidBrush (Color.Red);
    brushes[1] = new SolidBrush (Color.Yellow);
    brushes[2] = new SolidBrush (Color.Blue);
    brushes[3] = new SolidBrush (Color.Cyan);
    brushes[4] = new SolidBrush (Color.Magenta);
    brushes[5] = new SolidBrush (Color.Green);

    // Sum the inputs
    decimal total = 0.0m;
    foreach (decimal val in vals)
        total += val;

    // Draw the chart
    float start = 0.0f;
    float end = 0.0f;
    decimal current = 0.0m;

    for (int i=0; i<vals.Length; i++)
    {
        current += vals[i];
        start = end;
        end = (float) (current / total) * 360.0f;
        g.FillPie (brushes[i % 6], 0.0f, 0.0f, width, height,
            start, end - start);
    }

    // Clean up and return
    foreach (SolidBrush brush in brushes)
        brush.Dispose ();
}
</script>

圖4 DynamicImage.aspx

    ASP.NET 2.0中首次亮相的另一個有趣且非常有用的控件是MultiView控件。與View控件搭配使用時,MultiView可用來創建包含多個邏輯視圖的頁面。一次只能顯示一個視圖(其索引被分配給MultiView的ActiveViewIndex屬性的那個視圖),但您可以通過更改活動視圖索引來切換視圖。對於使用選項卡或其他控件來讓用戶在邏輯頁之間進行導航的頁面而言,MultiViews是非常理想的。

    圖5 中的頁面使用一個MultiView來顯示Pubs數據庫Titles表的兩個不同視圖:其中一個用GridView呈現,另一個用DetailsView呈現。視圖切換是通過從下拉列表中進行選擇來完成的。請注意,標記中的AllowPaging屬性使用戶可以瀏覽DetailsView中的記錄。

<%@ Page Theme="BasicBlue" %>

<html>
    <body>
        <form runat="server">
            <asp:SqlDataSource ID="Titles" RunAt="server"
                ConnectionString="server=localhost;database=pubs;
                Integrated Security=SSPI"
                SelectCommand="select title_id, title, price from titles"
            />
            <asp:DropDownList ID="ViewType" AutoPostBack="true"
                OnSelectedIndexChanged="OnSwitchView" RunAt="server"
            >
                <asp:ListItem Text="GridView" Selected="true" RunAt="server" />
                <asp:ListItem Text="DetailsView" RunAt="server" />
            </asp:DropDownList>
            <asp:MultiView ID="Main" ActiveViewIndex="0" RunAt="server">
                <asp:View RunAt="server">
                    <asp:GridView DataSourceID="Titles" RunAt="server" />
                </asp:View>
                <asp:View RunAt="server">
                    <asp:DetailsView DataSourceID="Titles" AllowPaging="true"
                        RunAt="server"
                    />
                </asp:View>
            </asp:MultiView>
        </form>
    </body>
</html>

<script language="C#" runat="server">
void Page_Load (Object sender, EventArgs e)
{
    if (IsPostBack)
        DataBind ();
}

void OnSwitchView (Object sender, EventArgs e)
{
    Main.ActiveViewIndex = ViewType.SelectedIndex;
}
</script>

圖5 MultiView.aspx

GridView和DetailsView控件

    DataGrid是ASP.NET中最受歡迎的控件之一,但在某些方面,它也成爲自己成功的犧牲品:如此豐富的功能,以至於讓ASP.NET開發人員不滿足於此,而是希望它能提供更多功能。DataGrid控件在ASP.NET 2.0中並沒有發生太大變化,只是添加了兩個分別名爲GridView和DetailsView的新控件,它們提供了通常要求DataGrid控件所具有的功能,並且還加入了一些屬於它們自己的新功能。

    GridView呈現HTML表的方式與DataGrid一樣,但與DataGrid不同的是,GridView可以完全依靠自己來分頁和排序。GridView還支持比DataGrid種類更爲豐富的列類型(在GridView用語中稱爲字段類型),並且它們具有更爲智能的默認呈現行爲,能夠自動呈現Boolean值(例如,通過複選框)。GridView也可以容易地與DetailsView搭配使用,以創建主-從視圖。GridView控件的主要缺陷是:像DataGrid一樣,它通過將信息傳回到服務器來完成它該做的大部分工作。

    圖6 中的頁面結合使用了GridView和DetailsView,以創建Pubs數據庫的Titles表的簡單主-從視圖。SqlDataSource控件爲其他控件提供數據,而綁定到DetailsView控件的SqlDataSource中的SelectParameter使DetailsView能夠顯示GridView中當前選擇的記錄。可以通過單擊GridView的Select按鈕(該按鈕因標記中的AutoGenerateSelectButton="true"屬性而存在)來選擇記錄。

<%@ Page Theme="BasicBlue" %>

<html>
    <body>
        <form runat="server">
            <asp:SqlDataSource ID="Titles1" RunAt="server"
                ConnectionString="server=localhost;database=pubs;Integrated Security=SSPI"
                SelectCommand="select title_id, title, price from titles"
            />
            <asp:SqlDataSource ID="Titles2" RunAt="server"
                ConnectionString="server=localhost;database=pubs;Integrated
                Security=SSPI"
                SelectCommand="select title_id, title, price from titles where
                title_id=@title_id"
            >
                <SelectParameters>
                    <asp:ControlParameter Name="title_id"
                        ControlID="MyGridView"
                        PropertyName="SelectedValue"
                    />
                </SelectParameters>
            </asp:SqlDataSource>
            <table><tr><td>
                <asp:GridView ID="MyGridView" DataSourceID="Titles1"
                    Width="100%" RunAt="server" AutoGenerateColumns="false"
                    SelectedIndex="0" AutoGenerateSelectButton="true"
                    DataKeyNames="title_id"
                >
                    <Columns>
                        <asp:BoundField HeaderText="Title ID"
                            DataField="title_id"
                        />
                        <asp:BoundField HeaderText="Book Title" DataField="title" />
                        <asp:BoundField HeaderText="Price" DataField="price"
                            DataFormatString="{0:c}" NullDisplayText="TBD"
                        />
                    </Columns>
                </asp:GridView>
            </td></tr>
            <tr><td>
                <asp:DetailsView DataSourceID="Titles2" RunAt="server"
                    AutoGenerateRows="false" Width="100%"
                >
                    <Fields>
                        <asp:BoundField HeaderText="Title ID"
                            DataField="title_id"
                        />
                        <asp:BoundField HeaderText="Book Title"
                            DataField="title"
                        />
                        <asp:BoundField HeaderText="Price" DataField="price"
                            DataFormatString="{0:c}" NullDisplayText="TBD"
                        />
                    </Fields>
                </asp:DetailsView>
            </td></tr></table>
        </form>
    </body>
</html>

圖6 MasterDetail.aspx

    請注意GridView和DetailsView控件中用於定義字段類型的和元素。這些元素實際上等效於DataGrid控件中的元素。圖7 列出了受支持的字段類型。特別重要的是ImageField和DropDownListField,它們都可以有效地削減目前開發人員爲在DataGrid中包含圖像和數據綁定下拉列表而編寫的大部分代碼。

字段類型 描述
AutoGeneratedField 默認字段類型
BoundField 綁定到數據源指定列
ButtonField 顯示一個按鈕、圖片按鈕或者鏈接按鈕
CheckBoxField 顯示一個複選框
CommandField 顯示一個用於選擇或者編輯的按鈕
DropDownListField 顯示一個下拉列表
HyperLinkField 顯示一個超級鏈接
ImageField 顯示一個圖片
TemplateField 內容由HTML模板來定義

圖7 GridView and DetailsView字段類型

新增的管理功能

    ASP.NET 1.x的另一個明顯的缺陷(已經在ASP.NET 2.0中得到修復)是根本沒有用於管理Web站點的接口(無論是聲明性接口還是編程接口)。在過去,更改配置設置意味着啓動記事本並編輯Machine.config或Web.config,但現在不再需要這麼做了。ASP.NET 2.0具有一個完善的管理API,它簡化了讀取和寫入配置設置的任務。它還包括一個管理GUI,您可以通過在瀏覽器中請求Webadmin.axd來顯示該GUI,如圖8所示。


圖8 管理GUI

    儘管在撰寫本文時尚不完善,但Webadmin.axd被設計爲使您可以配置ASP.NET 2.0中包含的各種服務(如成員身份和角色管理服務)、查看Web站點統計信息以及應用安全設置。

成員身份服務

    ASP.NET 2.0中新增的最佳功能之一是新的成員身份服務,它提供了用於創建和管理用戶帳戶的易於使用的API。ASP.NET 1.x大規模引入了窗體身份驗證,但仍然要求您編寫相當數量的代碼來執行實際操作中的窗體身份驗證。成員身份服務填補了ASP.NET 1.x窗體身份驗證服務的不足,並且使實現窗體身份驗證變得比以前簡單得多。

    成員身份API通過兩個新的類公開:Membership和MembershipUser。前者包含了用於創建用戶、驗證用戶以及完成其他工作的靜態方法。MembershipUser代表單個用戶,它包含了用於檢索和更改密碼、獲取上次登錄日期以及完成類似工作的方法和屬性。例如,下面的語句採用用戶名和密碼作爲參數,並返回true或false來指示它們是否有效。它取代了對ASP.NET 1.x應用程序中、使用Active Directory?或後端數據庫來驗證憑據的簡易方法的調用,如下所示:

bool isValid = Membership.ValidateUser (username, password);

    下面的語句返回一個MembershipUser對象,該對象表示用戶名爲“jeffpro”的用戶:

MembershipUser user = Membership.GetUser ("jeffpro");

    以下語句檢索一個已註冊用戶的電子郵件地址(假設記錄了電子郵件地址):

string email = user.Email;

    成員身份服務所管理的用戶名、密碼和其他數據存儲在哪裏?像ASP.NET 2.0中的幾乎所有狀態管理服務一樣,成員身份是基於提供程序的。提供程序是使服務可以與物理數據源進行交互的模塊。ASP.NET 2.0將包含Microsoft Access數據庫、SQL Server數據庫和Active Directory的成員身份提供程序,並且還可能包含其他數據存儲的成員身份提供程序。

    默認情況下,成員身份服務使用Access提供程序,並將成員身份數據存儲在應用程序的Data子目錄中名爲AspNetDB.mdb的文件中。可以通過Web.config的部分選擇備用提供程序。可以讓Webadmin.axd修改Web.config,而不必自己進行修改。下面的內容節選自Web.config(在Webadmin.axd創建了一個名爲WhidbeyLogin的SQL Server?數據庫,並將成員身份服務配置爲使用該數據庫之後):

<membership defaultProvider="WhidbeyLogin">
    <providers>
        <add name="WhidbeyLogin"
            type="System.Web.Security.SqlMembershipProvider, ..."
            connectionStringName="webAdminConnection632112624221191376"
            applicationName="/Whidbey" requiresUniqueEmail="false"
            enablePasswordRetrieval="true" enablePasswordReset="false"
            requiresQuestionAndAnswer="false"
            passwordFormat="Encrypted"
        />
    </providers>
</membership>

    connectionStringName屬性引用了Web.config的新部分中的連接字符串。ASP.NET 2.0將包含加密這部分的Web.config以保護數據庫連接字符串的能力。

    Webadmin.axd的用途並不僅限於創建數據庫和選擇成員身份提供程序:它還可以用於創建用戶、管理憑據以及其他用途。在Webadmin.axd和成員身份API之間,還存在用於管理站點註冊用戶的聲明性手段和編程手段。這是從ASP.NET 1.x向前邁出的一大步,它在很大程度上將憑據管理的問題留給用戶自己去處理。

登錄控件

    成員身份服務本身就顯著減少了驗證登錄和管理用戶所需的代碼量,此外還有一系列稱爲登錄控件的新控件使窗體身份驗證變得更加容易。登錄控件可以與成員身份服務配合使用,也可以不與其配合使用,但它們與該服務之間的集成性非常好,以至於當登錄控件與成員身份服務一起使用時,一些基本任務(例如,驗證用戶名和密碼以及用電子郵件發送遺忘的密碼)通常不必編寫任何代碼就可以完成。“新增控件”提要欄包含了計劃隨ASP.NET 2.0一起提供的一系列登錄控件。


圖9 Login控件

    圖9中所示的Login控件是登錄控件系列的核心控件。除了提供具有高度可自定義性的UI以外,它還能夠調用Membership.ValidateUser來驗證用戶名和密碼。Login控件還可以調用FormsAuthentication.RedirectFromLoginPage,將用戶重定向到他們在被重定向到登錄頁時嘗試到達的頁面。然後,FormsAuthentication.RedirectFromLoginPage將發出身份驗證Cookie。在本文稍後的部分中,您將看到Login和其他登錄控件的工作方式。

角色管理器

    如果不支持基於角色的安全性,那麼成員身份服務和登錄控件將是不完善的。在ASP.NET 1.x中,要將窗體身份驗證與角色結合起來,需要編寫代碼以將角色信息映射到各個傳入的請求。ASP.NET 2.0中新的角色管理器(它可以與成員身份服務配合使用,也可以不與其配合使用)取消了對此類代碼的需求,並且簡化了基於角色授予用戶訪問各種資源權限的任務。

    角色管理是基於提供程序的,它通過Web.config啓用。角色管理器通過新的Roles類來公開API,該類公開了名爲CreateRole、DeleteRole和AddUserToRole等的方法。值得注意的是,您或許永遠不需要調用這些方法,因爲Webadmin.axd完全能夠創建角色、將用戶分配給角色以及完成其他任務。一旦啓用,基於角色的安全性就能夠使用所提供的角色信息以及Web.config文件中的URL身份驗證指令來工作—這與ASP.NET 1.x中您已經熟悉的URL身份驗證相同。

    既然您已經熟悉了成員身份服務、登錄控件以及ASP.NET角色管理器,那麼您或許希望看到同時使用這三者的示例。本文可下載的代碼示例包含一個兩頁的應用程序,它演示了Visual Studio 2005樣式的窗體身份驗證。要部署該應用程序並對其進行測試,請首先將PublicPage.aspx、LoginPage.aspx和Web.config複製到Web服務器的某個虛擬目錄中。在該虛擬目錄中創建一個名爲Secure的子目錄,然後將ProtectedPage.aspx和其他Web.config文件複製到該子目錄中。

    啓動Webadmin.axd,並且將站點配置爲使用窗體身份驗證,將成員身份和角色服務配置爲使用您選擇的提供程序。同時,還要創建名爲Bob和Alice的用戶以及名爲Manager和Developer的角色。將Bob指定爲Manager角色,將Alice指定爲Developer角色。(我不會列出所有步驟,因爲在您使用任一方法閱讀本文之前,它們很可能會改變。幸運的是,Webadmin.axd相當直觀,並且它具有嚮導,可以引導您完成設置過程。)

    接下來,在瀏覽器中激活PublicPage.aspx,並單擊“View Secret Message”按鈕以查看ProtectedPage.aspx。ASP.NET會將您重定向到LoginPage.aspx,該頁面使用Login控件來請求用戶名和密碼。使用Bob的用戶名和密碼登錄。ProtectedPage.aspx應該顯示在瀏覽器窗口中,因爲Bob是Manager,並且可以通過Secure目錄中的Web.config文件將訪問權授予經理。請注意LoginName控件顯示的用戶名以及LoginStatus控件顯示的“Log out”鏈接。最後,關閉瀏覽器,然後重新打開它並重新激活PublicPage.aspx。單擊“View Secret Message”並以Alice的身份登錄。這一次,您將無法到達ProtectedPage.aspx,因爲Alice不是經理。

    我使用了一個類似的應用程序來講述ASP.NET 1.x中的窗體身份驗證,但1.x版本要求編寫非常多的代碼。2.0版本因其簡潔而不同尋常,尤其是沒有任何用於驗證在登錄窗體中鍵入的憑據、或者將用戶名映射到角色的代碼。如果您仍然不相信,請嘗試用ASP.NET 1.x實現同一應用程序!此外,請確保檢驗Webadmin.axd對Web.config所作的更改。除了其他內容以外,您還應該看到一個啓用角色Manager並且可能會指定角色管理提供程序的元素。

    您可能很想知道角色Manager是否經過了到數據庫(在其中,角色被存儲到每個請求中)的往返行程。值得慶幸的是,答案是“否”。它將角色編碼到Cookie中,並且爲了保密而將它們加密。如果一個用戶對應了衆多的角色(這種情況不太可能發生),以至於無法將這些角色編碼到一個Cookie中,則該Cookie將包含一個最近使用角色的列表,並且僅在迫不得已時才查詢數據庫。

個性化

    另一個新增的服務是個性化,它提供了一種現成的解決方案,用於解決存儲站點用戶的個性化設置問題。目前,此類設置通常存儲在Cookie、後端數據庫或這兩者中。無論這些設置存儲在何處,ASP.NET 1.x都不能提供什麼幫助。這需要由您來設置和管理後端數據存儲,以及使用經過身份驗證的用戶名、Cookie或其他某種機制來關聯個性化數據。

    ASP.NET 2.0個性化服務使得存儲各個用戶的設置以及隨意檢索這些設置變得非常容易。該服務基於用戶配置文件—您可以使用新的元素在Web.config中予以定義。下面的代碼節選自Web.config:

<profile>
    <properties>
        <add name="Theme" />
        <add name="Birthday" Type="System.DateTime" />
        <add name="LoginCount" Type="System.Int32" defaultValue="0" />
    </properties>
</profile>

    它定義了一個包含三個屬性的配置文件:一個名爲Theme的字符串,一個名爲Birthday的DateTime值,以及一個名爲LoginCount的整數。後面這個屬性被賦予默認值0。

    在運行時,您可以使用頁面的Profile屬性(該屬性引用包含該配置文件中定義的屬性的動態編譯類的實例)來訪問當前用戶的這些屬性。例如,下列語句可從當前用戶的配置文件中讀取屬性值:

string theme = Profile.Theme;
DateTime birthday = Profile.Birthday;
int logins = Profile.LoginCount;

    還可以將值賦予配置文件屬性:

Profile.Theme = "SmokeAndGlass";
Profile.Birthday = new DateTime (1959, 9, 30);
Profile.LoginCount = Profile.LoginCount + 1;

    個性化服務的一個明顯優勢是強類型化。另一個優勢在於個性化數據是按需讀寫的。請將此與會話狀態(無論是否使用,都會將其加載並保存到每個請求中)進行對比。但是,個性化服務的最大優勢可能在於您不必顯式地將數據存儲在任何位置;系統會替您完成該工作,並且它會永久性地存儲數據,以便數據在您需要時隨時可用。配置文件不會像會話那樣超時。

    那麼,個性化數據存儲在哪裏呢?這要依具體情況而定。個性化服務基於提供程序,因此您可以將其配置爲使用任何可用的提供程序。ASP.NET 2.0將至少附帶兩個個性化提供程序:一個用於Access,另一個用於SQL Server。如果您不另行指定,則個性化服務將使用Access提供程序,默認情況下,該提供程序會將個性化數據存儲在本地Data/AspNetDB.mdb中。您可以通過修改Web.config(手動或使用Webadmin.axd)來改用SQL Server數據庫。如果您不希望將個性化數據存儲在Access數據庫或SQL Server數據庫中,則可以編寫自己的提供程序。

    默認情況下,ASP.NET使用經過身份驗證的用戶名作爲所存儲的個性化數據的鍵,但您也可以將其配置爲支持匿名用戶。首先,通過將以下語句添加到Web.config中來啓用匿名標識:

<anonymousIdentification enabled="true" />

    然後,將allowAnonymous="true"添加到您要爲匿名用戶存儲的配置文件屬性中:

<name="Theme" allowAnonymous="true" />

    現在,Theme屬性可以作爲個性化設置使用,而無論站點的調用方是否經過了身份驗證。

    默認情況下,匿名標識使用Cookie來標識回返用戶。由支持的屬性可以用各種方式來配置這些Cookie。例如,您可以指定Cookie名稱,並指明是否應該將該Cookie的內容加密。您還可以將個性化服務配置爲使用無Cookie的匿名標識,因此它將依靠URL Munging來標識回返用戶。甚至還存在一個自動檢測選項:如果請求瀏覽器支持Cookie,則使用Cookie;如果不支持,則使用URL Munging。

    要查看個性化的工作方式,請運行本文隨附的下載資料中的Personalize.aspx示例。它會讓站點的每個訪問者選擇一個主題,然後記錄該主題,並且每當該訪問者返回時都將應用該主題。請注意,該主題是在頁面的PreInit事件(它是一個新事件,它的激發時間甚至早於Init)中以編程方式應用於該頁面的。

    在您運行該示例之前,需要啓用匿名標識,並定義一個包含名爲Theme的字符串屬性的配置文件。以下代碼行顯示了執行上述兩項任務的Web.config文件:

<configuration>
    <system.web>
        <anonymousIdentification enabled="true" />
        <profile>
            <properties>
                <property name="Theme" allowAnonymous="true" />
            </properties>
        </profile>
    </system.web>
</configuration>

SQL緩存依賴性

    ASP.NET 1.x中令人遺憾地缺少的另一項功能是數據庫緩存依賴性。可以將ASP.NET應用程序緩存中放置的項目與其他緩存項目聯繫起來,或者與文件系統中的對象聯繫起來,但不能與數據庫實體聯繫起來。ASP.NET 2.0通過引入SQL緩存依賴性來糾正這一由於疏忽而造成的錯誤。

    SQL緩存依賴性由新的SQLCacheDependency類的實例表示。它們的用法非常簡單。下面的語句將一個名爲ds的數據集插入到應用程序緩存中,並且在該數據集和Northwind數據庫的Products表之間創建依賴性:

Cache.Insert ("ProductsDataSet", ds,
    new SqlCacheDependency ("Northwind", "Products");

    如果Products表的內容改變,則ASP.NET會自動刪除該數據集。

    SQL緩存依賴性還可以與ASP.NET輸出緩存配合使用。下面的指令指示ASP.NET緩存來自包含頁面的輸出,直至Products表的內容改變或者滿60秒爲止(滿足任一條件即可):

<%@ OutputCache Duration="60" VaryByParam="None"
    SqlDependency="Northwind:Products"
%>

    SQL緩存依賴性適用於SQL Server 7.0、SQL Server 2000和即將問世的SQL Server 2005。對於SQL Server 2005,無需進行任何準備;但必須將SQL Server 7.0和SQL Server 2000數據庫配置爲支持SQL緩存依賴性。準備工作涉及到創建數據庫觸發器,以及創建一個特殊的表,以供ASP.NET在確定是否已經發生更改時參考。該表由一個後臺線程使用可配置的輪詢間隔(默認爲5秒鐘)來定期輪詢。在SQL Server 2005中,要檢測更改,既不需要特殊的表,也不需要輪詢。此外,SQL Server 2005緩存依賴性可以在行級應用,而SQL Server 7.0和SQL Server 2000緩存依賴性在表級工作。您可以使用Aspnet_regsqlcache.exe工具或Webadmin.axd來準備數據庫,以使其支持SQL緩存依賴性。

新的動態編譯模型

    ASP.NET 1.x中引入的衆多創新之一是:系統能夠在首次訪問您的代碼時對其進行編譯。但是,只有頁面能夠被自動編譯,並且輔助類(如數據訪問組件)必須單獨編譯。

    ASP.NET 2.0擴展了動態編譯模型,以便能夠自動編譯幾乎所有的組件。bin目錄仍然保留以便實現向後兼容性,但它現在添加了名爲Code和Resources的目錄。Code目錄中的C#和Visual Basic文件以及Resources目錄中的RESX和RESOURCE文件被ASP.NET自動編譯並緩存在系統子目錄中。此外,落入Code目錄中的Web服務描述語言(WSDL)文件被編譯爲Web服務代理,而XML架構定義語言(XSD)文件被編譯爲類型化數據集。通過Web.config,還可以擴展這些目錄以支持其他文件類型。

預編譯並且在不帶源代碼的情況下進行部署

    提到動態編譯,與ASP.NET 1.x有關的最常見問題之一是:是否可以預編譯頁面,以避免在首次訪問頁面時發生的編譯延遲?儘管該問題本身在某種程度上無關緊要(延遲非常小,並且延遲的開銷被成千上萬甚至數以百萬的後續請求所分攤),但Microsoft仍然感到有必要採取相應的措施來減輕開發人員的擔憂。這一“措施”就是能夠通過提交對名爲precompile.axd的幻像資源的請求,來預編譯應用程序中的所有頁面。

    但預編譯並不僅限於此。另一個經常被請求的功能是:能夠將整個應用程序預編譯爲可以在不帶源代碼的情況下進行部署的託管程序集(該功能在宿主方案中尤其有用)。ASP.NET 2.0包含一個名爲Aspnet_compiler.exe的新的命令行工具,它能夠執行預編譯並且在不帶源代碼的情況下進行部署;Visual Studio 2005將包含類似的功能。下面的命令將預編譯Web1目錄中的應用程序,並且在不帶源代碼的情況下將其部署到Web2:

Aspnet_compiler -v /web1 -p c:/web1 c:/web2

    之後,目標目錄將包含空的ASP.NET文件(ASPX、ASCX、ASIX等等)以及源目錄中存在的所有靜態內容(如HTML文件、.config文件和圖像文件)的副本。在不帶源代碼的情況下進行部署並不會爲您的知識產權提供牢不可破的保護,因爲聰明的ISP仍然可以通過反編譯生成的程序集來弄清楚應用程序的來龍去脈,但是,它確實對一般的代碼竊取者設置了更大的阻礙。

新的代碼分隔模型

    ASP.NET 1.x支持兩種編程模型:內聯模型—HTML和代碼共存於同一個ASPX文件中;代碼隱藏模型—它將HTML分隔到ASPX文件中,並將代碼分隔到源代碼文件(例如,C#文件)中。ASP.NET 2.0引入了第三個模型:一種新的代碼隱藏形式,它依賴於Visual C#和Visual Basic .NET編譯器中的不完全類支持。新的代碼隱藏解決了原來的代碼隱藏中存在的一個惱人的問題:傳統的代碼隱藏類必須包含受保護的字段,這些字段的類型和名稱需要映射到ASPX文件中聲明的相應控件。

    圖10 顯示了新的代碼隱藏模型的工作方式。Hello.aspx包含頁面的聲明部分,Hello.aspx.cs包含代碼。您應該注意@ Page指令中的CompileWith屬性。此外,請注意MyPage類中缺少的字段(它們提供到ASPX文件中聲明的控件的映射)。舊樣式的代碼隱藏仍然受支持,但新樣式將是今後的首選編程模型。一點都不奇怪,Visual Studio 2005天生就支持新的代碼分隔模型。

Hello.aspx

<%@ Page CompileWith="Hello.aspx.cs" ClassName="MyPage" %>

<html>
    <body>
        <form runat="server">
            <asp:TextBox ID="Input" RunAt="server" />
            <asp:Button Text="Test" OnClick="OnTest" RunAt="server" />
            <asp:Label ID="Output" RunAt="server" />
        </form>
    </body>
</html>

Hello.aspx.cs

using System;

partial class MyPage
{
    void OnTest (Object sender, EventArgs e)
    {
        Output.Text = "Hello, " + Input.Text;
    }
}

圖10 Codebehind模型

客戶端回調管理器

    ASP.NET 2.0中我最喜歡的功能之一就是由新的客戶端回調管理器提供的“輕量級回發”功能。在過去,ASP.NET頁面必須回發給服務器才能調用服務器端代碼。回發是低效的,因爲它們將包含由頁面控件生成的所有回發數據。它們還強制頁面刷新,從而導致不雅觀的閃爍。

    ASP.NET 2.0引入了客戶端回調管理器,它使頁面無需完全回發就可以回調到服務器。回調是異步的,並且通過XML-HTTP來完成。它們不包含回發數據,並且不會強制頁面刷新。(在服務器端,頁面像平常一樣執行至PreRender事件,但在即將呈現任何HTML之前停止。)它們確實需要支持XML-HTTP協議的瀏覽器(這通常意味着Microsoft Internet Explorer 5.0或更高版本)。

    使用客戶端回調管理器涉及三個步驟。首先,調用Page.GetCallbackEventReference以獲取對某個特定函數(可以從客戶端腳本中調用該函數,以執行到服務器的XML-HTTP回調)的引用。ASP.NET提供了該函數的名稱和實現。其次,在客戶端腳本中編寫一個將在回調返回時調用的方法。方法名稱是傳遞給GetCallbackEventReference的參數之一。第三,在頁面中實現ICallbackEventHandler接口。該接口包含一個方法—RaiseCallbackEvent,當回調發生時,該方法將在服務器端調用。RaiseCallbackEvent所返回的字符串將被返回到第二步所述的方法。

    圖11 中的代碼顯示了客戶端回調的工作方式,並且演示了它們的一個非常實際的用途。該頁面顯示了一個請求姓名和地址的窗體。在Zip Code字段中鍵入378xx或379xx郵政編碼,然後單擊Autofill按鈕,City字段中將顯示一個名稱。值得注意的是,該頁面會返回到服務器以獲取城市名稱,但它使用客戶端回調而不是完全回發來完成此工作。在實際操作中,它可以找到某個數據庫以將郵政編碼轉換爲城市名稱。請注意,該頁面並不像頁面在回發到服務器時通常所做的那樣進行重新繪製。相反,更新是快速且簡潔的!

<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>

<html>
    <body>
        <h1>Please Register</h1>
        <hr>
        <form runat="server">
            <table>
                <tr>
                    <td>First Name</td>
                    <td><asp:TextBox ID="FirstName" RunAt="server" /></td>
                    <td></td>
                </tr>
                <tr>
                    <td>Last Name</td>
                    <td><asp:TextBox ID="LastName" RunAt="server" /></td>
                    <td></td>
                </tr>
                <tr>
                    <td>Address 1</td>
                    <td><asp:TextBox ID="Address1" RunAt="server" /></td>
                    <td></td>
                </tr>
                <tr>
                    <td>Address 2</td>
                    <td><asp:TextBox ID="Address2" RunAt="server" /></td>
                    <td></td>
                </tr>
                <tr>
                    <td>City</td>
                    <td><asp:TextBox ID="City" RunAt="server" /></td>
                    <td></td>
                </tr>
                <tr>
                    <td>State</td>
                    <td><asp:TextBox ID="State" RunAt="server" /></td>
                    <td></td>
                </tr>
                <tr>
                    <td>Zip Code</td>
                    <td><asp:TextBox ID="Zip" RunAt="server" /></td>
                    <td><asp:Button ID="AutofillButton" Text="Autofill"
                        RunAt="server" /></td>
                </tr>
            </table>
        </form>
    </body>
</html>

<script language="javascript">
// Function called when callback returns
function __onCallbackCompleted (result, context)
{
    // Display the string returned by the server's RaiseCallbackEvent
    // method in the input field named "City"
    document.getElementById ('City').value = result;
}
</script>

<script language="C#" runat="server">
void Page_Load (Object sender, EventArgs e)
{
    // Get callback event reference (e.g., "__doCallback (...)")
    string cbref = GetCallbackEventReference (this,
        "document.getElementById ('Zip').value",
        "__onCallbackCompleted", "null", "null");

    // Wire the callback event reference to the Autofill button with
    // an onclick attribute (and add "return false" to event reference
    // to prevent a postback from occurring)
    AutofillButton.Attributes.Add ("onclick",
        cbref + "; return false;");
}

// Server-side callback event handler
string ICallbackEventHandler.RaiseCallbackEvent (string arg)
{
    if (arg.StartsWith ("378"))
        return "Oak Ridge";
    else if (arg.StartsWith ("379"))
        return "Knoxville";
    else
        return "Unknown";
}
</script>

圖11 callback.aspx

驗證組

    驗證控件是ASP.NET 1.x中更爲卓越的創新。諸如RequiredFieldValidator和RegularExpressionValidator之類的控件使開發人員能夠在客戶端和服務器上進行更爲智能的輸入驗證,而不必成爲客戶端腳本編寫和瀏覽器DOM方面的專家。遺憾的是,版本1.x驗證控件存在一個致命的缺陷,即:沒有一種比較好的方法來將這些控件組合在一起,以便頁面的一個部分上的驗證程序可以重寫該頁面其他部分上的驗證程序,並且無論其他驗證程序的狀態如何,都可以使回發發生。

    該問題由ValidationGroups1.aspx闡明,它包含在您可以針對本文下載的示例中。頁面的設計者預計用戶能夠填寫一組TextBox並回發到服務器,而不必同時填寫另一個組,但它並不按此方式工作。除非所有輸入字段都被填充,否則驗證程序將抱怨不休,如圖12所示。


圖12 ASP.NET 1.x中的驗證控件

    ASP.NET 2.0中的新驗證組功能一勞永逸地解決了該問題。現在,可以使用ValidationGroup屬性來組合驗證控件。可以用相同的方式將按鈕控件分配給組,並且當一個組中的所有驗證程序都對輸入感到滿意時,它們才允許回發發生,當然,前提是回發是由驗證程序同一組中的控件生成的。ValidationGroups2.aspx演示了該技術(參見圖13)。從表面上看,該頁面與ValidationGroups1.aspx完全相同。但在內部,它們卻完全不同。現在,您可以填寫任一組TextBox,並且通過單擊TextBox驗證組中的按鈕進行回發。

<html>
    <body>
        <form runat="server">
            <h1>New Users</h1>
            <table>
                <tr>
                    <td>User Name</td>
                    <td><asp:TextBox ID="NewUserName" RunAt="server" /></td>
                    <td><asp:RequiredFieldValidator ValidationGroup="NewUsers"
                        ControlToValidate="NewUserName" ErrorMessage="Required"
                        RunAt="server" /></td>
                </tr>
                <tr>
                    <td>Password</td>
                    <td><asp:TextBox ID="NewPassword1" TextMode="Password"
                        RunAt="server" /></td>
                    <td><asp:RequiredFieldValidator ValidationGroup="NewUsers"
                        ControlToValidate="NewPassword1" ErrorMessage="Required"
                        RunAt="server" /></td>
                </tr>
                <tr>
                    <td>Retype Password</td>
                    <td><asp:TextBox ID="NewPassword2" TextMode="Password"
                        RunAt="server" /></td>
                    <td><asp:RequiredFieldValidator ValidationGroup="NewUsers"
                        ControlToValidate="NewPassword2" ErrorMessage="Required"
                        RunAt="server" /></td>
                </tr>
                <tr>
                    <td>E-Mail Address</td>
                    <td><asp:TextBox ID="NewEMail" RunAt="server" /></td>
                    <td><asp:RequiredFieldValidator ValidationGroup="NewUsers"
                        ControlToValidate="NewEMail" ErrorMessage="Required"
                        RunAt="server" /></td>
                </tr>
                <tr>
                    <td></td>
                    <td><asp:Button ValidationGroup="NewUsers"
                        Text="Create Account" OnClick="OnCreateAccount"
                        RunAt="server" /></td>
                    <td></td>
                </tr>
            </table>
            <hr>
            <h1>Existing Users</h1>
            <table>
                <tr>
                    <td>User Name</td>
                    <td><asp:TextBox ID="UserName" RunAt="server" /></td>
                    <td><asp:RequiredFieldValidator
                        ValidationGroup="ExistingUsers"
                        ControlToValidate="UserName" ErrorMessage="Required"
                        RunAt="server" /></td>
                </tr>
                <tr>
                    <td>Password</td>
                    <td><asp:TextBox ID="Password" TextMode="Password"
                        RunAt="server" /></td>
                    <td><asp:RequiredFieldValidator
                        ValidationGroup="ExistingUsers"
                        ControlToValidate="Password" ErrorMessage="Required"
                        RunAt="server" /></td>
                </tr>
                <tr>
                    <td></td>
                    <td><asp:Button ValidationGroup="ExistingUsers"
                        Text="Log In" OnClick="OnLogIn" RunAt="server" /></td>
                    <td></td>
                </tr>
            </table>
        </form>
    </body>
</html>

<script language="C#" runat="server">
void OnCreateAccount (Object sender, EventArgs e) {}
void OnLogIn (Object sender, EventArgs e) {}
</script>

圖13 ValidationGroups2.aspx

跨頁面發送

    有關ASP.NET 1.x的最多抱怨是隻允許頁面回發到其本身。在版本2.0中,這一點通過引入跨頁面發送而得到改變。要設置跨頁面發送,您需要使用導致回發發生的控件的PostBackUrl屬性來指定目標頁面,如PageOne.aspx中的以下代碼所示:

<html>
    <body>
        <form runat="server">
            <asp:TextBox ID="Input" RunAt="server" />
            <asp:Button Text="Test" PostBackUrl="PageTwo.aspx"
                RunAt="server"
            />
        </form>
    </body>
</html>

    當被單擊時,PageOne.aspx中的按鈕將回發到PageTwo.aspx:

<html>
    <body>
        <asp:Label ID="Output" RunAt="server" />
    </body>
</html>

<script language="C#" runat="server">
void Page_Load (Object sender, EventArgs e)
{
    TextBox input = (TextBox) PreviousPage.FindControl ("Input");
    Output.Text = "Hello, " + input.Text;
}
</script>

    PageTwo.aspx使用Page類的新PreviousPage屬性來獲取對起始頁面的引用。對FindControl的簡單調用將返回對PageOne.aspx中聲明的TextBox的引用,以便可以檢索用戶的輸入。

    默認情況下,System.Web.UI.Page.PreviousPage返回對引起回發的頁面的弱類型化引用。但是,如果PageOne.aspx是唯一能夠向PageTwo.aspx發送的頁面,則PageTwo.aspx可以使用新的@ PreviousPageType指令來獲取對PageOne.aspx的強類型化訪問,如下面的代碼所示:

<%@ PreviousPageType TypeName="ASP.PageOne.aspx" %>
...

小結

    ASP.NET 2.0還包含其他我尚未討論的新功能。例如,內置的站點計數器服務使您能夠記錄站點使用情況的統計信息,並且在Webadmin.axd中或者在您自己的自定義GUI中查看它們。新的Web組件子系統提供了一個用於構建SharePoint服務器樣式門戶的框架(有關ASP.NET 2.0中的Web部件和門戶的詳細信息,請參閱本期雜誌《利用 ASP.NET 2.0 中的 Web 部件和個性化釋放站點的潛能》,而集成的移動設備支持則意味着,您不再需要安裝單獨的工具包來使輸出適合PDA及其他小型設備。對現有控件進行的無數增強使得這些控件在生成基於組件的Web頁方面比以往任何時候都更加靈活。

    現在正是學習ASP.NET 2.0的最佳時機,因爲要在目前規劃能夠在將來輕鬆升級的體系結構,需要知道哪些功能即將問世(以及哪些功能不會問世)。您的ASP.NET 1.x應用程序無需修改就可以在2.0版本上運行,因爲Microsoft已經允諾新平臺將向後兼容舊平臺。但是,未來將屬於ASP.NET 2.0,而這一未來將意味着更加豐富的功能和更少的代碼。有什麼理由不接受它呢?

    作者介紹:Jeff Prosise是MSDN Magazine的特約編輯,並且是多部書籍的作者,其中包括Programming Microsoft .NET(Microsoft Press,2002)。他還是Wintellect的共同創始人之一,該公司是一家專門研究Microsoft .NET的軟件諮詢和教育公司。

posted on 2005年02月23日 2:02 PM

發佈了7 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章