追加のユーザー情報を格納する (C#)
Note
この記事が作成された後で、ASP.NET メンバーシップ プロバイダーは ASP.NET Identity に置き換えられました。 この記事が作成された時点で使われていたメンバーシップ プロバイダーではなく、 ASP.NET Identity プラットフォームを使うようにアプリを更新することを強くお勧めします。 ASP.NET メンバーシップ システムと比べると、ASP.NET Identity には次のような多くの利点があります。
- パフォーマンスの向上
- 向上した拡張性とテストの容易性
- OAuth、OpenID Connect、2 要素認証のサポート
- クレームベースの ID のサポート
- ASP.Net Core との相互運用性の向上
このチュートリアルでは、非常に基本的なゲストブック アプリケーションを構築することで、この質問に答えます。 そのために、データベース内のユーザー情報をモデル化するためのさまざまなオプションを確認し、このデータを Membership フレームワークによって作成されたユーザー アカウントに関連付ける方法を確認します。
はじめに
ASP.NET のMembership フレームワークには、ユーザーを管理するための柔軟なインターフェイスがあります。 Membership API には、資格情報の検証、現在ログオンしているユーザーに関する情報の取得、新しいユーザー アカウントの作成、ユーザー アカウントの削除などのメソッドが含まれています。 Membership フレームワークの各ユーザー アカウントには、資格情報の検証とユーザー アカウント関連の重要なタスクの実行に必要なプロパティのみが含まれています。 Membership フレームワークのユーザー アカウントをモデル化する MembershipUser
クラスのメソッドとプロパティがその証拠となります。 このクラスには UserName
、Email
、IsLockedOut
などのプロパティおよび、GetPassword
、UnlockUser
などのメソッドがあります。
多くの場合、アプリケーションは Membership フレームワークに含まれていない追加のユーザー情報を格納する必要があります。 たとえば、オンライン小売業者の場合、各ユーザーが配送先住所と請求先住所、支払い情報、配送設定、連絡先電話番号を保存できるようにする必要があるでしょう。 さらに、システム内の各オーダーは、特定のユーザー アカウントに関連付けられます。
MembershipUser
クラスには、PhoneNumber
、DeliveryPreferences
、PastOrders
などのプロパティは含まれません。 では、アプリケーションに必要なユーザー情報を追跡し、Membership フレームワークと統合するにはどうすればよいでしょうか。 このチュートリアルでは、非常に基本的なゲストブック アプリケーションを構築することで、この質問に答えます。 そのために、データベース内のユーザー情報をモデル化するためのさまざまなオプションを確認し、このデータを Membership フレームワークによって作成されたユーザー アカウントに関連付ける方法を確認します。 それでは始めましょう。
手順 1: ゲストブック アプリケーションのデータ モデルを作成する
データベース内のユーザー情報をキャプチャし、Membership フレームワークによって作成されたユーザー アカウントに関連付けるために使用できるさまざまな手法があります。 これらの手法を説明するには、チュートリアル Web アプリケーションを拡張して、ある種類のユーザー関連データをキャプチャする必要があります。 (現在、アプリケーションのデータ モデルには、.SqlMembershipProvider
で必要なアプリケーション サービス テーブルのみが含まれています。)
認証されたユーザーがコメントを残すことができる、非常に単純なゲストブック アプリケーションを作成しましょう。 ゲストブックのコメントを保存するだけでなく、各ユーザーが自分のホーム タウン、ホームページ、署名を格納できるようにしましょう。 ユーザーのホーム タウン、ホーム ページ、署名が指定されている場合は、ゲストブックに残した各メッセージに表示されます。
GuestbookComments
テーブルの追加
ゲストブックのコメントをキャプチャするには、CommentId
、Subject
、Body
、CommentDate
などの列を持つ、GuestbookComments
というデータベース テーブルを作成する必要があります。 また、GuestbookComments
テーブル内の各レコードで、コメントを残したユーザーを参照する必要があります。
このテーブルをデータベースに追加するには、Visual Studio のデータベース エクスプローラーに移動し、SecurityTutorials
データベースにドリルダウンします。 [テーブル] フォルダーを右クリックし、[新しいテーブルの追加] を選択します。 これにより、新しいテーブルの列を定義できるインターフェイスが表示されます。
図 1: SecurityTutorials
データベースに新しいテーブルを追加する (クリックするとフルサイズの画像を表示します)
次に、GuestbookComments
の列を定義します。 最初に、CommentId
という名前の列を uniqueidentifier
型で追加します。 この列はゲストブック内の各コメントを一意に識別するため、NULL
を禁し、テーブルの主キーとしてマークします。 それぞれの INSERT
に CommentId
フィールドの値を指定するのではなく、列の既定値を NEWID()
に設定することで、INSERT
でこのフィールドに対して新しい uniqueidentifier
値が自動的に生成されることを示すことができます。 この最初のフィールドを追加し、それを主キーとしてマークし、既定値を設定すると、画面は図 2 に示すスクリーンショットのようになります。
図 2: CommentId
というプライマリ列を追加する (クリックするとフルサイズの画像を表示します)
次に、Subject
という名前の nvarchar(50)
型の列と、Body
という名前の nvarchar(MAX)
型の列を追加し、両方の列で NULL
を不許容にします。 その後、CommentDate
という名前の datetime
型の列を追加します。 NULL
を無効にし、CommentDate
列の既定値を getdate()
に設定します。
その後やることは、ユーザー アカウントを各ゲストブック コメントに関連付ける列を追加することのみです。 1 つの方法は、UserName
という名前の nvarchar(256)
型の列を追加することです。 これは、SqlMembershipProvider
以外のメンバーシップ プロバイダーを使用する場合に適した選択肢です。 ただし、このチュートリアル シリーズと同様に、SqlMembershipProvider
を使用する場合、aspnet_Users
テーブル内の UserName
列が一意であるとは限りません。 aspnet_Users
テーブルの主キーは UserId
であり、型は uniqueidentifier
です。 そのため、GuestbookComments
テーブルには、UserId
という、uniqueidentifier
型 (NULL
が許可されない) の列が必要です。 先に進み、この列を追加します。
Note
「SQL Server での Membership スキーマの 作成」チュートリアルで説明したように、Membership フレームワークは、別々のユーザー アカウントを持つ複数の Web アプリケーションが同じユーザー ストアを共有できるように設計されています。 これを行うには、ユーザー アカウントを別々のアプリケーションに分割します。 また、各ユーザー名はアプリケーション内で一意であることが保証されますが、同じユーザー ストアを使用する別のアプリケーションで同じユーザー名を使用できます。 aspnet_Users
テーブルでは、UserName
フィールドと ApplicationId
フィールドには複合 UNIQUE
制約がありますが、UserName
フィールド単体に対する制約はありません。 その結果、aspnet_Users テーブルに同じ UserName
値を持つ 2 つ以上のレコードが含まれる可能性があります。 ただし、aspnet_Users
テーブルの UserId
フィールドには UNIQUE
制約があります (主キーであるため)。 UNIQUE
制約がないと、GuestbookComments
テーブルと aspnet_Users
テーブルの間に外部キー制約を確立できないため、この制約が重要です。
UserId
列を追加したら、ツール バーの [保存] アイコンをクリックしてテーブルを保存します。 新しいテーブルに GuestbookComments
と名前を付けます。
GuestbookComments
テーブルに関する最後の問題が 1 つあります。GuestbookComments.UserId
列と aspnet_Users.UserId
列の間に外部キー制約を作成する必要があります。 これを実現するには、ツール バーの [リレーションシップ] アイコンをクリックして、[外部キー リレーションシップ] ダイアログ ボックスを起動します。 (または、[テーブル デザイナー] メニューに移動し、[リレーションシップ] を選択しても、このダイアログ ボックスを起動できます)。
[外部キーリレーションシップ] ダイアログ ボックスの左下隅にある [追加] ボタンをクリックします。 これにより、新しい外部キー制約が追加されますが、リレーションシップに関係するテーブルを定義する必要があります。
図 3: [外部キー リレーションシップ] ダイアログ ボックスを使用してテーブルの外部キー制約を管理する (クリックするとフルサイズの画像を表示します)
次に、右側の [テーブルと列の仕様] 行の省略記号アイコンをクリックします。 [テーブルと列] ダイアログ ボックスが開き、そこから主キー テーブルと列および、GuestbookComments
テーブルの外部キー列を指定できます。 具体的には、aspnet_Users
と UserId
を主キー テーブルと列として選択し、GuestbookComments
テーブルの UserId
を外部キー列として選択します (図 4 を参照)。 主および外部キーのテーブルと列を定義したら、[OK] をクリックして [外部キーのリレーションシップ] ダイアログ ボックスに戻ります。
図 4: aspnet_Users
および GuesbookComments
テーブル間の外部キー制約を確立する (クリックするとフルサイズの画像を表示します)
この時点で、外部キー制約が確立されました。 この制約を設けることで、存在しないユーザー アカウントを参照するゲストブック エントリが存在しないことが保証され、2 つのテーブル間のリレーションに整合性が保証 されます。 既定では、外部キー制約は、対応する子レコードがある場合、親レコードの削除を禁止します。 つまり、ユーザーが 1 つ以上のゲストブック コメントを作成し、そのユーザー アカウントを削除しようとしても、まずゲストブックのコメントを削除しない限り、削除は失敗します。
外部キー制約は、親レコードが削除されたときに関連付けられている子レコードを自動的に削除するように構成できます。 つまり、この外部キー制約は、ユーザー アカウントが削除されたときにユーザーのゲストブック エントリを自動的に削除するように設定できます。 これを行うには、[INSERT と UPDATE の仕様] セクションを展開し、[ルールの削除] プロパティを Cascade に設定します。
図 5: 削除を連鎖するように外部キー制約を構成する (クリックするとフルサイズの画像を表示します)
外部キー制約を保存するには、[閉じる] ボタンをクリックして [外部キー リレーションシップ] を終了します。 次に、ツール バーの [保存] アイコンをクリックして、テーブルとこのリレーションシップを保存します。
ユーザーのホーム タウン、ホームページ、署名の保存
GuestbookComments
表は、ユーザー アカウントとの一対多リレーションシップを共有する情報を格納する仕組みを示しています。 各ユーザー アカウントには任意の数のコメントが関連付けられるため、このリレーションシップは、各コメントを特定のユーザーにリンクする列を含む、一連のコメントを保持するテーブルを作成することによってモデル化されます。 SqlMembershipProvider
を使用する場合、このリンクは、UserId
という名前の uniqueidentifier
型の列と、この列と aspnet_Users.UserId
との外部キー制約を作成することで最適に確立されます。
ここでは、ユーザーのゲストブック コメントに表示されるホーム タウン、ホームページ、署名を格納するために、各ユーザー アカウントに 3 つの列を関連付ける必要があります。 これを実現するには、さまざまな方法があります。
aspnet_Users
またはaspnet_Membership
テーブルに新しい列を追加します。SqlMembershipProvider
で使用されるスキーマが変更されるため、このアプローチはお勧めしません。 この方法では、今後面倒なことが起こる可能性があります。 たとえば、将来のバージョンの ASP.NET で別のSqlMembershipProvider
スキーマが使用されることになると、どうなるでしょうか。 Microsoft が ASP.NET 2.0SqlMembershipProvider
データを新しいスキーマに移行するためのツールを収録する可能性はありますが、ASP.NET 2.0SqlMembershipProvider
スキーマに変更を行っている場合は、その変換は行えなくなります。ASP.NET の Profile フレームワークを使用し、ホーム タウン、ホームページ、署名のプロパティを定義します。 ASP.NET には、追加のユーザー固有のデータを格納するように設計された Profile フレームワークが含まれています。 Membership フレームワークと同様に、Profile フレームワークはプロバイダー モデルの上に構築されます。 .NET Framework には、SQL Server データベースにプロファイル データを格納する
SqlProfileProvider
が付属しています。 実際、データベースには、既に、SqlProfileProvider
(aspnet_Profile
) が使用するテーブルが含まれています。このテーブルは、SQL Server での Membership スキーマの作成チュートリアルでアプリケーション サービスを追加したときに追加されました。
プロファイル フレームワークの主な利点は、開発者がWeb.config
でプロファイル プロパティを定義できる点です。基になるデータ ストアとの間でプロファイル データをシリアル化するコードを記述する必要はありません。 要するに、プロファイル プロパティのセットを定義し、コードで操作することは非常に簡単です。 ただし、バージョン管理に関しては、依然としてプロファイル システムが必要な場合が多くあります。そのため、後で新しいユーザー固有のプロパティを追加する必要があるアプリケーションや、既存のプロパティを削除または変更するアプリケーションがある場合は、Profile フレームワークが最適なオプションではない可能性があります。 さらに、SqlProfileProvider
はプロファイル プロパティを高度に非正規化された方法で格納するため、プロファイル データに対して直接クエリを実行することはほぼ不可能になります (たとえば、ニューヨークがホーム タウンのユーザー数など)。
Profile フレームワークの詳細については、このチュートリアルの最後にある「その他の情報」セクションを参照してください。これら 3 つの列をデータベースの新しいテーブルに追加し、このテーブルと
aspnet_Users
の間に一対一のリレーションシップを確立します。この方法では、Profile フレームワークよりも少し多くの作業が必要ですが、データベースで追加のユーザー プロパティをモデル化する方法について、最大限の柔軟性が得られます。 このチュートリアルではこの方法をとります。
ホーム タウン、ホームページ、およびユーザーごとの署名を保存するための、UserProfiles
というテーブルを新規作成します。 [データベース エクスプローラー] ウィンドウで [テーブル] フォルダーを右クリックし、新しいテーブルを作成します。 最初の列に UserId
と名前を付け、その型を uniqueidentifier
に設定します。 NULL
値を禁止し、列を主キーとしてマークします。 次に、名前が HomeTown
で nvarchar(50)
型、名前が HomepageUrl
で nvarchar(100)
型の列および、nvarchar(500)
型の署名を追加します。 これら 3 つの列はそれぞれ、NULL
値を受け取ることができます。
図 6:UserProfiles
テーブルを作成する (クリックするとフルサイズの画像を表示します)
テーブルを UserProfiles
という名前で保存します。 最後に、UserProfiles
テーブルの UserId
フィールドと aspnet_Users.UserId
フィールドの間に外部キー制約を設定します。 GuestbookComments
と aspnet_Users
テーブル間の外部キー制約と同様に、この制約をカスケード削除にします。 UserProfiles
の UserId
フィールドは主キーであるため、これにより、各ユーザー アカウントの UserProfiles
テーブルに複数のレコードが発生しないことが保証されます。 この種類のリレーションシップは、1 対 1 と呼ばれます。
データ モデルが作成されたので、使用する準備ができました。 手順 2 と 3 では、現在ログオンしているユーザーがホーム タウン、ホームページ、署名情報を表示および編集する方法について説明します。 手順 4 では、認証されたユーザーがゲストブックに新しいコメントを送信し、既存のコメントを表示するためのインターフェイスを作成します。
手順 2: ユーザーのホーム タウン、ホームページ、署名を表示する
現在ログオンしているユーザーが自分のホーム タウン、ホームページ、署名情報を表示および編集できるようにするには、さまざまな方法があります。 TextBox コントロールと Label コントロールを使用してユーザー インターフェイスを手動で作成することも、DetailsView コントロールなどのデータ Web コントロールのいずれかを使用することもできます。 データベース SELECT
と UPDATE
ステートメントを実行するには、ページの分離コード クラスに ADO.NET コードを記述するか、または SqlDataSource で宣言型アプローチを採用します。 アプリケーションには階層化されたアーキテクチャが含まれていることが望ましいです。このアーキテクチャは、ページの分離コード クラスからプログラムで呼び出すか、ObjectDataSource コントロールを使用して宣言により呼び出すことができます。
このチュートリアル シリーズでは、フォーム認証、承認、ユーザー アカウント、ロールに重点を置いているため、これらのさまざまなデータ アクセス オプションや、ASP.NET ページから直接 SQL ステートメントを実行するよりも階層化アーキテクチャが推奨される理由については詳しく説明しません。 最も手早く簡単なオプションである DetailsView と SqlDataSource を使用して説明しますが、ここで説明する概念は、代替の Web コントロールとデータ アクセス ロジックにも確実に適用できます。 ASP.NET でデータを操作する方法の詳細については、私が執筆した「ASP.NET 2.0 でのデータ操作」チュートリアル シリーズをご覧ください。
Membership
フォルダーの AdditionalUserInfo.aspx
ページを開き、DetailsView コントロールをページに追加し、その ID
プロパティを UserProfile
に設定し、Width
プロパティと Height
プロパティをクリアします。 DetailsView のスマート タグを展開し、新しいデータ ソース コントロールにバインドするオプションを選択します。 こうすると、DataSource 構成ウィザードが起動します (図 7 を参照)。 最初の手順では、データ ソースの種類を指定するように求められます。 SecurityTutorials
データベースに直接接続するため、[データベース] アイコンを選択し、ID
を UserProfileDataSource
と指定します。
図 7: UserProfileDataSource
という新しい SqlDataSource コントロールを追加する (クリックするとフルサイズの画像を表示します)
次の画面では、使用するデータベースを指定するよう求められます。 SecurityTutorials
データベースの接続文字列を既に Web.config
で定義しました。 この SecurityTutorialsConnectionString
という接続文字列名は、ドロップダウン リストに含まれているでしょう。 このオプションを選択し、[次へ] をクリックします。
図 8: ドロップダウン リストから SecurityTutorialsConnectionString
を選択する (クリックするとフルサイズの画像を表示します)
次の画面では、クエリを実行するテーブルと列を指定するように求められます。 ドロップダウン リストから UserProfiles
テーブルを選択し、すべての列を確認します。
図 9: UserProfiles
テーブルからすべての列を取得する (クリックするとフルサイズの画像を表示します)
図 9 の現在のクエリは、UserProfiles
内のすべてのレコードを返しますが、必要なものは現在ログオンしているユーザーのレコードのみです。 WHERE
句を追加するには、WHERE
ボタンをクリックして [WHERE
句の追加] ダイアログ ボックスを表示します (図 10 を参照)。 ここでは、フィルターを適用する列、演算子、およびフィルター パラメーターのソースを選択できます。 列に UserId
、演算子に "=" を選択します。
残念ながら、現在ログオンしているユーザーの UserId
値を返す組み込みのパラメーター ソースはありません。 プログラムでこの値を取得する必要があります。 そこで、[ソース] ドロップダウン リストを [なし] に設定し、[追加] ボタンをクリックしてパラメーターを追加したら、[OK] をクリックします。
図 10: UserId
列にフィルター パラメーターを追加する (クリックするとフルサイズの画像を表示します)
[OK] をクリックすると、図 9 に示す画面に戻ります。 ただし、今回は、画面の下部にある SQL クエリに WHERE
句が含まれています。 [次へ] をクリックして、[クエリのテスト] 画面に移動します。 ここでは、クエリを実行して結果を確認できます。 [完了] をクリックして、ウィザードを完了します。
DataSource 構成ウィザードを完了すると、Visual Studio はウィザードで指定された設定に基づいて SqlDataSource コントロールを作成します。 さらに、SqlDataSource の SelectCommand
から返される各列の DetailsView に BoundFields を手動で追加します。 ユーザーはこの値を知る必要がないため、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
メソッドを呼び出して、現在ログオンしているユーザーへの参照を取得します。 これにより、ProviderUserKey
プロパティに UserId
を含む MembershipUser
オブジェクトが返されます。 その後、UserId
値は SqlDataSource の @UserId
パラメーターに割り当てられます。
Note
Membership.GetUser()
メソッドは、現在ログオンしているユーザーに関する情報を返します。 匿名ユーザーがページにアクセスすると、値 null
が返されます。 この場合、ProviderUserKey
プロパティを読み取ろうとすると、次のコード行の NullReferenceException
が表示されます。 もちろん、前のチュートリアルでは認証されたユーザーのみがこのフォルダー内の ASP.NET リソースにアクセスできるように、 URL 承認を構成したため、Membership.GetUser()
が AdditionalUserInfo.aspx
ページで null
値を返す心配はありません。 匿名アクセスが許可されているページで現在ログオンしているユーザーに関する情報にアクセスする必要がある場合は、プロパティを参照する前に、GetUser()
メソッドから null MembershipUser
以外のオブジェクトが返されるようにする必要があります。
ブラウザーから AdditionalUserInfo.aspx
ページにアクセスすると、UserProfiles
テーブルに行を追加していないため、空白のページが表示されます。 手順 6 では、CreateUserWizard コントロールをカスタマイズして、新しいユーザー アカウントの作成時に UserProfiles
テーブルに新しい行を自動的に追加する方法について説明します。 ただし、現時点では、テーブルにレコードを手動で作成する必要があります。
Visual Studio のデータベース エクスプローラーに移動し、[テーブル] フォルダーを展開します。 aspnet_Users
テーブルを右クリックし、[テーブル データの表示] を選択してテーブル内のレコードを表示します。UserProfiles
テーブルに対しても同じことを行います。 図 11 は、垂直方向に並べて表示された場合のこれらの結果を示しています。 私のデータベースには現在、Bruce、Fred、Tito の aspnet_Users
レコードがありますが、UserProfiles
テーブルにはレコードがありません。
図 11: テーブル aspnet_Users
および UserProfiles
の内容が表示されます (クリックするとフルサイズの画像を表示します)
HomeTown
、HomepageUrl
、Signature
フィールドの値を手動で入力して、UserProfiles
テーブルに新しいレコードを追加します。 新しい UserProfiles
レコードで有効な UserId
値を取得する最も簡単な方法は、aspnet_Users
テーブル内の特定のユーザー アカウントから UserId
フィールドを選択し、それをコピーして UserProfiles
の UserId
フィールドに貼り付けることです。 図 12 は、Bruce の新しいレコードが追加された後の UserProfiles
テーブルを示しています。
図 12: Bruce のレコードが UserProfiles
に追加されました (クリックするとフルサイズの画像を表示します)
AdditionalUserInfo.aspx
ページに戻り、Bruce としてログインします。 図 13 に示すように、Bruce の設定が表示されます。
図 13: 現在アクセスしているユーザーが自分の設定を表示する (クリックするとフルサイズの画像を表示します)
Note
次に進み、各メンバーシップ ユーザーの UserProfiles
テーブルにレコードを手動で追加します。 手順 6 では、CreateUserWizard コントロールをカスタマイズして、新しいユーザー アカウントの作成時に UserProfiles
テーブルに新しい行を自動的に追加する方法について説明します。
手順 3: ユーザーが自分のホーム タウン、ホームページ、署名を編集できるようにする
この時点で、現在ログインしているユーザーはホーム タウン、ホームページ、署名の設定を表示できますが、まだ変更することはできません。 データを編集できるように DetailsView コントロールを更新しましょう。
最初に、実行する UPDATE
ステートメントおよび対応するパラメーターを指定し、SqlDataSource の UpdateCommand
を追加する必要があります。 SqlDataSource を選択し、[プロパティ] ウィンドウで、UpdateQuery プロパティの横にある省略記号をクリックして、[コマンドおよびパラメーター エディター] ダイアログ ボックスを表示します。 テキスト ボックスに次の UPDATE
ステートメントを入力します。
UPDATE UserProfiles SET
HomeTown = @HomeTown,
HomepageUrl = @HomepageUrl,
Signature = @Signature
WHERE UserId = @UserId
次に、[パラメーターの更新] ボタンをクリックすると、UPDATE
ステートメント内の各パラメーターについて、SqlDataSource コントロールの UpdateParameters
コレクションにパラメーターが作成されます。 すべてのパラメーターのソースを [なし] に設定したまま、[OK] ボタンをクリックしてダイアログ ボックスを完了します。
図 14: SqlDataSource の UpdateCommand
、UpdateParameters
を指定する (クリックするとフルサイズの画像を表示します)
SqlDataSource コントロールに行った追加により、DetailsView コントロールで編集をサポートできるようになりました。 DetailsView のスマート タグで、[編集を有効にする] チェック ボックスをオンにします。 これにより、ShowEditButton
プロパティが True に設定された CommandField がコントロールの Fields
コレクションに追加されます。 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
に対応するレコードを持つユーザーでアクセスすると、ユーザーの設定が編集可能なインターフェイスに表示されます。
図 15: DetailsView が編集可能なインターフェイスをレンダリングします (クリックするとフルサイズの画像を表示します)
値を変更し、[更新] ボタンをクリックしてみてください。 何も起こっていないかのように見えます。 ポストバックが発生し、値はデータベースに保存されますが、保存が発生したことを示す視覚的なフィードバックはありません。
これを解決するには、Visual Studio に戻り、DetailsView の上にラベル コントロールを追加します。 ID
を SettingsUpdatedMessage
に、Text
プロパティ を "設定が更新されました" に、設定し、Visible
および EnableViewState
プロパティを 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: 設定が更新されたときに短いメッセージが表示されます (クリックするとフルサイズの画像を表示します)
Note
DetailsView コントロールの編集インターフェイスには、まだ多くの改善点があります。 標準サイズのテキスト ボックスを使用しますが、署名フィールドはおそらく複数行のテキスト ボックスである必要があります。 RegularExpressionValidator を使用して、ホームページの URL が入力された場合、それが "http://" または "https://" で始まっていることを確認する必要があります。 さらに、DetailsView コントロールでは DefaultMode
プロパティが Edit
に設定 されているため、[キャンセル] ボタンは機能しません。 削除するか、クリックすると、ユーザーを他のページ (~/Default.aspx
など) にリダイレクトする必要があります。 これらの改善は、読者の演習として残します。
マスター ページの AdditionalUserInfo.aspx
ページへのリンクの追加
現在、Web サイトには 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>
lnkUpdateSettings
HyperLink コントロールが LoggedInTemplate
に追加されていることに注意してください。 このリンクを設定すると、認証されたユーザーはすぐに、ホーム タウン、ホームページ、署名の設定を表示および変更できるページに移動できます。
手順 4: 新しいゲストブック コメントを追加する
Guestbook.aspx
ページでは、認証されたユーザーがゲストブックを表示し、コメントを残すことができます。 まず、新しいゲストブックコメントを追加するインターフェイスを作成します。
Visual Studio で Guestbook.aspx
ページを開き、2 つの TextBox コントロール (新しいコメントの件名用と本文用) で構成されるユーザー インターフェイスを構築します。 最初の TextBox コントロールの ID
プロパティを Subject
、そのColumns
プロパティを 40に設定し、2 番目の ID
を Body
、その TextMode
を MultiLine
に設定し、その Width
プロパティおよび Rows
プロパティをそれぞれ "95%" と 8 に設定します。 ユーザー インターフェイスを完成させるために、PostCommentButton
と名付けた Button Web コントロールを追加し、その Text
プロパティを "Post Your Comment" に設定します。
各ゲストブックのコメントには件名と本文が必要であるため、各 テキストボックスに RequiredFieldValidator を追加します。 ValidationGroup
これらのコントロールのプロパティを "EnterComment" に設定します。同様に、コントロールの ValidationGroup
プロパティを "EnterComment" に設定 PostCommentButton
します。 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
テーブルに新しいレコードを挿入します。 これは、次に示すようにさまざまな方法で実現できます。ボタンの Click
イベント ハンドラーに ADO.NET コードを記述します。または、ページに SqlDataSource コントロールを追加し、InsertCommand
を構成した後 Click
のイベント ハンドラーから Insert
メソッドを呼び出します。または、新しいゲストブック コメントを挿入する中間層を構築し、Click
のイベント ハンドラーからこの機能を呼び出します。 手順 3 で SqlDataSource の使い方を見たので、ここでは ADO.NET コードを使用します。
Note
Microsoft SQL Server データベースからプログラムでデータにアクセスするために使用される ADO.NET クラスは、名前空間にあります System.Data.SqlClient
。 この名前空間をページの分離コード クラス (つまり using System.Data.SqlClient;
) にインポートする必要がある場合があります。
PostCommentButton
の Click
イベントのイベント ハンドラーを作成し、次のコードを追加します。
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
TextBox の Text
がクリアされることで、ユーザーの値がポストバック全体で保持されるのを防ぎます。
先に進み、ブラウザーでこのページをテストします。 このページは Membership
フォルダー内にありますので、匿名の訪問者はアクセスできません。 そのため、まだログオンしていない場合は、最初にログオンする必要があります。 Subject
TextBox と Body
TextBox に値を入力し、PostCommentButton
ボタンをクリックします。 これにより、新しいレコードが GuestbookComments
に追加されます。 ポストバック時に、指定した件名と本文がテキストボックスから消去されます。
PostCommentButton
ボタンをクリックした後、コメントがゲストブックに追加されたことを示す視覚的なフィードバックは発生しません。 既存のゲストブックコメントを表示するために、このページを更新する必要があります。これは手順 5 で行います。 これを完了すると、追加されたコメントがコメントの一覧に表示され、適切な視覚的フィードバックが行われます。 ここでは、GuestbookComments
テーブルの内容を調べて、ゲストブックのコメントが保存されたことを確認します。
図 17 は、2 つのコメントが投稿された後の GuestbookComments
テーブルの内容を示しています。
図 17: GuestbookComments
テーブルにゲストブックのコメントを表示できます (クリックするとフルサイズの画像を表示します)
Note
ユーザーが潜在的に危険なマークアップ (HTML など) を含むゲストブック コメントを挿入しようとすると、ASP.NET が HttpRequestValidationException
をスローします。 この例外の詳細、スローされる理由、および危険な可能性のある値の送信をユーザーに許可する方法については、要求の検証に関するホワイトペーパーを参照してください。
手順 5: 既存のゲストブックのコメントを一覧表示する
コメントを残すことに加え、Guestbook.aspx
ページにアクセスするユーザーもゲストブックの既存のコメントを閲覧できる必要があります。 これを実現するには、ページの下部にCommentList
という名前の ListView コントロールを追加します。
Note
ListView コントロールは、ASP.NET バージョン 3.5 で初めて導入されました。 これは、非常にカスタマイズ可能かつ柔軟なレイアウトで項目の一覧を表示するように設計されており、GridView のような組み込みの編集、挿入、削除、ページング、並べ替え機能も備わっています。 ASP.NET 2.0 を使用している場合は、代わりに DataList または Repeater コントロールを使用する必要があります。 ListView の使用方法の詳細については、Scott Guthrie のブログ エントリ「asp:ListView コントロール」および「ListView コントロールを使用したデータの表示」を参照してください。
ListView のスマート タグを開き、[データ ソースの選択] ドロップダウン リストからコントロールを新しいデータ ソースにバインドします。 手順 2 と同様に、データ ソース構成ウィザードが起動します。 [データベース] アイコンを選択し、結果の SqlDataSource に CommentsDataSource
と名前を付け、[OK] をクリックします。 次に、ドロップダウン リストから接続文字列 SecurityTutorialsConnectionString
を選択し、[次へ] をクリックします。
手順 2 のこの時点で、ドロップダウン リストから UserProfiles
テーブルを選択し、返す列を選択して、クエリするデータを指定しました (図 9 を参照)。 ただし、今回は、GuestbookComments
のレコードだけでなく、投稿者のホーム タウン、ホームページ、署名、ユーザー名も返される SQL ステートメントを作成します。 そのため、[カスタム SQL ステートメントまたはストアド プロシージャを指定する] ラジオ ボタンを選択し、[次へ] をクリックします。
これにより、[カスタム ステートメントまたはストアド プロシージャの定義] 画面が表示されます。 [クエリ ビルダー] ボタンをクリックして、クエリをグラフィカルに作成します。 まず、クエリ ビルダーにより、クエリの対象となるテーブルを指定するように求められます。 テーブル GuestbookComments
、UserProfiles
、aspnet_Users
を 選択し、[OK] をクリックします。 これにより、3 つのテーブルすべてがデザイン サーフェイスに追加されます。 テーブル GuestbookComments
、UserProfiles
、aspnet_Users
には外部キー制約があるため、クエリ ビルダーは自動的に、これらのテーブルに JOIN
を行います。
あとは、返す列を指定すると完了です。 GuestbookComments
テーブルから、Subject
、Body
、CommentDate
列を選択すると、UserProfiles
テーブルが HomeTown
、HomepageUrl
、Signature
列を返し、aspnet_Users
が UserName
を返します。 また、SELECT
クエリの末尾に "ORDER BY CommentDate DESC
" を追加して、最新の投稿が最初に返されるようにします。 これらの選択を行った後、クエリ ビルダー インターフェイスは図 18 のスクリーン ショットのようになります。
図 18: 構築されたクエリ JOIN
の GuestbookComments
、UserProfiles
、aspnet_Users
テーブル (クリックするとフルサイズの画像を表示します)
[OK] をクリックしてクエリ ビルダー ウィンドウを閉じ、[カスタム ステートメントまたはストアド プロシージャの定義] 画面に戻ります。 [次へ] をクリックして [クエリのテスト] 画面に進み、[クエリのテスト] ボタンをクリックすると、クエリ結果を表示できます。 準備ができたら、[完了] をクリックしてデータ ソースの構成ウィザードを完了します。
手順 2 でデータ ソースの構成ウィザードを完了した時点で、関連付けられている DetailsView コントロールの Fields
コレクションが更新され、SelectCommand
から返される各列の BoundField が含まれるようになりました。 ただし、ListView は変更されません。レイアウトを定義する必要があります。 ListView のレイアウトは、宣言型マークアップで構築するか、スマート タグの [ListView の構成] オプションから手動で構築することができます。 私は普段、手動でマークアップを定義することを好みますが、あなたにとって最も自然な方法を使用してください。
私は自分の ListView コントロールに、次の LayoutTemplate
、ItemTemplate
、ItemSeparatorTemplate
を使用しました。
<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
の結果となるマークアップは、LayoutTemplate
の itemPlaceholder
コントロールに配置されます。 LayoutTemplate
には、itemPlaceholder
の他に、DataPager コントロールが含まれています。このコントロールでは、ListView がページあたり 10 件のゲストブック コメント (既定) のみを表示するように制限され、ページング インターフェイスがレンダリングされます。
ItemTemplate
では、各ゲストブック コメントの件名が <h4>
要素に表示され、本文が件名の下に表示されます。 本文の表示に使用される構文は、Eval("Body")
データバインド構文から返されるデータを受け取り、それを文字列に変換し、改行を <br />
要素で置き換えることに注意してください。 空白は HTML で無視されるため、コメントの送信時に入力された改行を表示するには、この変換が必要です。 ユーザーの署名は、本文の下に斜体で表示され、その後にユーザーのホーム タウン、ホームページへのリンク、コメントが作成された日時、コメントを残したユーザーのユーザー名が表示されます。
少し時間をとり、ブラウザーでページを表示してください。 手順 5 でゲストブックに追加したコメントがここに表示されます。
図 19: Guestbook.aspx
にゲストブックのコメントが表示されるようになりました (クリックするとフルサイズの画像を表示します)
ゲストブックに新しいコメントを追加してみてください。 PostCommentButton
ボタンをクリックすると、ページがポストバックを行い、コメントがデータベースに追加されますが、ListView コントロールは新しいコメントを表示するための更新を行いません。 これは、次のいずれかの方法で修正できます。
PostCommentButton
ボタンのClick
イベント ハンドラーを更新し、ListView コントロールのDataBind()
メソッドがデータベースに新しいコメントが挿入された後に呼び出されるようにします。または、- ListView コントロールの
EnableViewState
プロパティをfalse
に設定します。 この方法が有効な理由は、コントロールのビューの状態を無効にすることで、すべてのポストバックの基になるデータに再バインドする必要があるためです。
このチュートリアルからダウンロードできるチュートリアル Web サイトは、両方の手法を示しています。 Click
イベント ハンドラーには、false
に対する ListView コントロールの EnableViewState
プロパティと、プログラムでデータを ListView に再バインドするために必要なコードがありますが、コメントアウトされています。
Note
現在の AdditionalUserInfo.aspx
ページでは、ユーザーがホーム タウン、ホームページ、署名の設定を表示および編集できます。 AdditionalUserInfo.aspx
を更新し、ログインしたユーザーのゲストブック コメントを表示すると便利でしょう。 つまり、ユーザーは自分の情報を調べて変更するだけでなく、AdditionalUserInfo.aspx
ページにアクセスして、過去に行った自分のゲストブックのコメントを確認できます。 これは興味を持っている読者のための演習として残します。
手順 6: CreateUserWizard コントロールをカスタマイズしてホーム タウン、ホームページ、署名のインターフェイスを含める
Guestbook.aspx
ページで使用される SELECT
クエリは、INNER JOIN
を使用して GuestbookComments
、UserProfiles
、aspnet_Users
テーブルの関連するレコードを結合します。 UserProfiles
にレコードのないユーザーがゲストブック コメントを作成しても、INNER JOIN
は UserProfiles
および aspnet_Users
に一致するレコードがある場合のみ GuestbookComments
を返すため、ListView にコメントは表示されません。 手順 3 で説明したように、ユーザーのレコードが UserProfiles
にない場合、そのユーザーは AdditionalUserInfo.aspx
ページでユーザー設定を閲覧または編集することはできません。
言うまでもなく、設計上の決定により、Membership システムのすべてのユーザー アカウントに一致するレコードが UserProfiles
テーブルに含まれている必要があります。 必要なのは、CreateUserWizard を使用して新しいメンバーシップ ユーザー アカウントが作成されるたびに、対応するレコードを UserProfiles
に追加することです。
「ユーザー アカウントの作成」チュートリアルで説明したように、新しいメンバーシップ ユーザー アカウントが作成された後、CreateUserWizard コントロールによってCreatedUser
イベントが発生します。 このイベントのイベント ハンドラーを作成し、先ほど作成されたユーザーの UserId を取得した後、UserProfiles
テーブルにレコードを挿入します (HomeTown
、HomepageUrl
、Signature
列には、既定値が設定されます)。 さらに、追加のテキストボックスを含めて CreateUserWizard コントロールのインターフェイスをカスタマイズすることで、ユーザーにこれらの値の入力を求めることができます。
まず、CreatedUser
イベント ハンドラーの UserProfiles
テーブルに既定値を使用して新しい行を追加する方法を見てみましょう。 その後、CreateUserWizard コントロールのユーザー インターフェイスをカスタマイズして、新しいユーザーのホーム タウン、ホームページ、署名を収集する追加のフォーム フィールドを含める方法について説明します。
UserProfiles
に規定の行を追加する
ユーザー アカウントの作成チュートリアルでは、Membership
フォルダー内の CreatingUserAccounts.aspx
ページに CreateUserWizard コントロールを追加しました。 CreateUserWizard コントロールでユーザー アカウントの作成時に UserProfiles
テーブルにレコードを追加するには、CreateUserWizard コントロールの機能を更新する必要があります。 このチュートリアルでは、これらの変更を CreatingUserAccounts.aspx
ページに加えるのではなく、新しい CreateUserWizard コントロールを EnhancedCreateUserWizard.aspx
ページに追加し、変更を行います。
Visual Studio で EnhancedCreateUserWizard.aspx
ページを開き、ツールボックスからページに CreateUserWizard コントロールをドラッグします。 CreateUserWizard コントロールの ID
プロパティを NewUserWizard
に設定します。 ユーザー アカウントの作成チュートリアルで説明したように、CreateUserWizard の既定のユーザー インターフェイスは、必要な情報を訪問者に求めます。 この情報が提供されると、コントロールは内部的に Membership フレームワークで新しいユーザー アカウントを作成します。ユーザーがコードを記述する必要はありません。
CreateUserWizard コントロールでは、ワークフロー中に多数のイベントが発生します。 訪問者が要求情報を入力してフォームを送信すると、CreateUserWizard コントロールは最初に CreatingUser
イベントを発生させます。 作成プロセス中に問題が発生した場合、CreateUserError
イベントが発生します。ただし、ユーザーが正常に作成された場合は、CreatedUser
イベントが発生します。 ユーザー アカウントの作成チュートリアルでは、指定されたユーザー名に先頭または末尾のスペースが含まれていないことを確認し、そのユーザー名がパスワードのどこにも含まれないようにするために、CreatingUser
イベントのイベント ハンドラーを作成しました。
先ほど作成したユーザーの UserProfiles
テーブルに行を追加するには、CreatedUser
イベントのイベント ハンドラーを作成する必要があります。 CreatedUser
イベントが発生した時点で、ユーザー アカウントは既に Membership フレームワークに作成されているため、そのアカウントの 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
に割り当てます。これにより、HomeTown
、HomepageUrl
、Signature
フィールドにデータベースの NULL
値が挿入されます。
ブラウザーから EnhancedCreateUserWizard.aspx
ページにアクセスし、新しいユーザー アカウントを作成します。 その後、Visual Studio に戻り、aspnet_Users
テーブルと UserProfiles
テーブルの内容を確認します (以前と同様に。図 12 参照)。 aspnet_Users
に新しいユーザー アカウントが表示され、対応する UserProfiles
行が表示されます (HomeTown
、HomepageUrl
、Signature
には NULL
の値)。
図 20: 新しいユーザー アカウントと UserProfiles
レコードが追加されました (クリックするとフルサイズの画像を表示します)
訪問者が新しいアカウント情報を入力し、[ユーザーの作成] ボタンをクリックすると、ユーザー アカウントが作成され、UserProfiles
テーブルに行が追加されます。 次に、CreateUserWizard に CompleteWizardStep
が表示され、成功メッセージと [続行] ボタンが表示されます。 [続行] ボタンをクリックするとポストバックが発生しますが、アクションは実行されません。ユーザーは EnhancedCreateUserWizard.aspx
ページに残ったままです。
CreateUserWizard コントロールの ContinueDestinationPageUrl
プロパティを介して、[続行] ボタンがクリックされたときに、ユーザーが移動する先の URL を指定できます。 ContinueDestinationPageUrl
プロパティを "~/Membership/AdditionalUserInfo.aspx" に設定します。 これにより、新しいユーザーが AdditionalUserInfo.aspx
に移動し、設定を表示、更新できるようになります。
CreateUserWizard のインターフェイスをカスタマイズして、新しいユーザーのホーム タウン、ホームページ、署名を要求する
CreateUserWizard コントロールの既定のインターフェイスは、ユーザー名、パスワード、電子メールなどの主要なユーザー アカウント情報のみを収集する必要があるシンプルなアカウント作成シナリオには十分です。 しかし、アカウントの作成中に、訪問者にホームタウン、ホームページ、署名の入力を求める場合はどうでしょうか。 サインアップ時に追加情報を収集するように CreateUserWizard コントロールのインターフェイスをカスタマイズできます。この情報は、CreatedUser
のイベント ハンドラーで使用して、基になるデータベースに追加のレコードを挿入できます。
CreateUserWizard コントロールは、ASP.NET ウィザード コントロールを拡張します。このコントロールでは、ページ開発者が順序付けされた WizardSteps
を定義できます。 ウィザード コントロールは、アクティブなステップをレンダリングし、訪問者がこれらの手順を移動できるようにするナビゲーション インターフェイスを提供します。 ウィザード コントロールは、長いタスクをいくつかの短い手順に分割するのに最適です。 ウィザード コントロールの詳細については、「ASP.NET 2.0 ウィザード コントロールを使用したステップ バイ ステップ ユーザー インターフェイスの作成」を参照してください。
CreateUserWizard コントロールの既定のマークアップでは、2 つの WizardSteps
を定義します: CreateUserWizardStep
と CompleteWizardStep
。
<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>
1 つ目の WizardStep
である CreateUserWizardStep
は、ユーザー名、パスワード、電子メールなどを求めるインターフェイスをレンダリングします。 訪問者がこの情報を入力し、[ユーザーの作成] をクリックすると、CompleteWizardStep
が表示され、成功メッセージと [続行] ボタンが表示されます。
CreateUserWizard コントロールのインターフェイスをカスタマイズして、追加のフォーム フィールドを含めるには、次の操作を行います。
新しく 1 つ以上の
WizardStep
を作成し、追加のユーザー インターフェイス要素を含めます。 CreateUserWizard に新規WizardStep
を追加するには、スマート タグの [WizardSteps
の追加と削除] リンクをクリックしてWizardStep
コレクション エディターを起動します。 そこから、ウィザードの手順を追加、削除、並べ替えを行えます。 このチュートリアルではこのアプローチを使用します。CreateUserWizardStep
を編集可能なWizardStep
に変換します。これにより、CreateUserWizardStep
がWizardStep
に置き換えられ、そのマークアップはCreateUserWizardStep
に一致するユーザー インターフェイスを定義します。CreateUserWizardStep
をWizardStep
に変換すると、コントロールの位置を変更したり、このステップにユーザー インターフェイス要素を追加したりできます。CreateUserWizardStep
またはCompleteWizardStep
を編集可能なWizardStep
に変換するには、コントロールのスマート タグから [ユーザー ステップの作成のカスタマイズ] または [ステップ全体のカスタマイズ] リンクをクリックします。上記の 2 つのオプションを組み合わせて使用します。
注意すべき重要な点の 1 つは、CreateUserWizardStep
内から [ユーザーの作成] ボタンをクリックしたときに、CreateUserWizard コントロールがユーザー アカウントの作成プロセスを実行することです。 CreateUserWizardStep
の後にさらに WizardStep
があるかどうかは関係ありません。
CreateUserWizard コントロールにカスタム WizardStep
を追加して追加のユーザー入力を収集する場合、カスタム WizardStep
は CreateUserWizardStep
の前にも後にも置けます。 CreateUserWizardStep
の前に置く場合は、追加のユーザー入力は CreatedUser
イベント ハンドラーで使用可能なカスタム WizardStep
から収集されます。 ただし、カスタム WizardStep
が CreateUserWizardStep
の後にある場合は、カスタム WizardStep
が表示されるまでに、新しいユーザー アカウントが作成済みであり、CreatedUser
イベントが発生済みとなります。
図 21 は、追加された WizardStep
が CreateUserWizardStep
よりも前にある場合のワークフローを示しています。 追加のユーザー情報は CreatedUser
イベントが発生する時点で収集されているため、これらの入力を取得し、DBNull.Value
ではなく、INSERT
ステートメントのパラメーター値に使用するように CreatedUser
イベント ハンドラーを更新するだけで済みます。
図 21: 追加の WizardStep
がCreateUserWizardStep
に先行する場合の CreateUserWizard ワークフロー (クリックするとフルサイズの画像を表示します)
ただし、CreateUserWizardStep
の後にカスタム WizardStep
を配置した場合は、ユーザー アカウント作成プロセスはユーザーがホーム タウン、ホームページ、または署名を入力することになる前に行われます。 このような場合は、図 22 に示すように、ユーザー アカウントの作成後に、この追加情報をデータベースに挿入する必要があります。
図 22: 追加の WizardStep
がCreateUserWizardStep
の後になる場合の CreateUserWizard ワークフロー (クリックするとフルサイズの画像を表示します)
図 22 に示すワークフローは、手順 2 が完了するまで、UserProfiles
テーブルへのレコードの挿入を待機します。 ただし、手順 1 の後に訪問者がブラウザーを閉じると、ユーザー アカウントが作成された状態に達しますが、UserProfiles
にレコードは追加されません。 回避策の 1 つは、NULL
または既定値を持つレコードを CreatedUser
イベント ハンドラーの UserProfiles
に挿入してから (手順 1 後に発生)、手順 2 の完了後にこのレコードを更新することです。 これにより、ユーザーが途中で登録プロセスを終了した場合でも、ユーザー アカウントの UserProfiles
レコードが追加されます。
このチュートリアルでは CreateUserWizardStep
の後、CompleteWizardStep
の前に発生する WizardStep
を新しく作成します。 まず WizardStep を配置して構成してから、コードを見てみましょう。
CreateUserWizard コントロールのスマート タグから、[WizardStep
の追加と削除] を選択すると、[WizardStep
コレクション エディター] ダイアログが表示されます。 WizardStep
を新しく追加し、その ID
を UserSettings
、Title
を "設定"、StepType
を Step
に設定します。 次に、図 23 に示すように、CreateUserWizardStep
("新しいアカウントにサインアップ") の後であり、CompleteWizardStep
("完了") の前になるように配置します。
図 23: CreateUserWizard コントロールに新規 WizardStep
を追加する (クリックするとフルサイズの画像を表示します)
[OK] をクリックして、[WizardStep
コレクション エディター] ダイアログを閉じます。 新しい 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>
要素に注意してください。 ここでは、新しいユーザーのホーム タウン、ホームページ、署名を収集するためのユーザー インターフェイスを追加する必要があります。 このコンテンツは、宣言構文またはデザイナーで入力できます。 デザイナーを使用するには、スマート タグのドロップダウン リストから [設定] ステップを選択して、デザイナーにステップを表示します。
Note
スマート タグのドロップダウン リストからステップを選択すると、開始ステップのインデックスを指定する CreateUserWizard コントロールの ActiveStepIndex
プロパティが更新されます。 そのため、このドロップダウン リストを使用してデザイナーの [設定] ステップを編集する場合は、ユーザーが初めて EnhancedCreateUserWizard.aspx
ページにアクセスしたときにこの手順が表示されるように、必ず "新しいアカウントにサインアップ" に戻してください。
"設定" ステップ内に、HomeTown
、HomepageUrl
、Signature
という 3 つの 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
に新しい行が追加されます。HomeTown
、HomepageUrl
、Signature
のデータベース NULL
値が使用されます。 ホーム タウン、ホームページ、署名に入力された値は使用されません。 結果は、UserProfiles
レコードを持つ新しいユーザー アカウントであり、フィールド HomeTown
、HomepageUrl
、Signature
はまだ入力されていない状態です。
ユーザーが入力したホーム タウン、ホームページ、署名の値を取得し、適切な 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 コントロールを参照する必要があります。 これを実現するには、最初に FindControl
メソッドを使用してプログラムで UserSettings WizardStep
を参照し、次にもう一度 WizardStep
内のテキストボックスを参照します。 テキストボックスが参照されたら、UPDATE
ステートメントを実行する準備が整います。 UPDATE
ステートメントには CreatedUser
イベント ハンドラーの INSERT
ステートメントと同じ数のパラメーターがありますが、ここでは、ユーザーが指定するホーム タウン、ホームページ、および署名の値を使用します。
このイベント ハンドラーが配置された状態で、ブラウザーから EnhancedCreateUserWizard.aspx
ページにアクセスし、ホーム タウン、ホームページ、署名の値を指定して新しいユーザー アカウントを作成します。 新しいアカウントを作成すると、AdditionalUserInfo.aspx
ページにリダイレクトされます。ここで、先ほど入力したホーム タウン、ホームページ、署名の情報が表示されます。
Note
ウェブサイトには現在、CreatingUserAccounts.aspx
と EnhancedCreateUserWizard.aspx
の 2 つのページで訪問者が新しいアカウントを作成できます。 Web サイトマップとログイン ページは CreatingUserAccounts.aspx
ページを指していますが、CreatingUserAccounts.aspx
ページではユーザーのホーム タウン、ホームページ、署名の情報が求められず、UserProfiles
に対応する行が追加されません。 したがって、この機能を提供するために CreatingUserAccounts.aspx
ページを更新するか、CreatingUserAccounts.aspx
ではなく EnhancedCreateUserWizard.aspx
を参照するようにサイトマップおよびログイン ページを更新します。 後者のオプションを選択する場合は、匿名ユーザーが EnhancedCreateUserWizard.aspx
ページにアクセスできるように、Membership
フォルダーの Web.config
ファイルを必ず更新してください。
まとめ
このチュートリアルでは、Membership フレームワーク内のユーザー アカウントに関連するデータをモデル化する手法について説明しました。 特に、ユーザー アカウントと一対多リレーションシップを共有するモデリング エンティティと、一対一リレーションシップを共有するデータについて説明しました。 さらに、SqlDataSource コントロールを使用した例や、ADO.NET コードを使用した他の例を使用して、この関連情報を表示、挿入、更新する方法を確認しました。
これで、ユーザー アカウントに目を向けたチュートリアルを終了します。 次のチュートリアルからは、ロールに注目します。 次のいくつかのチュートリアルでは、Roles フレームワーク、新しいロールの作成方法、ユーザーにロールを割り当てる方法、ユーザーが属するロールを判別する方法、ロールベースの承認を適用する方法について説明します。
プログラミングに満足!
もっと読む
この記事で説明したトピックの詳細については、次のリソースを参照してください。
- ASP.NET 2.0 でのデータへのアクセスと更新
- ASP.NET 2.0 ウィザード コントロール
- ASP.NET 2.0 Wizard コントロールを使用したステップ バイ ステップ ユーザー インターフェイスの作成
- カスタム DataSource コントロール パラメーターの作成
- CreateUserWizard コントロールのカスタマイズ
- DetailsView コントロールのクイックスタート
- ListView コントロールを使用したデータの表示
- ASP.NET 2.0 での検証コントロールの仕組み
- データの挿入、削除、編集
- ASP.NET でのフォームの検証
- カスタム ユーザー登録情報の収集
- ASP.NET 2.0 のプロファイル
- asp:ListView コントロール
- ユーザー プロファイルのクイックスタート
作成者について
複数の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジに取り組んでいます。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 Scott には、mitchell@4guysfromrolla.com または http://ScottOnWriting.NET のブログを介して連絡できます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 今後の MSDN の記事を確認することに関心がありますか? その場合は、mitchell@4GuysFromRolla.com までご一報ください。