儲存其他的使用者資訊 (VB)

作者:Scott Mitchell

注意

本文撰寫之後,ASP.NET 成員資格提供者已被 ASP.NET 身分識別取代。 強烈建議您將應用程式更新為使用 ASP.NET 身分識別 平臺,而不是本文撰寫時精選的成員資格提供者。 ASP.NET 身分識別的一些優點優於 ASP.NET 成員資格系統,包括 :

  • 更好的效能
  • 改善擴充性和可測試性
  • 支援 OAuth、OpenID Connect 和雙因素驗證
  • 宣告型身分識別支援
  • 與 ASP.Net Core 的較佳互操作性

下載程式代碼下載 PDF

在本教學課程中,我們將藉由建置非常基本的客體簿應用程式來回答這個問題。 如此一來,我們將探討在資料庫中建立使用者資訊模型化的不同選項,然後瞭解如何將此數據與成員資格架構所建立的使用者帳戶產生關聯。

簡介

Asp。NET 的成員資格架構提供彈性的介面來管理使用者。 成員資格 API 包含驗證認證的方法、擷取目前登入使用者的相關信息、建立新的用戶帳戶,以及刪除使用者帳戶等。 成員資格架構中的每個用戶帳戶只包含驗證認證和執行基本使用者帳戶相關工作所需的屬性。 這是類別的方法和屬性MembershipUser所辨識,此類別會在成員資格架構中建立用戶帳戶的模型。 這個類別具有、、 和 IsLockedOut等屬性UserName,以及 和 UnlockUserGetPassword方法。 Email

通常,應用程式必須儲存成員資格架構中未包含的其他用戶資訊。 例如,在線零售商可能需要讓每位使用者儲存出貨和帳單位址、付款資訊、傳遞喜好設定,以及聯繫人電話號碼。 此外,系統中的每個訂單都會與特定的用戶帳戶相關聯。

類別MembershipUser不包含 或 DeliveryPreferencesPastOrders之類的PhoneNumber屬性。 因此,我們要如何追蹤應用程式所需的使用者資訊,並將其與成員資格架構整合? 在本教學課程中,我們將藉由建置非常基本的客體簿應用程式來回答這個問題。 如此一來,我們將探討在資料庫中建立使用者資訊模型化的不同選項,然後瞭解如何將此數據與成員資格架構所建立的使用者帳戶產生關聯。 現在就開始吧!

步驟 1:建立客體簿應用程式的數據模型

有各種不同的技術可用來擷取資料庫中的用戶資訊,並將它與成員資格架構所建立的使用者帳戶產生關聯。 為了說明這些技術,我們需要增強教學課程 Web 應用程式,以便擷取某種使用者相關數據。 (目前,應用程式的數據模型只包含 .) 所需的 SqlMembershipProvider應用程式服務數據表

讓我們建立非常簡單的來賓簿應用程式,讓已驗證的使用者可以留下批注。 除了儲存來賓簿批注之外,讓我們允許每個用戶儲存他的主市、首頁和簽章。 如果提供,使用者的主市、首頁和簽章會出現在來賓簿中留下的每個訊息上。

GuestbookComments新增數據表

為了擷取客體簿批注,我們需要建立名為的資料庫GuestbookComments數據表,其具有、SubjectBodyCommentDateCommentId數據行。 我們也需要讓數據表中的每個 GuestbookComments 記錄參考離開批注的使用者。

若要將此數據表新增至我們的資料庫,請移至 Visual Studio 中的 [資料庫總管],然後向下切入 SecurityTutorials 資料庫。 以滑鼠右鍵按兩下 [資料表] 資料夾,然後選擇 [新增資料表]。 這會顯示一個介面,可讓我們定義新數據表的數據行。

將新的數據表新增至 SecurityTutorials 資料庫

圖 1:將新的資料表新增至 SecurityTutorials 資料庫 (按兩下即可檢視大小完整的映像)

接下來,定義 GuestbookComments的數據行。 首先,新增名為 的數據行,其類型uniqueidentifierCommentId 。 此數據行會唯一識別客體簿中的每個批註,因此不允許 NULL ,並將它標示為數據表的主鍵。 我們不需要為每個 CommentIdINSERT欄位提供值,而是藉由將數據行的預設值設定為 NEWID(),來指出應該針對這個欄位INSERT自動產生新的uniqueidentifier值。 新增此第一個字段之後,將它標示為主鍵,並設定其預設值,您的畫面看起來應該類似圖 2 所示的螢幕快照。

新增名為 CommentId 的主要數據行

圖 2:新增名為 CommentId (按一下以檢視完整大小的映射)

接下來,在兩個資料列中新增名為 Subject 類型的nvarchar(50)資料行,以及類型為的數據Bodynvarchar(MAX)行,不允許NULL兩個資料行中的 。 接著,新增名為 CommentDate 類型的 datetime數據行。 不允許 NULL ,並將資料 CommentDate 列的預設值設定為 getdate()

所有保留專案都是新增一個數據行,以將用戶帳戶與每個來賓簿批注產生關聯。 其中一個選項是新增名為 UserName 類型的 nvarchar(256)數據行。 使用 以外的 SqlMembershipProvider成員資格提供者時,這是適合的選擇。 但是,使用 SqlMembershipProvider時,如同我們在本教學課程系列中, UserName 數據表中的數據 aspnet_Users 行不保證是唯一的。 資料表 aspnet_Users 的主鍵為 UserId ,且類型 uniqueidentifier為 。 因此, GuestbookComments 數據表需要名為 UserId 的數據 uniqueidentifier 行, (不允許 NULL 值) 。 繼續並新增此數據行。

注意

如在 SQL Server 教學課程中建立成員資格架構中所討論,成員資格架構的設計目的是要讓多個具有不同用戶帳戶的 Web 應用程式共用相同的使用者存放區。 其作法是將用戶帳戶分割成不同的應用程式。 而且,雖然每個使用者名稱保證在應用程式內都是唯一的,但相同的用戶名稱可能會使用相同的使用者存放區在不同的應用程式中使用。 和 ApplicationId 欄位上的UserName數據表中有aspnet_Users複合UNIQUE條件約束,但不只是欄位上的UserName條件約束。 因此,aspnet_Users數據表可能會有兩個具有相同值的 (或更多) 記錄 UserName 。 不過,UNIQUE數據表UserId欄位上的aspnet_Users條件約束 (,因為它是主鍵) 。 條件UNIQUE約束很重要,因為如果沒有條件約束,我們就無法在 和 aspnet_Users 數據表之間建立GuestbookComments外鍵條件約束。

新增數據 UserId 行之後,按兩下工具列中的 [儲存] 圖示,以儲存資料表。 將新資料表 GuestbookComments命名為 。

我們有最後一個問題要與數據表一GuestbookComments起處理:我們需要在數據行與aspnet_Users.UserId數據行之間建立GuestbookComments.UserId外鍵條件約束。 若要達成此目的,請按兩下工具列中的 [關聯性] 圖示,以啟動 [外鍵關聯性] 對話方塊。 (或者,您可以移至 [數據表 Designer] 功能表,然後選擇 [關聯性]。)

按兩下 [外鍵關聯性] 對話框左下角的 [新增] 按鈕。 這將會新增外鍵條件約束,雖然我們仍然需要定義參與關聯性的數據表。

使用外鍵關聯性對話框來管理數據表的外鍵條件約束

圖 3:使用 [外鍵關聯性] 對話框來管理數據表的外鍵條件約束, (按兩下即可檢視大小完整的影像)

接下來,按下右側 [資料表和數據行規格] 資料列中的省略號圖示。 這會啟動 [數據表和數據行] 對話框,我們可以從中指定主鍵數據表和數據行,以及數據表中的 GuestbookComments 外鍵數據行。 特別是,選取 aspnet_UsersUserId 做為主鍵數據表和數據行,以及 UserIdGuestbookComments 數據表選取做為外鍵數據行, (請參閱圖 4) 。 定義主鍵和數據行之後,按兩下 [確定] 傳回 [外鍵關聯性] 對話方塊。

在 aspnet_Users 和 GuesbookComments 數據表之間建立外鍵條件約束

圖 4:在 和 GuesbookComments 數據表之間建立aspnet_Users外鍵條件約束, (按兩下即可檢視大小完整的影像)

此時已建立外鍵條件約束。 此條件約束的存在可確保兩個數據表之間的 關係完整性 ,方法是保證永遠不會有參考不存在用戶帳戶的客體簿專案。 根據預設,如果有對應的子記錄,外鍵條件約束將不允許刪除父記錄。 也就是說,如果使用者提出一或多個來賓簿批注,然後我們嘗試刪除該用戶帳戶,除非先刪除其來賓簿批注,否則刪除將會失敗。

外鍵條件約束可以設定為在刪除父記錄時自動刪除相關聯的子記錄。 換句話說,我們可以設定此外鍵條件約束,以便在刪除使用者的使用者帳戶時自動刪除使用者的來賓簿專案。 若要達成此目的,請展開 [INSERT 和 UPDATE 規格] 區段,並將 [刪除規則] 屬性設定為 Cascade。

將外鍵條件約束設定為串聯刪除

圖 5:將外鍵條件約束設定為串聯刪除 (按鍵即可檢視大小完整的映像)

若要儲存外鍵條件約束,請按兩下 [關閉] 按鈕結束外鍵關聯性。 然後按下工具列中的 [儲存] 圖示,以儲存數據表和此關聯性。

儲存使用者的首頁、首頁和簽章

GuestbookComments下表說明如何儲存與用戶帳戶共用一對多關聯性的資訊。 由於每個用戶帳戶可能有任意數目的相關聯批注,因此建立數據表來保存一組批注,其中包含一個數據行,可將每個批注連結回特定用戶的數據行,來建立此關聯性模型。 使用 SqlMembershipProvider時,最好建立名為 UserId 類型的 uniqueidentifier 數據行,以及此數據行與 aspnet_Users.UserId之間的外鍵條件約束來建立此連結。

我們現在需要建立三個數據行與每個用戶帳戶的關聯,以儲存使用者的主市、首頁和簽章,其會出現在來賓簿批注中。 有幾種不同的方式可以完成這項作業:

  • 將新數據行新增至aspnet_Usersaspnet_Membership表。 我不建議使用此方法,因為它會修改 所使用的 SqlMembershipProvider架構。 此決策可能會回到您的下路。 例如,如果未來的版本 ASP.NET 使用不同的 SqlMembershipProvider 架構,該怎麼辦。 Microsoft 可能包含將 ASP.NET 2.0 SqlMembershipProvider 資料遷移至新架構的工具,但如果您已修改 ASP.NET 2.0 SqlMembershipProvider 架構,則可能無法進行這類轉換。

  • 使用 ASP。NET 的設定檔架構,定義主市、首頁和簽章的配置檔屬性。 ASP.NET 包含配置文件架構,其設計目的是要儲存額外的使用者特定數據。 如同成員資格架構,配置文件架構是建置在提供者模型之上。 .NET Framework 隨附的 SqlProfileProvider ,會將配置檔數據儲存在 SQL Server 資料庫中。 事實上,我們的資料庫已經有 (aspnet_Profile) 所使用的SqlProfileProvider數據表,因為我們在 SQL Server 教學課程中將應用程式服務新增回建立成員資格架構時新增。
    配置文件架構的主要優點是,它可讓開發人員定義 中的 Web.config 配置檔屬性,而不需要撰寫程式代碼,即可將配置檔數據串行化至基礎數據存放區或從基礎數據存放區串行化。 簡單地說,定義一組配置檔屬性,並在程式代碼中使用它們非常容易。 不過,配置檔系統在進行版本設定時會留下許多所需專案,因此,如果您有預期稍後要新增新使用者特定屬性的應用程式,或移除或修改現有的屬性,則 Profile 架構可能不是最佳選項。 此外,會 SqlProfileProvider 以高度反正規化的方式儲存配置檔屬性,使得不可能直接對配置檔數據執行查詢 (,例如,有多少使用者擁有紐約) 的家市。
    如需配置文件架構的詳細資訊,請參閱本教學課程結尾的一節。

  • 將這三個數據行新增至資料庫中的新數據表,並建立此數據表與此數據表之間的一對一關聯性aspnet_Users 這種方法牽涉到比使用 Profile 架構還要多一些,但提供在資料庫中如何建立其他使用者屬性模型的最大彈性。 這是我們將在本教學課程中使用的選項。

我們將建立名為 UserProfiles 的新數據表,以儲存每個使用者的住家城市、首頁和簽章。 以滑鼠右鍵按兩下 [資料庫總管] 視窗中的 [資料表] 資料夾,然後選擇建立新的資料表。 將第一個資料列 UserId 命名為 ,並將其類型設定為 uniqueidentifier。 不允許 NULL 值,並將數據行標示為主鍵。 接下來,新增名為: HomeTown 類型的 nvarchar(50)數據行; HomepageUrl 類型 nvarchar(100)為 ,而 類型 nvarchar(500)為 的簽章。 這三個數據行中的每一個 NULL 都可以接受值。

建立 UserProfiles 數據表

圖 6:建立 UserProfiles 數據表 (按鍵即可檢視大小完整的映射)

儲存資料表並將命名為 UserProfiles。 最後,在數據表欄位UserIdaspnet_Users.UserId欄位之間建立UserProfiles外鍵條件約束。 如同我們在 和 aspnet_Users 數據表之間GuestbookComments使用外鍵條件約束一樣,請刪除此條件約束串聯。 UserId由於中的UserProfiles欄位是主鍵,因此這可確保每個使用者帳戶的UserProfiles數據表中不會有一筆以上的記錄。 這種類型的關聯性稱為一對一。

既然我們已經建立數據模型,我們就可以使用它。 在步驟 2 和 3 中,我們將探討目前登入的使用者如何檢視和編輯其主鎮、首頁和簽章資訊。 在步驟 4 中,我們將建立已驗證使用者的介面,以將新的批註提交至來賓簿,並檢視現有的批注。

步驟 2:顯示使用者的首頁、首頁和簽章

有各種不同的方式可讓目前登入的用戶檢視和編輯他的主市、首頁和簽章資訊。 我們可以使用 TextBox 和 Label 控制項手動建立使用者介面,或者可以使用其中一個數據 Web 控制項,例如 DetailsView 控件。 若要執行資料庫SELECTUPDATE語句,我們可以在頁面的程式代碼後置類別中撰寫 ADO.NET 程序代碼,或者,使用 SqlDataSource 的宣告式方法。 在理想情況下,我們的應用程式會包含階層式架構,我們可以透過頁面的程式代碼後置類別以程式設計方式叫用,或透過 ObjectDataSource 控件以宣告方式叫用。

由於本教學課程系列著重於窗體驗證、授權、用戶帳戶和角色,因此不會徹底討論這些不同的數據存取選項,或為何偏好階層式架構,而不是直接從 ASP.NET 頁面執行 SQL 語句。 我將會逐步解說使用 DetailsView 和 SqlDataSource – 最快速且最簡單的選項,但討論的概念當然可以套用至替代的 Web 控件和數據存取邏輯。 如需在 ASP.NET 中使用數據的詳細資訊,請參閱在 ASP.NET 2.0 教學課程系列中使用數據

AdditionalUserInfo.aspx開啟資料夾中的頁面Membership,並將DetailsView控件新增至頁面,並將其ID屬性設定為 UserProfile ,並清除其 WidthHeight 屬性。 展開 DetailsView 的智慧標記,然後選擇將其系結至新的數據源控件。 這會啟動 DataSource 設定精靈 (請參閱圖 7) 。 第一個步驟會要求您指定數據源類型。 因為我們要直接連線到 SecurityTutorials 資料庫,請選擇 [資料庫] 圖示,並將 ID 指定為 UserProfileDataSource

新增名為 UserProfileDataSource 的新 SqlDataSource 控制件

圖 7:新增名為 UserProfileDataSource (的 SqlDataSource 控件 ,以檢視大小完整的映射)

下一個畫面會提示資料庫使用。 我們已在 中Web.configSecurityTutorials資料庫定義 連接字串。 此 連接字串 名稱 – SecurityTutorialsConnectionString 應該位於下拉式清單中。 選取此選項,然後按 [下一步]。

從 Drop-Down 清單中選擇 SecurityTutorialsConnectionString

圖 8:從 [Drop-Down 列表 (按兩下SecurityTutorialsConnectionString以檢視大小完整的影像)

後續畫面會要求我們指定要查詢的數據表和數據行。 UserProfiles從下拉式清單中選擇資料表,並檢查所有數據行。

從 UserProfiles 數據表中恢復所有數據行

圖 9:從 UserProfiles 數據表中帶回所有數據行 (按兩下即可檢視大小完整的影像)

圖 9 中的目前查詢會傳回 中的所有UserProfiles記錄,但我們只對目前登入用戶的記錄感興趣。 若要新增 WHERE 子句,請按下 WHERE 按鈕以顯示 [新增 WHERE 子句] 對話框, (請參閱圖 10) 。 您可以在這裡選取要篩選的數據行、運算元,以及篩選參數的來源。 選取 UserId 作為數據行,然後選取 「=」 作為運算符。

不幸的是,沒有內建參數來源可傳回目前登入的使用者 UserId 值。 我們必須以程序設計方式擷取此值。 因此,將 [來源] 下拉式清單設定為 [無],按兩下 [新增] 按鈕以新增 參數,然後按兩下 [確定]。

在 UserId 資料行上新增篩選參數

圖 10:在數據行上 UserId 新增篩選參數 (按兩下即可檢視大小完整的影像)

按兩下 [確定] 之後,您會返回圖 9 所示的畫面。 不過,這次,畫面底部的 SQL 查詢應該包含 WHERE 子句。 按 [下一步] 以移至 [測試查詢] 畫面。 您可以在這裏執行查詢並查看結果。 按一下 [完成] 以完成精靈。

完成 DataSource 設定精靈時,Visual Studio 會根據精靈中指定的設定建立 SqlDataSource 控制件。 此外,它會針對 SqlDataSource SelectCommand所傳回的每個數據行,手動將 BoundFields 新增至 DetailsView。 不需要在 DetailsView 中顯示 UserId 欄位,因為使用者不需要知道此值。 您可以直接從 DetailsView 控制件的宣告式標記中移除此欄位,或按兩下其智慧標記中的 [編輯字段] 連結。

此時,頁面的宣告式標記看起來應該類似下列內容:

<asp:DetailsView ID="UserProfile" runat="server"
          AutoGenerateRows="False" DataKeyNames="UserId"

          DataSourceID="UserProfileDataSource">
     <Fields>
          <asp:BoundField DataField="HomeTown" HeaderText="HomeTown"
               SortExpression="HomeTown" />
          <asp:BoundField DataField="HomepageUrl" HeaderText="HomepageUrl"

               SortExpression="HomepageUrl" />
          <asp:BoundField DataField="Signature" HeaderText="Signature"
               SortExpression="Signature" />
     </Fields>

</asp:DetailsView>
<asp:SqlDataSource ID="UserProfileDataSource" runat="server"
          ConnectionString="<%$ ConnectionStrings:SecurityTutorialsConnectionString %>"
          SelectCommand="SELECT [UserId], [HomeTown], [HomepageUrl], [Signature] FROM
          [UserProfiles] WHERE ([UserId] = @UserId)">
     <SelectParameters>

          <asp:Parameter Name="UserId" Type="Object" />
     </SelectParameters>
</asp:SqlDataSource>

我們需要以程式設計方式將 SqlDataSource 控件的參數 UserId 設定為目前登入使用者的 UserId 參數,然後再選取數據。 這可以藉由建立 SqlDataSource 事件的 Selecting 事件處理程式,並在該處新增下列程式代碼來完成:

Protected Sub UserProfileDataSource_Selecting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.SqlDataSourceSelectingEventArgs) Handles UserProfileDataSource.Selecting
     ' Get a reference to the currently logged on user
     Dim currentUser As MembershipUser = Membership.GetUser()

     ' Determine the currently logged on user's UserId value
     Dim currentUserId As Guid = CType(currentUser.ProviderUserKey, Guid)

     ' Assign the currently logged on user's UserId to the @UserId parameter
     e.Command.Parameters("@UserId").Value = currentUserId
End Sub

上述程式代碼會藉由呼叫 Membership 類別 GetUser 的 方法,取得目前登入使用者的參考開始。 這會傳 MembershipUser 回 物件,其 ProviderUserKey 屬性包含 UserId。 然後,此值 UserId 會指派給 SqlDataSource 的參數 @UserId

注意

方法 Membership.GetUser() 會傳回目前登入使用者的相關信息。 如果匿名使用者正在瀏覽頁面,則會傳回 的值 Nothing。 在這種情況下,這會在嘗試讀取 ProviderUserKey 屬性時,NullReferenceException在下列程式代碼行上導致 。 當然,我們不需要擔心 Membership.GetUser() 在頁面中傳回 Nothing,因為我們已在上一個教學課程中 AdditionalUserInfo.aspx 設定 URL 授權,因此只有已驗證的使用者才能存取此資料夾中的 ASP.NET 資源。 如果您需要在允許匿名存取的頁面存取目前登入使用者的相關信息,請務必在參考其屬性之前,先檢查 MembershipUserGetUser() 方法傳回的物件不是 Nothing。

如果您透過瀏覽器瀏覽 AdditionalUserInfo.aspx 頁面,您會看到空白頁面,因為我們尚未將任何資料列新增至 UserProfiles 資料表。 在步驟 6 中,我們將探討如何自定義 CreateUserWizard 控件,以在建立新的使用者帳戶時自動將新數據列新增至 UserProfiles 數據表。 不過,我們現在必須手動在數據表中建立記錄。

流覽至 Visual Studio 中的 [資料庫總管],然後展開 [數據表] 資料夾。 以滑鼠右鍵按鍵表, aspnet_Users 然後選擇 [顯示資料表數據] 以查看資料表中的記錄;對 UserProfiles 數據表執行相同的動作。 圖 11 顯示垂直並排時的結果。 我的資料庫中目前 aspnet_Users 有 Bruce、Fred 和 Tito 的記錄,但數據表中 UserProfiles 沒有記錄。

會顯示aspnet_Users和UserProfiles數據表的內容

圖 11:和 UserProfiles 資料表的內容aspnet_Users會顯示 (按兩下即可檢視大小完整的影像)

手動輸入、 HomepageUrlSignature 欄位的值HomeTown,以將新記錄新增至UserProfiles數據表。 取得新UserProfiles記錄中有效UserId值最簡單的方式,是從數據表中的aspnet_Users特定用戶帳戶選取UserId欄位,然後複製並貼到 UserId 中的UserProfiles欄位中。 圖 12 顯示 UserProfiles 針對 Bruce 新增記錄之後的數據表。

已將記錄新增至 Bruce 的 UserProfiles

圖 12:已針對 Bruce 新增 UserProfiles 記錄 (按兩下以檢視大小完整的影像)

AdditionalUserInfo.aspx page返回 ,以 Bruce 身分登入。 如圖 13 所示,會顯示暴力密碼的設定。

目前流覽的用戶會顯示其設定

圖 13:目前瀏覽的用戶會顯示其設定 (按兩下即可檢視大小完整的影像)

注意

請繼續並手動為每個成員資格使用者新增數據表中的 UserProfiles 記錄。 在步驟 6 中,我們將探討如何自定義 CreateUserWizard 控件,以在建立新的使用者帳戶時自動將新數據列新增至 UserProfiles 數據表。

步驟 3:允許使用者編輯主鎮、首頁和簽章

此時,目前登入的使用者可以檢視其主鎮、首頁和簽章設定,但還無法修改。 讓我們更新DetailsView控件,以便編輯數據。

我們需要做的第一件事是為 SqlDataSource 新增 , UpdateCommandUPDATE 指定要執行的語句及其對應的參數。 選取 SqlDataSource,然後從 屬性視窗 按兩下 UpdateQuery 屬性旁的省略號,以顯示 [命令和參數 編輯器] 對話框。 在文字框中輸入下列 UPDATE 語句:

UPDATE UserProfiles SET
     HomeTown = @HomeTown,
     HomepageUrl = @HomepageUrl,
     Signature = @Signature
WHERE UserId = @UserId

接下來,按兩下 [重新整理參數] 按鈕,這會針對語句中的每個參數,在 SqlDataSource 控制件的 UpdateParameters 集合中 UPDATE 建立參數。 將所有參數的來源保留為 [無],然後按兩下 [確定] 按鈕以完成對話方塊。

指定 SqlDataSource 的 UpdateCommand 和 UpdateParameters

圖 14:指定 SqlDataSource 的 UpdateCommandUpdateParameters (按兩下即可檢視大小完整的映像)

由於我們對 SqlDataSource 控制項所做的新增,DetailsView 控制項現在可以支援編輯。 從 DetailsView 的智慧標記中,核取 [啟用編輯] 複選框。 這會將 CommandField 新增至控件的集合, Fields 並將其 ShowEditButton 屬性設定為 True。 當 DetailsView 以唯讀模式顯示,並在編輯模式中顯示時更新和取消按鈕時,這會轉譯 [編輯] 按鈕。 不過,我們不需要使用者按兩下 [編輯],而是可以將DetailsView控件的 DefaultMode 屬性 設定為 Edit,以「永遠可編輯」狀態呈現DetailsView。

透過這些變更,DetailsView 控件的宣告式標記看起來應該類似下列內容:

<asp:DetailsView ID="UserProfile" runat="server"

          AutoGenerateRows="False" DataKeyNames="UserId"
          DataSourceID="UserProfileDataSource" DefaultMode="Edit">
     <Fields>
          <asp:BoundField DataField="HomeTown" HeaderText="HomeTown"

               SortExpression="HomeTown" />
          <asp:BoundField DataField="HomepageUrl" HeaderText="HomepageUrl"
               SortExpression="HomepageUrl" />
          <asp:BoundField DataField="Signature" HeaderText="Signature"

               SortExpression="Signature" />
          <asp:CommandField ShowEditButton="True" />
     </Fields>
</asp:DetailsView>

請注意 CommandField 和 DefaultMode 屬性的新增。

請繼續並透過瀏覽器測試此頁面。 使用 中 UserProfiles具有對應記錄的用戶流覽時,用戶的設定會顯示在可編輯的介面中。

DetailsView 會轉譯可編輯的介面

圖 15:D etailsView 會轉譯可編輯的介面 (按兩下以檢視大小完整的影像)

請嘗試變更值,然後按兩下 [更新] 按鈕。 看起來就像沒有發生任何事一樣。 有回傳,且值會儲存至資料庫,但沒有發生儲存的視覺意見反應。

若要解決此問題,請返回 Visual Studio,並在 DetailsView 上方新增標籤控件。 將 ID 設定為 SettingsUpdatedMessage,其 Text 屬性設定為 [已更新您的設定],並將其 VisibleEnableViewState 屬性設定為 False

<asp:Label ID="SettingsUpdatedMessage" runat="server"
     Text="Your settings have been updated."
     EnableViewState="false"
     Visible="false">
</asp:Label>

每當 DetailsView 更新時,我們需要顯示 SettingsUpdatedMessage 標籤。 若要達成此目的,請建立DetailsView事件的 ItemUpdated 事件處理程式,並新增下列程式代碼:

Protected Sub UserProfile_ItemUpdated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewUpdatedEventArgs) Handles UserProfile.ItemUpdated
     SettingsUpdatedMessage.Visible = True
End Sub

透過瀏覽器返回 AdditionalUserInfo.aspx 頁面,並更新數據。 這次會顯示有用的狀態消息。

更新設定時會顯示簡短訊息

圖 16:[更新設定] (按兩下即可檢視完整大小的影像 時,會顯示簡短訊息)

注意

DetailsView 控件的編輯介面會留下許多需要。 它會使用標準大小的文字框,但 [簽章] 字段應該是多行文本框。 RegularExpressionValidator 應該用來確保輸入的首頁 URL 開頭為 “http://” 或 “https://”。 此外,由於 DetailsView 控制件的 DefaultMode 屬性設定為 Edit,所以 [取消] 按鈕不會執行任何動作。 它應該被移除,或按兩下時,將使用者重新導向至其他頁面 (,例如 ~/Default.aspx) 。 我將這些增強功能保留為讀者的練習。

目前,網站不會提供頁面的任何連結 AdditionalUserInfo.aspx 。 線上的唯一方法是直接在瀏覽器的網址列中輸入頁面的 URL。 讓我們在主版頁面中新增此頁面 Site.master 的連結。

回想一下,主版頁面在其 ContentPlaceHolder 中包含 LoginContent LoginView Web 控件,以顯示已驗證和匿名訪客的不同標記。 更新 LoginView 控件的 LoggedInTemplate ,以包含頁面的連結 AdditionalUserInfo.aspx 。 進行這些變更之後,LoginView 控件的宣告式標記看起來應該如下所示:

<asp:LoginView ID="LoginView1" runat="server">
     <LoggedInTemplate>
          Welcome back,
          <asp:LoginName ID="LoginName1" runat="server" />.

          <br />
          <asp:HyperLink ID="lnkUpdateSettings" runat="server" 
               NavigateUrl="~/Membership/AdditionalUserInfo.aspx">
               Update Your Settings</asp:HyperLink>
     </LoggedInTemplate>
     <AnonymousTemplate>

          Hello, stranger.
     </AnonymousTemplate>
</asp:LoginView>

請注意,將 HyperLink 控制項新增 lnkUpdateSettingsLoggedInTemplate。 透過此連結,已驗證的使用者可以快速跳至頁面,以檢視和修改其主鎮、首頁和簽章設定。

步驟 4:新增來賓簿批注

頁面 Guestbook.aspx 是經過驗證的使用者可以檢視來賓簿並留下批注的位置。 讓我們從建立介面開始,以新增來賓簿批注。

Guestbook.aspx在 Visual Studio 中開啟頁面,並建構包含兩個 TextBox 控制件的使用者介面,一個用於新批注的主旨,另一個用於其本文。 將第一個 TextBox 控制件的 ID 屬性設定為 Subject ,並將其 Columns 屬性設定為 40;將第二BodyID個 的 設定為 ,TextModeMultiLine並將其 WidthRows 屬性分別設定為 “95%” 和 8。 若要完成使用者介面,請新增名為 PostCommentButton 的 Button Web 控件,並將其 Text 屬性設定為 [張貼您的批注]。

由於每個客體批注都需要主旨和本文,因此請為每個 TextBoxes 新增 RequiredFieldValidator。 ValidationGroup將這些控件的 屬性設定為 「EnterComment」,同樣地,請將PostCommentButton控件的 ValidationGroup 屬性設定為 “EnterComment”。 如需 ASP 的詳細資訊。NET 的驗證控件,請參閱 ASP.NET 中的窗體驗證

在製作使用者介面之後,頁面的宣告式標記看起來應該如下所示:

<h3>Leave a Comment</h3>
<p>
     <b>Subject:</b>
     <asp:RequiredFieldValidator ID="SubjectReqValidator" runat="server"

          ErrorMessage="You must provide a value for Subject"
          ControlToValidate="Subject" ValidationGroup="EnterComment">
     </asp:RequiredFieldValidator><br />
     <asp:TextBox ID="Subject" Columns="40" runat="server"></asp:TextBox>

</p>
<p>
     <b>Body:</b>
     <asp:RequiredFieldValidator ID="BodyReqValidator" runat="server"
          ControlToValidate="Body"

          ErrorMessage="You must provide a value for Body" ValidationGroup="EnterComment">
     </asp:RequiredFieldValidator><br />
     <asp:TextBox ID="Body" TextMode="MultiLine" Width="95%"

          Rows="8" runat="server"></asp:TextBox>
</p>
<p>
     <asp:Button ID="PostCommentButton" runat="server" 

          Text="Post Your Comment"
          ValidationGroup="EnterComment" />
</p>

完成使用者介面之後,下一個工作就是在按兩下 時PostCommentButton,將新記錄GuestbookComments插入數據表中。 這可以透過多種方式完成:我們可以在 Button Click 的事件處理程式中撰寫 ADO.NET 程式代碼;我們可以將 SqlDataSource 控件新增至頁面、設定其 ,然後從ClickClick事件處理程式呼叫其 InsertCommandInsert 方法;或者,我們可以建置負責插入新客體批注的仲介層,並從事件處理程式叫用此功能。 因為我們在步驟 3 中查看了使用 SqlDataSource,讓我們在這裡使用 ADO.NET 程序代碼。

注意

用來以程式設計方式存取 Microsoft SQL Server 資料庫的 ADO.NET 類別位於 命名空間中System.Data.SqlClient。 您可能需要將此命名空間匯入頁面的程式代碼後置類別 (也就是 Imports System.Data.SqlClient) 。

建立 PostCommentButton事件的 Click 事件處理程式,並新增下列程序代碼:

Protected Sub PostCommentButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles PostCommentButton.Click
     If Not Page.IsValid Then Exit Sub

     ' Determine the currently logged on user's UserId
     Dim currentUser As MembershipUser = Membership.GetUser()
     Dim currentUserId As Guid = CType(currentUser.ProviderUserKey, Guid)

     ' Insert a new record into GuestbookComments
     Dim connectionString As String = 
          ConfigurationManager.ConnectionStrings("SecurityTutorialsConnectionString").ConnectionString
     Dim insertSql As String = "INSERT INTO GuestbookComments(Subject, Body, UserId)
          VALUES(@Subject, @Body, @UserId)"

     Using myConnection As New SqlConnection(connectionString)

          myConnection.Open()
          Dim myCommand As New SqlCommand(insertSql, myConnection)
          myCommand.Parameters.AddWithValue("@Subject", Subject.Text.Trim())
          myCommand.Parameters.AddWithValue("@Body", Body.Text.Trim())
          myCommand.Parameters.AddWithValue("@UserId", currentUserId)
          myCommand.ExecuteNonQuery()
          myConnection.Close()
     End Using

     ' "Reset" the Subject and Body TextBoxes

     Subject.Text = String.Empty
     Body.Text = String.Empty
End Sub

事件處理程式會 Click 從檢查使用者提供的數據是否有效開始。 如果不是,事件處理程式會在插入記錄之前結束。 假設提供的數據有效,則目前登入的使用者 UserId 值會擷取並儲存在局部變數中 currentUserId 。 需要這個值,因為我們必須在將記錄GuestbookComments插入 時提供UserId值。

接著,會從 Web.config 擷取資料庫的 連接字串SecurityTutorialsINSERT並指定 SQL 語句。 SqlConnection然後建立並開啟物件。 接下來, SqlCommand 會建構 物件,並指派查詢中使用的 INSERT 參數值。 接著會 INSERT 執行語句,並關閉連接。 在事件處理程序結束時,會清除和 Body TextBoxes Text 的屬性,Subject讓使用者的值不會在回傳中保存。

請繼續在瀏覽器中測試此頁面。 由於此頁面位於 Membership 資料夾中,因此匿名訪客無法存取。 因此,如果您尚未) ,您必須先登入 (。 在和 Body TextBox 中輸入值Subject,然後按鍵PostCommentButton。 這會導致將新記錄新增至 GuestbookComments。 在回傳時,您提供的主體和本文會從 TextBoxes 抹除。

按兩下 PostCommentButton 按鈕之後,沒有將批註新增至來賓簿的視覺意見反應。 我們仍然需要更新此頁面,以顯示我們在步驟 5 中將執行的現有來賓簿批注。 完成此動作之後,剛新增的批註會出現在批注清單中,並提供適當的視覺意見反應。 現在,檢查數據表的內容 GuestbookComments ,確認您的來賓簿批註已儲存。

圖 17 顯示兩個批注離開之後數據表 GuestbookComments 的內容。

您可以在 GuestbookComments 數據表中看到客體簿批注

圖 17:您可以在 [數據表] GuestbookComments (按兩下即可檢視大小完整的影像)

注意

如果使用者嘗試插入包含潛在危險標記的客體簿批註,例如 HTML , ASP.NET 會擲回 HttpRequestValidationException。 若要深入瞭解此例外狀況、擲回的原因,以及如何允許使用者提交潛在危險值,請參閱 要求驗證白皮書

步驟 5:列出現有的客體簿批注

除了留下批注之外,流覽 Guestbook.aspx 頁面的用戶也應該能夠檢視來賓簿的現有批注。 若要達成此目的,請將名為 CommentList 的 ListView 控件新增至頁面底部。

注意

ListView 控件是 ASP.NET 3.5 版的新功能。 其設計目的是要以非常可自定義且有彈性的版面配置來顯示項目清單,但仍提供內建編輯、插入、刪除、分頁和排序功能,例如 GridView。 如果您使用 ASP.NET 2.0,則必須改用 DataList 或 Repeater 控件。 如需使用 ListView 的詳細資訊,請參閱 Scott Guthrie 的部落格文章: asp:ListView 控件和我的文章: 使用 ListView 控件顯示數據

開啟 ListView 的智慧標記,然後從 [選擇數據源] 下拉式清單中,將控件系結至新的數據源。 如我們在步驟 2 中所見,這會啟動 [數據源設定精靈]。 選取 [資料庫] 圖示,將產生的 SqlDataSource CommentsDataSource命名為 ,然後按兩下 [確定]。 接下來,從下拉式清單中選取 SecurityTutorialsConnectionString 連接字串,然後按 [下一步]。

此時,在步驟 2 中,我們會從下拉式清單中挑選 UserProfiles 數據表並選取要傳回的數據行,以傳回 (參考圖 9) 來指定要查詢的數據。 不過,這次我們想要製作一個 SQL 語句,不僅會從 GuestbookComments提取記錄,還要製作批注者的主城市、首頁、簽章和用戶名稱。 因此,選取 [指定自定義 SQL 語句或預存程式] 單選按鈕,然後按 [下一步]。

這會顯示 [定義自定義語句或預存程式] 畫面。 按兩下 [查詢產生器] 按鈕,以圖形方式建置查詢。 查詢產生器一開始會提示我們指定要從中查詢的數據表。 GuestbookComments選取 、 UserProfilesaspnet_Users 數據表,然後按下 [確定]。 這會將這三個數據表新增至設計介面。 由於、 UserProfilesaspnet_Users 數據表之間GuestbookComments有外鍵條件約束,因此查詢產生器會自動JOIN設定這些數據表。

所有保留專案都是指定要傳回的數據行。 GuestbookComments從資料表中選取SubjectBodyCommentDate 資料列;HomeTownUserProfiles資料表傳回、 HomepageUrlSignature 資料列; 並從 傳回 UserNameaspnet_Users。 此外,將 「ORDER BY CommentDate DESC新增至查詢結尾 SELECT ,以便先傳回最新的貼文。 進行這些選取之後,您的查詢產生器介面看起來應該類似圖 18 中的螢幕快照。

GuestbookComments、UserProfiles 和 aspnet_Users 數據表的建構查詢 JOIN

圖 18:建構查詢 JOIN (UserProfilesaspnet_UsersGuestbookComments按一下即可檢視完整大小的影像)

按兩下 [確定] 關閉 [查詢產生器] 視窗,並返回 [定義自定義語句或預存程式] 畫面。 按 [下一步] 前進到 [測試查詢] 畫面,您可以按兩下 [測試查詢] 按鈕來檢視查詢結果。 當您準備好時,請按兩下 [完成] 以完成 [設定數據源精靈]。

當我們在步驟 2 中完成設定數據源精靈時,相關聯的 DetailsView 控件集合 Fields 已更新為包含 所傳回之每個數據行的 SelectCommandBoundField。 不過,ListView 會保持不變;我們仍然需要定義其配置。 ListView 的版面配置可以透過其宣告式標記,或從其智慧標記中的 [設定 ListView] 選項手動建構。 我通常偏好手動定義標記,但使用您最自然的方法。

我最後針對 ListView 控制器使用下列 LayoutTemplateItemTemplateItemSeparatorTemplate

<asp:ListView ID="CommentList" runat="server" DataSourceID="CommentsDataSource">

     <LayoutTemplate>
          <span ID="itemPlaceholder" runat="server" />
          <p>
               <asp:DataPager ID="DataPager1" runat="server">

                    <Fields>
                         <asp:NextPreviousPagerField ButtonType="Button" 
                              ShowFirstPageButton="True"
                              ShowLastPageButton="True" />
                    </Fields>
               </asp:DataPager>

          </p>
     </LayoutTemplate>
     <ItemTemplate>
          <h4>
               <asp:Label ID="SubjectLabel" runat="server" 
                    Text='<%# Eval("Subject") %>' />

          </h4>
          <asp:Label ID="BodyLabel" runat="server" 
               Text='<%# Eval("Body").ToString().Replace(Environment.NewLine, "<br />") %>' />
          <p>

          ---<br />
          <asp:Label ID="SignatureLabel" Font-Italic="true" runat="server"
               Text='<%# Eval("Signature") %>' />

          <br />
          <br />
          My Home Town:
          <asp:Label ID="HomeTownLabel" runat="server" 
               Text='<%# Eval("HomeTown") %>' />

          <br />
          My Homepage:
          <asp:HyperLink ID="HomepageUrlLink" runat="server" 
               NavigateUrl='<%# Eval("HomepageUrl") %>' 
               Text='<%# Eval("HomepageUrl") %>' />

          </p>
          <p align="center">
          Posted by
          <asp:Label ID="UserNameLabel" runat="server" 
               Text='<%# Eval("UserName") %>' /> 

          on
          <asp:Label ID="CommentDateLabel" runat="server" 
               Text='<%# Eval("CommentDate") %>' />
          </p>
     </ItemTemplate>

     <ItemSeparatorTemplate>
          <hr />
     </ItemSeparatorTemplate>
</asp:ListView>

LayoutTemplate 定義 控件發出的標記,而 會 ItemTemplate 轉譯 SqlDataSource 傳回的每個專案。 ItemTemplate產生的標記會放在的 itemPlaceholder 控件中LayoutTemplate。 除了 itemPlaceholder之外,還 LayoutTemplate 包含 DataPager 控件,這會限制 ListView 只顯示每頁 10 個來賓簿批注, (預設) 並轉譯分頁介面。

我在 ItemTemplate 主體下方的元素中 <h4> 顯示每個客體批注的主旨。 請注意,用來顯示本文的語法會採用 databinding 語句所 Eval("Body") 傳回的數據、將它轉換成字串,並以 元素取代換行符 <br /> 。 需要此轉換,才能顯示提交批註時輸入的換行符,因為 HTML 會忽略空格符。 用戶的簽章會顯示在斜體內文下方,後面接著使用者的住家城市、首頁的連結、建立批注的日期和時間,以及離開批注的人員的用戶名稱。

請花點時間透過瀏覽器檢視頁面。 您應該會在此處顯示的步驟 5 中看到您新增至來賓簿的批注。

Guestbook.aspx現在顯示來賓簿的批注

圖 19Guestbook.aspx 現在顯示來賓簿的批注, (按兩下即可檢視完整大小的影像)

請嘗試將新的批註新增至客體簿。 按兩下 PostCommentButton 頁面回傳的按鈕,並將批註新增至資料庫,但 ListView 控制項不會更新以顯示新的批注。 這可由下列其中一項修正:

  • 更新 PostCommentButton 按鈕的 Click 事件處理程式,以便在將新批注插入資料庫之後叫用 ListView 控件的 DataBind() 方法,或
  • 將 ListView 控制件的 EnableViewState 屬性設定為 False。 此方法的運作方式是藉由停用控件的檢視狀態,而必須將它重新系結至每個回傳的基礎數據。

本教學課程中可下載的教學課程網站說明這兩種技術。 ListView 控件的 EnableViewState 屬性 False 和以程式設計方式將數據重新系結至 ListView 所需的程式碼,會出現在事件處理程式中 Click ,但會標記為批註。

注意

AdditionalUserInfo.aspx目前頁面可讓用戶檢視和編輯其住家城市、首頁和簽章設定。 更新以顯示已登入使用者的來賓簿批注可能很好 AdditionalUserInfo.aspx 。 也就是說,除了檢查和修改其資訊之外,用戶可以瀏覽 AdditionalUserInfo.aspx 頁面以查看過去所做的來賓簿批注。 我將此保留為感興趣的讀者練習。

步驟 6:自定義 CreateUserWizard 控件以包含主鎮、首頁和簽章的介面

頁面SELECT所使用的Guestbook.aspx查詢會使用 INNER JOIN 結合、 UserProfilesaspnet_Users 數據表之間的GuestbookComments相關記錄。 如果中沒有記錄UserProfiles的使用者提出來賓簿批注,則批注將不會顯示在 ListView 中,因為 INNER JOIN 只有在 和 aspnet_Users中有相符的記錄時,才會傳回GuestbookComments記錄UserProfiles。 如我們在步驟 3 中所見,如果用戶在頁面中沒有記錄 UserProfiles ,就無法檢視或編輯其設定 AdditionalUserInfo.aspx

不需要說,由於我們的設計決策,成員資格系統中的每一個使用者帳戶在數據表中 UserProfiles 都有相符的記錄非常重要。 每當透過 CreateUserWizard 建立新的成員資格用戶帳戶時,我們想要新增 UserProfiles 對應的記錄。

建立使用者帳戶 教學課程中所述,建立新的成員資格用戶帳戶之後,CreateUserWizard 控件會引發其 CreatedUser 事件。 我們可以為此事件建立事件處理程式、取得剛建立使用者的 UserId,然後將記錄UserProfiles插入具有、 HomepageUrlSignature 資料行預設值的HomeTown數據表中。 此外,您可以自定義 CreateUserWizard 控件的介面來包含其他 TextBox,以提示使用者輸入這些值。

讓我們先看看如何將新的數據列新增至 UserProfiles 事件處理程式中具有預設值的 CreatedUser 數據表。 接下來,我們將瞭解如何自定義 CreateUserWizard 控件的使用者介面,以包含其他表單域來收集新使用者的主市、首頁和簽章。

將預設數據列新增至UserProfiles

在 [ 建立使用者帳戶 ] 教學課程中,我們已將 CreateUserWizard 控件新增至 CreatingUserAccounts.aspx 資料夾中的頁面 Membership 。 為了讓 CreateUserWizard 控制項在使用者帳戶建立時將記錄新增至 UserProfiles 數據表,我們需要更新 CreateUserWizard 控制件的功能。 讓我們改為將新的 CreateUserWizard 控件新增至EnhancedCreateUserWizard.aspx頁面,並在該處修改本教學課程,而不是對頁面進行這些變更CreatingUserAccounts.aspx

EnhancedCreateUserWizard.aspx在 Visual Studio 中開啟頁面,並將 CreateUserWizard 控件從 [工具箱] 拖曳至頁面。 將 CreateUserWizard 控制的 ID 屬性設定為 NewUserWizard。 如同我們在 建立使用者帳戶 教學課程中所討論,CreateUserWizard 的預設使用者介面會提示訪客提供必要資訊。 一旦提供這項資訊,控件就會在成員資格架構中內部建立新的用戶帳戶,而不需要我們撰寫單行程序代碼。

CreateUserWizard 控件在其工作流程期間會引發一些事件。 訪客提供要求資訊並提交窗體之後,CreateUserWizard 控件一開始會引發其 CreatingUser 事件。 如果在建立程式期間發生問題,就會 CreateUserError 引發事件 ;不過,如果成功建立使用者,則會 CreatedUser 引發事件 。 在建立 使用者帳戶 教學課程中,我們建立了 事件的事件處理程式 CreatingUser ,以確保提供的用戶名稱不包含任何前置或尾端空格,而且用戶名稱不會出現在密碼中的任何位置。

為了在數據表中 UserProfiles 為剛建立的使用者新增數據列,我們需要為 CreatedUser 事件建立事件處理程式。 在事件引發時 CreatedUser ,已在 Membership 架構中建立用戶帳戶,讓我們能夠擷取帳戶的 UserId 值。

建立 事件的CreatedUser事件處理程式NewUserWizard,並新增下列程序代碼:

Protected Sub NewUserWizard_CreatedUser(ByVal sender As Object, ByVal e As System.EventArgs) Handles NewUserWizard.CreatedUser
     ' Get the UserId of the just-added user
     Dim newUser As MembershipUser = Membership.GetUser(NewUserWizard.UserName)
     Dim newUserId As Guid = CType(newUser.ProviderUserKey, Guid)

     ' Insert a new record into UserProfiles
     Dim connectionString As String = 

          ConfigurationManager.ConnectionStrings("SecurityTutorialsConnectionString").ConnectionString
     Dim insertSql As String = "INSERT INTO UserProfiles(UserId, HomeTown, HomepageUrl,
          Signature) VALUES(@UserId, @HomeTown, @HomepageUrl, @Signature)"

     Using myConnection As New SqlConnection(connectionString)
          myConnection.Open()
          Dim myCommand As New SqlCommand(insertSql, myConnection)
          myCommand.Parameters.AddWithValue("@UserId", newUserId)
          myCommand.Parameters.AddWithValue("@HomeTown", DBNull.Value)

          myCommand.Parameters.AddWithValue("@HomepageUrl", DBNull.Value)
          myCommand.Parameters.AddWithValue("@Signature", DBNull.Value)
          myCommand.ExecuteNonQuery()
          myConnection.Close()
     End Using
End Sub

上述程式代碼是藉由擷取剛新增用戶帳戶的UserId。 這是使用 Membership.GetUser(username) 方法來傳回特定使用者的相關信息,然後使用 ProviderUserKey 屬性來擷取其UserId來完成。 CreateUserWizard 控制項中使用者輸入的使用者名稱可透過其 UserName 屬性取得。

接下來,會從 Web.config 擷取 連接字串,INSERT並指定語句。 必要的 ADO.NET 物件會具現化並執行 命令。 此程式代碼會將 DBNull 實例指派給 @HomeTown@HomepageUrl@Signature 參數,這會影響插入、 HomepageUrlSignature 欄位的資料庫NULLHomeTown

EnhancedCreateUserWizard.aspx透過瀏覽器瀏覽頁面,並建立新的用戶帳戶。 這麼做之後,請返回 Visual Studio 並檢查 和 UserProfiles 數據表的內容aspnet_Users, (,就像我們在圖 12) 中所做的一樣。 您應該會在 中看到aspnet_Users新的用戶帳戶,以及具有 NULLHomepageUrlSignature) 值的HomeTown對應UserProfiles數據列 (。

已新增新的用戶帳戶和UserProfiles記錄

圖 20:已新增新的使用者帳戶和 UserProfiles 記錄, (按單擊即可檢視完整大小的影像)

在訪客提供新的帳戶資訊並按兩下 [建立使用者] 按鈕之後,就會建立用戶帳戶,並新增至數據表的數據 UserProfiles 列。 CreateUserWizard 接著會顯示其 CompleteWizardStep,以顯示成功訊息和 [繼續] 按鈕。 按兩下 [繼續] 按鈕會導致回傳,但不會採取任何動作,讓使用者停留在 EnhancedCreateUserWizard.aspx 頁面上。

我們可以指定 URL,以透過 CreateUserWizard 控制的 ContinueDestinationPageUrl 屬性按兩下 [繼續] 按鈕時,將使用者傳送至 。 將 ContinueDestinationPageUrl 屬性設定為 “~/Membership/AdditionalUserInfo.aspx”。 這會讓新使用者前往 AdditionalUserInfo.aspx,讓他們可以在其中檢視和更新其設定。

自定義 CreateUserWizard 的介面,以提示新使用者的主市、首頁和簽章

CreateUserWizard 控件的預設介面足以用於簡單的帳戶建立案例,其中只需要收集核心用戶帳戶資訊,例如使用者名稱、密碼和電子郵件。 但是,如果我們想要提示訪客在建立她的帳戶時進入她的主市、首頁和簽章,該怎麼辦? 您可以自定義 CreateUserWizard 控件的介面,以在註冊時收集其他資訊,而且此資訊可用於事件處理程式, CreatedUser 將其他記錄插入基礎資料庫中。

CreateUserWizard 控制項會擴充 ASP.NET Wizard 控制項,這是一個控制項,可讓頁面開發人員定義一系列的已排序 WizardSteps。 精靈控件會轉譯使用中的步驟,並提供導覽介面,讓訪客可以瀏覽這些步驟。 精靈控件很適合用來將長任務細分成數個簡短步驟。 如需精靈控件的詳細資訊,請參閱 使用 ASP.NET 2.0 精靈控件建立逐步使用者介面

CreateUserWizard 控制元件的預設標記會定義兩 WizardSteps個 : CreateUserWizardStepCompleteWizardStep

<asp:CreateUserWizard ID="NewUserWizard" runat="server"
          ContinueDestinationPageUrl="~/Membership/AdditionalUserInfo.aspx">
     <WizardSteps>
          <asp:CreateUserWizardStep ID="CreateUserWizardStep1" runat="server">

          </asp:CreateUserWizardStep>
          <asp:CompleteWizardStep ID="CompleteWizardStep1" runat="server">
          </asp:CompleteWizardStep>
     </WizardSteps>
</asp:CreateUserWizard>

第一個 WizardStepCreateUserWizardStep轉譯提示輸入使用者名稱、密碼、電子郵件等的介面。 訪客提供這項資訊並按兩下 [建立使用者] 之後,她會顯示 CompleteWizardStep,其中顯示成功訊息和 [繼續] 按鈕。

若要自定義 CreateUserWizard 控件的介面以包含其他表單域,我們可以:

  • 建立一或多個新WizardStep,包含額外的使用者介面專案。 若要將新的 WizardStep 新增至 CreateUserWizard,請按兩下其智慧標記中的 [新增/移除WizardStep] 連結,以啟動WizardStep集合 編輯器。 您可以從該處新增、移除或重新排序精靈中的步驟。 這是我們將在本教學課程中使用的方法。

  • CreateUserWizardStep轉換進入可WizardStep編輯這會以對等WizardStep專案取代 ,CreateUserWizardStep其標記會定義符合 CreateUserWizardStep之的使用者介面。 藉由將 CreateUserWizardStep 轉換成 , WizardStep 我們可以重新置放控制項,或將其他使用者介面元素新增至此步驟。 若要將 CreateUserWizardStepCompleteWizardStep 轉換成可 WizardStep編輯的 ,請按下控件智慧標記中的 [自定義建立使用者步驟] 或 [自定義完成步驟] 連結。

  • 使用上述兩個選項的一些組合。

請務必記住,CreateUserWizard 控件會在按兩下 [建立使用者] 按鈕 CreateUserWizardStep時執行其用戶帳戶建立程式。 如果 之後有額外的 WizardStepCreateUserWizardStep ,則不重要。

將自定義WizardStep新增至 CreateUserWizard 控件以收集其他使用者輸入時,可以在 之前或之後CreateUserWizardStep放置自定義WizardStep。 如果它出現在 之前 CreateUserWizardStep ,則從自定義 WizardStep 收集的額外使用者輸入可供事件處理程式使用 CreatedUser 。 不過,如果自定義 WizardStep 在之後 CreateUserWizardStep ,在顯示自定義 WizardStep 時,已經建立新的用戶帳戶,而且 CreatedUser 事件已經引發。

圖 21 顯示新增之前加入 WizardStepCreateUserWizardStep工作流程。 由於事件引發時 CreatedUser 已收集其他用戶資訊,因此我們只需要更新 CreatedUser 事件處理程式來擷取這些輸入,並使用這些輸入作為 INSERT 語句的參數值 (,而不是 DBNull.Value) 。

當其他WizardStep位於 CreateUserWizardStep 之前時,CreateUserWizard 工作流程

圖 21:當其他 WizardStep 專案在 (之前 CreateUserWizardStep 按兩下 以檢視完整大小的影像 時,CreateUserWizard 工作流程)

不過,如果在 之後CreateUserWizardStep放置自定義WizardStep,則建立用戶帳戶程式會在用戶有機會進入她的家庭城市、首頁或簽章之前發生。 在這種情況下,這個額外的信息必須在用戶帳戶建立之後插入資料庫,如圖 22 所示。

當其他WizardStep出現在 CreateUserWizardStep 之後的 CreateUserWizard 工作流程

圖 22:當其他專案出現在 (單擊以CreateUserWizardStep 檢視完整大小的影像時WizardStep,CreateUserWizard 工作流程)

圖 22 中顯示的工作流程會等候將記錄插入數據表, UserProfiles 直到步驟 2 完成為止。 不過,如果訪客在步驟 1 之後關閉瀏覽器,我們就會到達用戶帳戶建立的狀態,但未將任何記錄新增至 UserProfiles。 其中一個因應措施是在事件處理程式中CreatedUser插入UserProfiles或預設值的記錄NULL, (在步驟 1) 之後引發,然後在步驟 2 完成之後更新此記錄。 這可確保 UserProfiles 即使使用者於中間結束註冊程式,也會為用戶帳戶新增記錄。

在本教學課程中,我們將建立新的 WizardStep ,其發生在 之後 CreateUserWizardStep ,但在 之前 CompleteWizardStep。 讓我們先就地取得WizardStep並加以設定,然後查看程序代碼。

從 CreateUserWizard 控制件的智慧標記中,選取 [新增/移除 WizardStep s],這會顯示 WizardStep [集合 編輯器] 對話框。 將新的 WizardStep新增 ,將其 ID 設定為 UserSettings,並將其 Title 設定為 [您的設定],並將其 StepType 設定為 Step。 然後將它 CreateUserWizardStep 放在 (「註冊新帳戶」) 之後,以及 (「完成」) 之前 CompleteWizardStep ,如圖 23 所示。

將新的WizardStep新增至 CreateUserWizard 控件

圖 23:新增 WizardStep 至 CreateUserWizard 控件 (按兩下即可檢視完整大小的影像)

按兩下 [確定] 關閉 [WizardStep集合 編輯器] 對話框。 New WizardStep 是由 CreateUserWizard 控件更新的宣告式標記所辨識:

<asp:CreateUserWizard ID="NewUserWizard" runat="server"

          ContinueDestinationPageUrl="~/Membership/AdditionalUserInfo.aspx">
     <WizardSteps>
          <asp:CreateUserWizardStep ID="CreateUserWizardStep1" runat="server">
          </asp:CreateUserWizardStep>
          <asp:WizardStep runat="server" ID="UserSettings" StepType="Step"

               Title="Your Settings">
          </asp:WizardStep>
          <asp:CompleteWizardStep ID="CompleteWizardStep1" runat="server">
          </asp:CompleteWizardStep>
     </WizardSteps>
</asp:CreateUserWizard>

請注意新的 <asp:WizardStep> 專案。 我們需要新增使用者介面,以在這裡收集新使用者的主市、首頁和簽章。 您可以在宣告式語法中或透過 Designer 輸入此內容。 若要使用 Designer,請從智慧標記的下拉式清單中選取 [您的設定] 步驟,以查看 Designer 中的步驟。

注意

選取智慧標記下拉式清單的步驟會更新 CreateUserWizard 控件的 ActiveStepIndex 屬性,指定起始步驟的索引。 因此,如果您使用此下拉式清單來編輯 Designer 中的 [您的設定] 步驟,請務必將它設回 「註冊您的新帳戶」,以便在使用者第一次瀏覽EnhancedCreateUserWizard.aspx頁面時顯示此步驟。

在 「您的設定」步驟內建立使用者介面,其中包含三個名為 HomeTownHomepageUrlSignature的 TextBox 控制件。 建構此介面之後,CreateUserWizard 的宣告式標記看起來應該如下所示:

<asp:CreateUserWizard ID="NewUserWizard" runat="server"

          ContinueDestinationPageUrl="~/Membership/AdditionalUserInfo.aspx">
     <WizardSteps>
          <asp:CreateUserWizardStep ID="CreateUserWizardStep1" runat="server">
          </asp:CreateUserWizardStep>
          <asp:WizardStep runat="server" ID="UserSettings" StepType="Step"

                    Title="Your Settings">
               <p>
                    <b>Home Town:</b><br />
                    <asp:TextBox ID="HomeTown" runat="server"></asp:TextBox>

               </p>
               <p>
                    <b>Homepage URL:</b><br />
                    <asp:TextBox ID="HomepageUrl" Columns="40" runat="server"></asp:TextBox>

               </p>
               <p>
                    <b>Signature:</b><br />
                    <asp:TextBox ID="Signature" TextMode="MultiLine" Width="95%"

                         Rows="5" runat="server"></asp:TextBox>
               </p>
          </asp:WizardStep>
          <asp:CompleteWizardStep ID="CompleteWizardStep1" runat="server">

          </asp:CompleteWizardStep>
     </WizardSteps>
</asp:CreateUserWizard>

請繼續瀏覽此頁面,並透過瀏覽器建立新的用戶帳戶,並指定住家城市、首頁和簽章的值。 完成之後,CreateUserWizardStep會在 Membership 架構中建立用戶帳戶,而事件處理程式會CreatedUser執行,這會將新的數據列新增至 UserProfiles,但具有 、 HomepageUrlSignature的資料庫NULLHomeTown。 永遠不會使用針對住家鎮、首頁和簽章輸入的值。 淨結果是新的用戶帳戶,其中包含UserProfiles尚未指定、 HomepageUrlSignature 欄位的記錄HomeTown

我們需要在「您的設定」步驟之後執行程式碼,該步驟會採用使用者輸入的首頁、honepage 和簽章值,並更新適當的 UserProfiles 記錄。 每次使用者在精靈控件中的步驟之間移動時,都會引發精靈 ActiveStepChanged 的事件 。 我們可以為此事件建立事件處理程式,並在「您的設定」步驟完成時更新 UserProfiles 數據表。

新增 CreateUserWizard ActiveStepChanged 事件的事件處理程式,並新增下列程式代碼:

Protected Sub NewUserWizard_ActiveStepChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles NewUserWizard.ActiveStepChanged
     ' Have we JUST reached the Complete step?
     If NewUserWizard.ActiveStep.Title = "Complete" Then
          Dim UserSettings As WizardStep = CType(NewUserWizard.FindControl("UserSettings"),WizardStep)

          ' Programmatically reference the TextBox controls
          Dim HomeTown As TextBox = CType(UserSettings.FindControl("HomeTown"), TextBox)
          Dim HomepageUrl As TextBox = CType(UserSettings.FindControl("HomepageUrl"), TextBox)
          Dim Signature As TextBox = CType(UserSettings.FindControl("Signature"), TextBox)

          ' Update the UserProfiles record for this user
          ' Get the UserId of the just-added user
          Dim newUser As MembershipUser = Membership.GetUser(NewUserWizard.UserName)
          Dim newUserId As Guid = CType(newUser.ProviderUserKey, Guid)

          ' Insert a new record into UserProfiles
          Dim connectionString As String = ConfigurationManager.ConnectionStrings("SecurityTutorialsConnectionString").ConnectionString

          Dim updateSql As String = "UPDATE UserProfiles SET HomeTown = @HomeTown, HomepageUrl
               = @HomepageUrl, Signature = @Signature WHERE UserId = @UserId"

          Using myConnection As New SqlConnection(connectionString)
               myConnection.Open()
               Dim myCommand As New SqlCommand(updateSql, myConnection)
               myCommand.Parameters.AddWithValue("@HomeTown", HomeTown.Text.Trim())
               myCommand.Parameters.AddWithValue("@HomepageUrl", HomepageUrl.Text.Trim())
               myCommand.Parameters.AddWithValue("@Signature", Signature.Text.Trim())

               myCommand.Parameters.AddWithValue("@UserId", newUserId)
               myCommand.ExecuteNonQuery()
               myConnection.Close()
          End Using
     End If
End Sub

上述程式代碼會從判斷我們是否剛到達「完成」步驟開始。 由於「完成」步驟會在「您的設定」步驟之後立即發生,因此當訪客到達「完成」步驟時,這表示她剛完成「您的設定」步驟。

在這種情況下,我們需要以程序設計方式參考 中的 UserSettings WizardStepTextBox 控制件。 使用 方法以程式設計方式參考 ,然後再次參考 UserSettings WizardStep中的 TextBox,WizardStep即可完成FindControl此作業。 一旦參考 TextBox,我們就可以執行 UPDATE 語句。 語句 UPDATE 的參數數目 INSERT 與事件處理程式中的 CreatedUser 語句相同,但在這裡我們使用使用者所提供的住家城市、首頁和簽章值。

有了這個事件處理程式,請瀏覽 EnhancedCreateUserWizard.aspx 瀏覽器頁面,並建立新的用戶帳戶,以指定住家城市、首頁和簽章的值。 建立新帳戶之後,您應該重新導向至 AdditionalUserInfo.aspx 頁面,其中會顯示剛輸入的首頁、首頁和簽章資訊。

注意

我們的網站目前有兩個頁面,訪客可以從中建立新的帳戶: CreatingUserAccounts.aspxEnhancedCreateUserWizard.aspx。 網站的網站地圖和登入頁面指向 CreatingUserAccounts.aspx 頁面,但 CreatingUserAccounts.aspx 頁面不會提示使用者輸入其主市、首頁和簽章資訊,而且不會將對應的數據列新增至 UserProfiles。 因此,請更新 CreatingUserAccounts.aspx 頁面,使其提供這項功能,或更新要參考 EnhancedCreateUserWizard.aspx 的網站地圖和登入頁面,而不是 CreatingUserAccounts.aspx。 如果您選擇後者選項,請務必更新 Membership 資料夾的 Web.config 檔案,以便允許匿名使用者存取 EnhancedCreateUserWizard.aspx 頁面。

摘要

在本教學課程中,我們已探討模型化數據的技術,這些數據與成員資格架構內的使用者帳戶相關。 特別是,我們查看了與使用者帳戶共用一對多關聯性的模型實體,以及共用一對一關聯性的數據。 此外,我們已瞭解如何顯示、插入和更新這項相關信息,以及使用 SqlDataSource 控制件的一些範例,以及使用 ADO.NET 程式代碼的其他範例。

本教學課程會完成我們的用戶帳戶。 從下一個教學課程開始,我們將注意角色。 在接下來的幾個教學課程中,我們將探討角色架構、如何建立新角色、如何指派角色給使用者、如何判斷用戶所屬的角色,以及如何套用角色型授權。

快樂的程序設計!

深入閱讀

如需本教學課程中所討論之主題的詳細資訊,請參閱下列資源:

關於作者

Scott Mitchell 是多個 ASP/ASP.NET 書籍的作者,且 4GuysFromRolla.com 的作者,自 1998 年以來,已與 Microsoft Web 技術合作。 Scott 是獨立顧問、訓練員和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以在 或 透過在 的http://ScottOnWriting.NET部落格連線mitchell@4guysfromrolla.com到 Scott。

特別感謝...

本教學課程系列是由許多實用的檢閱者檢閱。 有興趣檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行 mitchell@4GuysFromRolla.com放在 。