共用方式為


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

作者 :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 的資料行。 首先,新增名為 CommentId 類型的 uniqueidentifier 資料行。 此資料行會唯一識別客體簿中的每個批註,因此不允許 NULL ,並將它標示為數據表的主鍵。 我們可以藉由將資料行的預設值設定為 INSERTCommentId ,來指出應該針對這個欄位自動產生新的 uniqueidentifier 值,而不是為每個 欄位 INSERT 提供值 NEWID() 。 新增此第一個欄位,並將其標示為主鍵,並設定其預設值之後,您的畫面看起來應該類似圖 2 所示的螢幕擷取畫面。

新增名為 CommentId 的主要資料行

圖 2:新增名為 CommentId (按一下即可檢視完整大小的影像)

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

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

注意

如我們在SQL Server教學課程中建立成員資格架構中所述,成員資格架構的設計目的是讓具有不同使用者帳戶的多個 Web 應用程式共用相同的使用者存放區。 其作法是將使用者帳戶分割成不同的應用程式。 而且,雖然每個使用者名稱保證在應用程式中都是唯一的,但相同的使用者名稱可能會用於使用相同使用者存放區的不同應用程式中。 和 ApplicationId 欄位上的 aspnet_Users 資料表 UserName 中有複合 UNIQUE 條件約束,但不只是欄位上的 UserName 條件約束。 因此,aspnet_Users資料表可能會有兩個具有相同值的 (或更多) 記錄 UserName 。 不過,資料表欄位 UserId (有一個 UNIQUE 條件約束 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 sththat 會將設定檔資料儲存在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 控制項新增至頁面,並將其 屬性設定為 UserProfile ,並清除其 IDWidthHeight 屬性。 展開 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 void UserProfileDataSource_Selecting(object sender, 
          SqlDataSourceSelectingEventArgs e)
{
     // Get a reference to the currently logged on user
     MembershipUser currentUser = Membership.GetUser();
 
     // Determine the currently logged on user's UserId value
     Guid currentUserId = (Guid)currentUser.ProviderUserKey;
 
     // Assign the currently logged on user's UserId to the @UserId parameter
     e.Command.Parameters["@UserId"].Value = currentUserId;
}

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

注意

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

如果您透過瀏覽器流覽 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:已針對暴力密碼新增 UserProfiles 記錄 (按一下即可檢視完整大小的影像)

AdditionalUserInfo.aspx返回頁面,以暴力密碼登入。 如圖 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:DetailsView 轉譯可編輯的介面 (按一下即可檢視完整大小的影像)

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

若要解決此問題,請返回 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 void UserProfile_ItemUpdated(object sender, DetailsViewUpdatedEventArgs e)
{
     SettingsUpdatedMessage.Visible = true;
}

透過瀏覽器返回 AdditionalUserInfo.aspx 頁面並更新資料。 這次會顯示有用的狀態訊息。

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

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

注意

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

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

回想一下,主版頁面在其 LoginContent ContentPlaceHolder 中包含 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;將第二個 ID 的 設定為 Body ,並將其 TextMode 設定為 MultiLine ,並將其 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 。 您可能需要將此命名空間匯入頁面的程式碼後置類別 (亦即 using System.Data.SqlClient;) 。

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

protected void PostCommentButton_Click(object sender, EventArgs e)
{
     if (!Page.IsValid)
          return;
 
     // Determine the currently logged on user's UserId
     MembershipUser currentUser = Membership.GetUser();
     Guid currentUserId = (Guid)currentUser.ProviderUserKey;
 
     // Insert a new record into GuestbookComments
     string connectionString = 
          ConfigurationManager.ConnectionStrings["SecurityTutorialsConnectionString"].ConnectionString;
     string insertSql = "INSERT INTO GuestbookComments(Subject, Body, UserId) VALUES(@Subject,
               @Body, @UserId)";
 
     using (SqlConnection myConnection = new SqlConnection(connectionString))
     {
          myConnection.Open();
          SqlCommand myCommand = 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();
     }
 
     // "Reset" the Subject and Body TextBoxes
     Subject.Text = string.Empty;
     Body.Text = string.Empty;
}

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

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

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

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

圖 17 顯示離開兩個批註之後的資料表內容 GuestbookComments

您可以在 GuestbookComments 資料表中看到 Guestbook 批註

圖 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從資料表中 Subject 選取 、 BodyCommentDate 資料行;從資料表傳 HomeTown 回 、 HomepageUrlSignatureUserProfiles 資料行;並從 傳 aspnet_UsersUserName 。 此外,將 「 ORDER BY CommentDate DESC 」 新增至查詢結尾 SELECT ,以便先傳回最新的文章。 進行這些選取之後,您的查詢產生器介面看起來應該類似圖 18 中的螢幕擷取畫面。

建構的查詢 JOINs GuestbookComments、UserProfiles 和aspnet_Users資料表

圖 18:建構的查詢 JOIN (aspnet_UsersGuestbookCommentsUserProfiles按一下即可檢視大小完整的映射)

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

當我們在步驟 2 中完成設定資料來源精靈時,相關聯的 DetailsView 控制項 Fields 集合已更新,以包含 所傳回之每個資料行的 SelectCommand BoundField。 不過,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 中有 UserProfiles 相符的記錄時,才會傳回 GuestbookComments 記錄。 如同我們在步驟 3 中所見,如果使用者在頁面中沒有記錄 UserProfiles ,則無法檢視或編輯其設定 AdditionalUserInfo.aspx

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

建立使用者帳戶教學課程中所述,建立新的成員資格使用者帳戶之後,CreateUserWizard 控制項會引發其CreatedUser 事件。 我們可以為此事件建立事件處理常式、取得剛建立使用者的 UserId,然後將記錄 UserProfiles 插入具有 、 HomepageUrl 和 資料 Signature 行預設值的 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 ,已在成員資格架構中建立使用者帳戶,讓我們能夠擷取帳戶的 UserId 值。

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

protected void NewUserWizard_CreatedUser(object sender, EventArgs e)
{
     // Get the UserId of the just-added user
     MembershipUser newUser = Membership.GetUser(NewUserWizard.UserName);
     Guid newUserId = (Guid)newUser.ProviderUserKey;
 
     // Insert a new record into UserProfiles
     string connectionString = 
          ConfigurationManager.ConnectionStrings["SecurityTutorialsConnectionString"].ConnectionString;
     string insertSql = "INSERT INTO UserProfiles(UserId, HomeTown, HomepageUrl,
          Signature) VALUES(@UserId, @HomeTown, @HomepageUrl, @Signature)";
 
     using (SqlConnection myConnection = new SqlConnection(connectionString))
     {
          myConnection.Open();
          SqlCommand myCommand = 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();
     }
}

上述程式碼是藉由擷取剛新增使用者帳戶的 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,請按一下其智慧標籤中的 [新增/移除 WizardSteps ] 連結,以啟動 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 void NewUserWizard_ActiveStepChanged(object sender, EventArgs e)
{
     // Have we JUST reached the Complete step?
     if (NewUserWizard.ActiveStep.Title == "Complete")
     {
          WizardStep UserSettings = NewUserWizard.FindControl("UserSettings") as
          WizardStep;
 
          // Programmatically reference the TextBox controls
          TextBox HomeTown = UserSettings.FindControl("HomeTown") as TextBox;
          TextBox HomepageUrl = UserSettings.FindControl("HomepageUrl") as TextBox;
          TextBox Signature = UserSettings.FindControl("Signature") as TextBox;
 
          // Update the UserProfiles record for this user
          // Get the UserId of the just-added user
          MembershipUser newUser = Membership.GetUser(NewUserWizard.UserName);
          Guid newUserId = (Guid)newUser.ProviderUserKey;
 
          // Insert a new record into UserProfiles
          string connectionString = 
               ConfigurationManager.ConnectionStrings["SecurityTutorialsConnectionString"].ConnectionString;
          string updateSql = "UPDATE UserProfiles SET HomeTown = @HomeTown, HomepageUrl
               = @HomepageUrl, Signature = @Signature WHERE UserId = @UserId";
 
          using (SqlConnection myConnection = new SqlConnection(connectionString))
          {
               myConnection.Open();
               SqlCommand myCommand = 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();
          }
     }
}

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

在這種情況下,我們需要以程式設計方式參考 中的 UserSettings WizardStep TextBox 控制項。 使用 方法以程式設計方式參考 ,然後再次參考 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。 Scott 可以透過 mitchell@4guysfromrolla.com 在 上的部落格或透過 http://ScottOnWriting.NET 其部落格來連線。

特別感謝...

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