Armazenar informações de usuário adicionais (C#)

por Scott Mitchell

Observação

Desde que este artigo foi escrito, os provedores de associação de ASP.NET foram substituídos por ASP.NET Identity. É altamente recomendável atualizar aplicativos para usar a plataforma ASP.NET Identity em vez dos provedores de associação apresentados no momento em que este artigo foi escrito. ASP.NET Identity tem várias vantagens sobre o sistema de associação ASP.NET, incluindo :

  • Melhor desempenho
  • Extensibilidade e testabilidade aprimoradas
  • Suporte para OAuth, OpenID Connect e autenticação de dois fatores
  • Suporte à identidade baseada em declarações
  • Melhor interoperabilidade com ASP.Net Core

Baixar código ou baixar PDF

Neste tutorial, responderemos a essa pergunta criando um aplicativo de guestbook muito rudimentar. Ao fazer isso, examinaremos diferentes opções para modelar informações do usuário em um banco de dados e, em seguida, veremos como associar esses dados às contas de usuário criadas pela estrutura De associação.

Introdução

ASP. A estrutura de Associação do NET oferece uma interface flexível para gerenciar usuários. A API de Associação inclui métodos para validar credenciais, recuperar informações sobre o usuário conectado no momento, criar uma nova conta de usuário e excluir uma conta de usuário, entre outros. Cada conta de usuário na estrutura Associação contém apenas as propriedades necessárias para validar credenciais e executar tarefas essenciais relacionadas à conta de usuário. Isso é evidenciado pelos métodos e propriedades da MembershipUser classe , que modela uma conta de usuário na estrutura Associação. Essa classe tem propriedades como UserName, Emaile IsLockedOut, e métodos como GetPassword e UnlockUser.

Geralmente, os aplicativos precisam armazenar informações adicionais do usuário não incluídas na estrutura Associação. Por exemplo, um varejista online pode precisar permitir que cada usuário armazene seus endereços de envio e cobrança, informações de pagamento, preferências de entrega e número de telefone de contato. Além disso, cada ordem no sistema está associada a uma conta de usuário específica.

A MembershipUser classe não inclui propriedades como PhoneNumber ou DeliveryPreferences ou PastOrders. Então, como acompanhar as informações de usuário necessárias para o aplicativo e integrá-la à estrutura de associação? Neste tutorial, responderemos a essa pergunta criando um aplicativo de guestbook muito rudimentar. Ao fazer isso, examinaremos diferentes opções para modelar informações do usuário em um banco de dados e, em seguida, veremos como associar esses dados às contas de usuário criadas pela estrutura De associação. Vamos começar!

Etapa 1: Criando o modelo de dados do aplicativo Guestbook

Há uma variedade de técnicas que podem ser empregadas para capturar informações do usuário em um banco de dados e associá-la às contas de usuário criadas pela estrutura associação. Para ilustrar essas técnicas, precisaremos aumentar o aplicativo Web do tutorial para que ele capture algum tipo de dados relacionados ao usuário. (Atualmente, o modelo de dados do aplicativo contém apenas as tabelas de serviços de aplicativo necessárias para o SqlMembershipProvider.)

Vamos criar um aplicativo de guestbook muito simples em que um usuário autenticado pode deixar um comentário. Além de armazenar comentários de guestbook, vamos permitir que cada usuário armazene sua cidade natal, home page e assinatura. Se fornecido, a cidade natal, a home page e a assinatura do usuário aparecerão em cada mensagem que ele deixou no livro de convidados.

Adicionando aGuestbookCommentstabela

Para capturar os comentários do guestbook, precisamos criar uma tabela de banco de dados chamada GuestbookComments que tenha colunas como CommentId, Subject, Bodye CommentDate. Também precisamos ter cada registro na GuestbookComments tabela referenciando o usuário que deixou o comentário.

Para adicionar essa tabela ao nosso banco de dados, acesse o banco de dados Explorer no Visual Studio e faça uma busca detalhada no SecurityTutorials banco de dados. Clique com o botão direito do mouse na pasta Tabelas e escolha Adicionar Nova Tabela. Isso apresenta uma interface que nos permite definir as colunas para a nova tabela.

Adicionar uma nova tabela ao banco de dados SecurityTutorials

Figura 1: Adicionar uma nova tabela ao SecurityTutorials banco de dados (clique para exibir a imagem em tamanho real)

Em seguida, defina as GuestbookCommentscolunas de . Comece adicionando uma coluna chamada CommentId do tipo uniqueidentifier. Esta coluna identificará exclusivamente cada comentário no guestbook, portanto, não NULL permitirá e o marcará como a chave primária da tabela. Em vez de fornecer um valor para o CommentId campo em cada INSERT, podemos indicar que um novo uniqueidentifier valor deve ser gerado automaticamente para esse campo, INSERT definindo o valor padrão da coluna como NEWID(). Depois de adicionar esse primeiro campo, marcando-o como a chave primária e definindo seu valor padrão, sua tela deverá ser semelhante à captura de tela mostrada na Figura 2.

Adicionar uma coluna primária chamada CommentId

Figura 2: Adicionar uma coluna primária chamada CommentId (clique para exibir a imagem em tamanho real)

Em seguida, adicione uma coluna chamada Subject de tipo nvarchar(50) e uma coluna chamada Body do tipo nvarchar(MAX), não permitindo NULL s em ambas as colunas. Depois disso, adicione uma coluna chamada CommentDate do tipo datetime. Não permitir NULL e definir o CommentDate valor padrão da coluna como getdate().

Tudo o que resta é adicionar uma coluna que associe uma conta de usuário a cada comentário de guestbook. Uma opção seria adicionar uma coluna chamada UserName do tipo nvarchar(256). Essa é uma opção adequada ao usar um provedor de associação diferente do SqlMembershipProvider. No entanto, ao usar o SqlMembershipProvider, como estamos nesta série de tutoriais, não há garantia de que a UserName coluna na aspnet_Users tabela seja exclusiva. A aspnet_Users chave primária da tabela é UserId e é do tipo uniqueidentifier. Portanto, a GuestbookComments tabela precisa de uma coluna chamada UserId de tipo uniqueidentifier (não permitindo NULL valores). Vá em frente e adicione esta coluna.

Observação

Como discutimos no tutorial Criando o esquema de associação no SQL Server, a estrutura Associação foi projetada para habilitar vários aplicativos Web com contas de usuário diferentes para compartilhar o mesmo repositório de usuários. Ele faz isso particionando contas de usuário em aplicativos diferentes. E embora cada nome de usuário tenha a garantia de ser exclusivo em um aplicativo, o mesmo nome de usuário pode ser usado em aplicativos diferentes usando o mesmo repositório de usuários. Há uma restrição composta UNIQUE na tabela nos UserName campos e ApplicationId , mas não uma apenas no UserNameaspnet_Users campo. Consequentemente, é possível que a tabela aspnet_Users tenha dois (ou mais) registros com o mesmo UserName valor. No entanto, há uma UNIQUE restrição no aspnet_Users campo da UserId tabela (já que ela é a chave primária). Uma UNIQUE restrição é importante porque sem ela não é possível estabelecer uma restrição de chave estrangeira entre as GuestbookComments tabelas e aspnet_Users .

Depois de adicionar a UserId coluna, salve a tabela clicando no ícone Salvar na Barra de Ferramentas. Nomeie a nova tabela GuestbookCommentscomo .

Temos um último problema a ser atendido com a GuestbookComments tabela: precisamos criar uma restrição de chave estrangeira entre a GuestbookComments.UserId coluna e a aspnet_Users.UserId coluna. Para conseguir isso, clique no ícone Relação na Barra de Ferramentas para iniciar a caixa de diálogo Relações de Chave Estrangeira. (Como alternativa, você pode iniciar essa caixa de diálogo acessando o menu Tabela Designer e escolhendo Relações.)

Clique no botão Adicionar no canto inferior esquerdo da caixa de diálogo Relações de Chave Estrangeira. Isso adicionará uma nova restrição de chave estrangeira, embora ainda precisemos definir as tabelas que participam da relação.

Usar a caixa de diálogo Relações de Chave Estrangeira para gerenciar restrições de chave estrangeira de uma tabela

Figura 3: usar a caixa de diálogo Relações de Chave Estrangeira para gerenciar restrições de chave estrangeira de uma tabela (clique para exibir a imagem em tamanho real)

Em seguida, clique no ícone de reticências na linha "Especificações de Tabela e Colunas" à direita. Isso iniciará a caixa de diálogo Tabelas e Colunas, da qual podemos especificar a tabela e a coluna de chave primária e a coluna de chave estrangeira da GuestbookComments tabela. Em particular, selecione aspnet_Users e UserId como a tabela e a coluna da chave primária e UserId na GuestbookComments tabela como a coluna de chave estrangeira (consulte a Figura 4). Depois de definir as tabelas e colunas de chave primária e estrangeira, clique em OK para retornar à caixa de diálogo Relações de Chave Estrangeira.

Estabelecer uma restrição de chave estrangeira entre as tabelas aspnet_Users e GuesbookComments

Figura 4: Estabelecer uma restrição de chave estrangeira entre as aspnet_Users tabelas e GuesbookComments (clique para exibir a imagem em tamanho real)

Neste ponto, a restrição de chave estrangeira foi estabelecida. A presença dessa restrição garante a integridade relacional entre as duas tabelas garantindo que nunca haverá uma entrada de guestbook referente a uma conta de usuário inexistente. Por padrão, uma restrição de chave estrangeira não permitirá que um registro pai seja excluído se houver registros filho correspondentes. Ou seja, se um usuário fizer um ou mais comentários de guestbook e tentarmos excluir essa conta de usuário, a exclusão falhará, a menos que seus comentários da pasta de convidado sejam excluídos primeiro.

Restrições de chave estrangeira podem ser configuradas para excluir automaticamente os registros filho associados quando um registro pai é excluído. Em outras palavras, podemos configurar essa restrição de chave estrangeira para que as entradas da pasta de convidado de um usuário sejam excluídas automaticamente quando sua conta de usuário for excluída. Para fazer isso, expanda a seção "Especificação INSERT e UPDATE" e defina a propriedade "Excluir Regra" como Cascade.

Configurar a restrição de chave estrangeira para exclusões em cascata

Figura 5: Configurar a restrição de chave estrangeira para exclusões em cascata (clique para exibir a imagem em tamanho real)

Para salvar a restrição de chave estrangeira, clique no botão Fechar para sair das Relações de Chave Estrangeira. Em seguida, clique no ícone Salvar na Barra de Ferramentas para salvar a tabela e essa relação.

Armazenando a cidade inicial, a home page e a assinatura do usuário

A GuestbookComments tabela ilustra como armazenar informações que compartilham uma relação um-para-muitos com contas de usuário. Como cada conta de usuário pode ter um número arbitrário de comentários associados, essa relação é modelada criando uma tabela para conter o conjunto de comentários que inclui uma coluna que vincula cada comentário a um usuário específico. Ao usar o SqlMembershipProvider, esse link é melhor estabelecido criando uma coluna chamada UserId de tipo uniqueidentifier e uma restrição de chave estrangeira entre essa coluna e aspnet_Users.UserId.

Agora precisamos associar três colunas a cada conta de usuário para armazenar a cidade natal, a home page e a assinatura do usuário, que aparecerão em seus comentários de pasta de convidado. Há várias maneiras diferentes de fazer isso:

  • Adicionar novas colunas aoaspnet_UsersOuaspnet_MembershipTabelas. Eu não recomendaria essa abordagem porque ela modifica o esquema usado pelo SqlMembershipProvider. Essa decisão pode voltar para assombrá-lo na estrada. Por exemplo, e se uma versão futura do ASP.NET usar um esquema diferente SqlMembershipProvider . A Microsoft pode incluir uma ferramenta para migrar os dados do ASP.NET 2.0 SqlMembershipProvider para o novo esquema, mas se você tiver modificado o esquema ASP.NET 2.0 SqlMembershipProvider , essa conversão poderá não ser possível.

  • Use ASP. Estrutura de perfil do NET, definindo uma propriedade de perfil para a cidade natal, home page e assinatura. ASP.NET inclui uma estrutura de perfil projetada para armazenar dados adicionais específicos do usuário. Assim como a estrutura Associação, a estrutura De perfil é criada em cima do modelo de provedor. O .NET Framework é fornecido com um SqlProfileProvider sthat armazena dados de perfil em um banco de dados SQL Server. Na verdade, nosso banco de dados já tem a tabela usada pelo SqlProfileProvider (aspnet_Profile), pois ele foi adicionado quando adicionamos os serviços de aplicativo novamente no tutorial Criando o esquema de associação em SQL Server.
    O main benefício da estrutura de perfil é que ela permite que os desenvolvedores definam as propriedades de perfil no Web.config – nenhum código precisa ser gravado para serializar os dados de perfil de e para o armazenamento de dados subjacente. Em suma, é incrivelmente fácil definir um conjunto de propriedades de perfil e trabalhar com elas no código. No entanto, o sistema de perfil deixa muito a desejar quando se trata de controle de versão, portanto, se você tiver um aplicativo em que espera que novas propriedades específicas do usuário sejam adicionadas posteriormente ou que as existentes sejam removidas ou modificadas, a estrutura de perfil pode não ser a melhor opção. Além disso, o SqlProfileProvider armazena as propriedades de perfil de forma altamente desnormalizada, tornando quase impossível executar consultas diretamente nos dados do perfil (como quantos usuários têm uma cidade natal, Nova York).
    Para obter mais informações sobre a estrutura de perfil, consulte a seção "Leituras adicionais" no final deste tutorial.

  • Adicione essas três colunas a uma nova tabela no banco de dados e estabeleça uma relação um-para-um entre esta tabela easpnet_Users. Essa abordagem envolve um pouco mais de trabalho do que com a estrutura De perfil, mas oferece máxima flexibilidade em como as propriedades de usuário adicionais são modeladas no banco de dados. Essa é a opção que usaremos neste tutorial.

Criaremos uma nova tabela chamada UserProfiles para salvar a cidade natal, a home page e a assinatura de cada usuário. Clique com o botão direito do mouse na pasta Tabelas na janela Explorer banco de dados e escolha criar uma nova tabela. Nomeie a primeira coluna UserId e defina seu tipo uniqueidentifiercomo . Não permitir NULL valores e marcar a coluna como uma chave primária. Em seguida, adicione colunas chamadas: HomeTown do tipo nvarchar(50); HomepageUrl do tipo nvarchar(100); e Assinatura do tipo nvarchar(500). Cada uma dessas três colunas pode aceitar um NULL valor.

Criar a tabela UserProfiles

Figura 6: Criar a UserProfiles tabela (clique para exibir a imagem em tamanho real)

Salve a tabela e nomeie-a UserProfilescomo . Por fim, estabeleça uma restrição de chave estrangeira entre o UserProfiles campo da UserId tabela e o aspnet_Users.UserId campo . Como fizemos com a restrição de chave estrangeira entre as GuestbookComments tabelas e aspnet_Users , essa restrição é excluída em cascata. Como o UserId campo em UserProfiles é a chave primária, isso garante que não haverá mais de um registro na UserProfiles tabela para cada conta de usuário. Esse tipo de relação é conhecido como um para um.

Agora que temos o modelo de dados criado, estamos prontos para usá-lo. Nas Etapas 2 e 3, veremos como o usuário conectado no momento pode exibir e editar suas informações de cidade natal, home page e assinatura. Na Etapa 4, criaremos a interface para que os usuários autenticados enviem novos comentários ao guestbook e exibam os existentes.

Etapa 2: exibindo a cidade inicial, a home page e a assinatura do usuário

Há várias maneiras de permitir que o usuário conectado atualmente veja e edite sua cidade natal, home page e informações de assinatura. Poderíamos criar manualmente a interface do usuário com controles TextBox e Label ou poderíamos usar um dos controles da Web de dados, como o controle DetailsView. Para executar o banco de dados SELECT e UPDATE as instruções, podemos escrever ADO.NET código na classe code-behind da nossa página ou, como alternativa, empregar uma abordagem declarativa com o SqlDataSource. O ideal é que nosso aplicativo contenha uma arquitetura em camadas, que poderíamos invocar programaticamente da classe code-behind da página ou declarativamente por meio do controle ObjectDataSource.

Como esta série de tutoriais se concentra na autenticação de formulários, autorização, contas de usuário e funções, não haverá uma discussão completa sobre essas diferentes opções de acesso a dados ou por que uma arquitetura em camadas é preferencial em vez de executar instruções SQL diretamente da página ASP.NET. Vou percorrer usando um DetailsView e SqlDataSource – a opção mais rápida e fácil – mas os conceitos discutidos certamente podem ser aplicados a controles da Web alternativos e à lógica de acesso a dados. Para obter mais informações sobre como trabalhar com dados em ASP.NET, consulte minha série de tutoriais Trabalhando com Dados no ASP.NET 2.0 .

Abra a AdditionalUserInfo.aspx página na Membership pasta e adicione um controle DetailsView à página, definindo sua ID propriedade UserProfile como e limpando suas Width propriedades e Height . Expanda a Marca Inteligente do DetailsView e escolha associá-la a um novo controle da fonte de dados. Isso iniciará o Assistente de Configuração do DataSource (consulte a Figura 7). A primeira etapa solicita que você especifique o tipo de fonte de dados. Como vamos nos conectar diretamente ao SecurityTutorials banco de dados, escolha o ícone banco de dados, especificando o ID como UserProfileDataSource.

Adicionar um novo controle SqlDataSource chamado UserProfileDataSource

Figura 7: Adicionar um novo controle SqlDataSource chamado UserProfileDataSource (clique para exibir a imagem em tamanho real)

A próxima tela solicita o uso do banco de dados. Já definimos uma cadeia de conexão no Web.config para o SecurityTutorials banco de dados. Esse nome de cadeia de conexão – SecurityTutorialsConnectionString – deve estar na lista suspensa. Selecione esta opção e clique em Avançar.

Escolha SecurityTutorialsConnectionString na Lista de Drop-Down

Figura 8: Escolher SecurityTutorialsConnectionString na Lista de Drop-Down (Clique para exibir a imagem em tamanho real)

A tela subsequente solicita que especifique a tabela e as colunas a serem consultadas. Escolha a UserProfiles tabela na lista suspensa e marcar todas as colunas.

Trazer de volta todas as colunas da tabela UserProfiles

Figura 9: Trazer de volta todas as colunas da UserProfiles tabela (clique para exibir a imagem em tamanho real)

A consulta atual na Figura 9 retorna todos os registros em UserProfiles, mas estamos interessados apenas no registro do usuário conectado no momento. Para adicionar uma WHERE cláusula, clique no WHERE botão para abrir a caixa de diálogo Adicionar WHERE Cláusula (consulte a Figura 10). Aqui, você pode selecionar a coluna na qual filtrar, o operador e a origem do parâmetro de filtro. Selecione UserId como a coluna e "=" como o Operador.

Infelizmente, não há nenhuma fonte de parâmetro interna para retornar o valor do UserId usuário conectado no momento. Precisaremos obter esse valor programaticamente. Portanto, defina a lista suspensa Origem como "Nenhum", clique no botão Adicionar para adicionar o parâmetro e clique em OK.

Adicionar um parâmetro de filtro na coluna UserId

Figura 10: Adicionar um parâmetro de filtro na UserId coluna (clique para exibir a imagem em tamanho real)

Depois de clicar em OK, você será retornado para a tela mostrada na Figura 9. Desta vez, no entanto, a consulta SQL na parte inferior da tela deve incluir uma WHERE cláusula . Clique em Avançar para passar para a tela "Testar Consulta". Aqui você pode executar a consulta e ver os resultados. Clique em Concluir para concluir o assistente.

Ao concluir o Assistente de Configuração do DataSource, o Visual Studio cria o controle SqlDataSource com base nas configurações especificadas no assistente. Além disso, ele adiciona Manualmente BoundFields ao DetailsView para cada coluna retornada pelo SqlDataSource.SelectCommand Não é necessário mostrar o UserId campo no DetailsView, pois o usuário não precisa saber esse valor. Você pode remover esse campo diretamente da marcação declarativa do controle DetailsView ou clicando no link "Editar Campos" de sua Marca Inteligente.

Neste ponto, a marcação declarativa da página deve ser semelhante à seguinte:

<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>

Precisamos definir programaticamente o parâmetro do UserId controle SqlDataSource como o do usuário conectado no momento antes que UserId os dados sejam selecionados. Isso pode ser feito criando um manipulador de eventos para o evento sqlDataSource Selecting e adicionando o seguinte código:

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;
}

O código acima começa obtendo uma referência ao usuário conectado no momento chamando o Membership método da GetUser classe . Isso retorna um MembershipUser objeto , cuja ProviderUserKey propriedade contém o UserId. Em UserId seguida, o valor é atribuído ao parâmetro sqlDataSource @UserId .

Observação

O Membership.GetUser() método retorna informações sobre o usuário conectado no momento. Se um usuário anônimo estiver visitando a página, ele retornará um valor de null. Nesse caso, isso levará a um NullReferenceException na linha de código a seguir ao tentar ler a ProviderUserKey propriedade . É claro que não precisamos nos preocupar Membership.GetUser() em retornar um null valor na página porque configuramos a AdditionalUserInfo.aspx autorização de URL em um tutorial anterior para que apenas usuários autenticados pudessem acessar os recursos ASP.NET nessa pasta. Se você precisar acessar informações sobre o usuário conectado no momento em uma página em que o acesso anônimo é permitido, certifique-se de marcar que um objeto nãonull MembershipUser seja retornado do método antes de GetUser() referenciar suas propriedades.

Se você visitar a AdditionalUserInfo.aspx página por meio de um navegador, verá uma página em branco porque ainda não adicionamos nenhuma linha à UserProfiles tabela. Na Etapa 6, veremos como personalizar o controle CreateUserWizard para adicionar automaticamente uma nova linha à UserProfiles tabela quando uma nova conta de usuário for criada. Por enquanto, no entanto, precisaremos criar manualmente um registro na tabela.

Navegue até o banco de dados Explorer no Visual Studio e expanda a pasta Tabelas. Clique com o botão direito do aspnet_Users mouse na tabela e escolha "Mostrar Dados da Tabela" para ver os registros na tabela; faça o mesmo para a UserProfiles tabela. A Figura 11 mostra esses resultados quando lado a lado verticalmente. No meu banco de dados, atualmente aspnet_Users há registros para Bruce, Fred e Tito, mas nenhum registro na UserProfiles tabela.

O conteúdo das tabelas aspnet_Users e UserProfiles é exibido

Figura 11: o aspnet_Users conteúdo das tabelas e UserProfiles são exibidos (clique para exibir a imagem em tamanho real)

Adicione um novo registro à UserProfiles tabela digitando manualmente em valores para os HomeTowncampos , HomepageUrle Signature . A maneira mais fácil de obter um valor válido UserId no novo UserProfiles registro é selecionar o UserId campo de uma conta de usuário específica na aspnet_Users tabela e copiá-lo e colá-lo no UserId campo em UserProfiles. A Figura 12 mostra a UserProfiles tabela depois que um novo registro foi adicionado para Bruce.

Um registro foi adicionado a UserProfiles para Bruce

Figura 12: Um registro foi adicionado a UserProfiles para Bruce (Clique para exibir imagem em tamanho real)

Volte para a AdditionalUserInfo.aspx página, conectado como Bruce. Como mostra a Figura 13, as configurações de Bruce são exibidas.

O usuário que está visitando é mostrado suas configurações

Figura 13: O usuário visitante atualmente é mostrado suas configurações (clique para exibir imagem em tamanho real)

Observação

Vá em frente e adicione manualmente registros na UserProfiles tabela para cada usuário de Associação. Na Etapa 6, veremos como personalizar o controle CreateUserWizard para adicionar automaticamente uma nova linha à UserProfiles tabela quando uma nova conta de usuário for criada.

Etapa 3: Permitir que o usuário edite sua cidade natal, home page e assinatura

Neste ponto, o usuário conectado no momento pode exibir sua cidade natal, página inicial e configuração de assinatura, mas ele ainda não pode modificá-los. Vamos atualizar o controle DetailsView para que os dados possam ser editados.

A primeira coisa que precisamos fazer é adicionar um UpdateCommand para o SqlDataSource, especificando a UPDATE instrução a ser executada e seus parâmetros correspondentes. Selecione SqlDataSource e, no janela Propriedades, clique nas reticências ao lado da propriedade UpdateQuery para abrir a caixa de diálogo Editor de Comandos e Parâmetros. Insira a seguinte UPDATE instrução na caixa de texto:

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

Em seguida, clique no botão "Atualizar Parâmetros", que criará um parâmetro na coleção do UpdateParameters controle SqlDataSource para cada um dos parâmetros na UPDATE instrução . Deixe a origem de todos os parâmetros definidos como Nenhum e clique no botão OK para concluir a caixa de diálogo.

Especificar UpdateCommand e UpdateParameters do SqlDataSource

Figura 14: especifique os SqlDataSource UpdateCommand e UpdateParameters (Clique para exibir a imagem em tamanho real)

Devido às adições que fizemos ao controle SqlDataSource, o controle DetailsView agora pode dar suporte à edição. Na Marca Inteligente do DetailsView, marcar caixa de seleção "Habilitar Edição". Isso adiciona um CommandField à coleção do Fields controle com sua ShowEditButton propriedade definida como True. Isso renderiza um botão Editar quando o DetailsView é exibido no modo somente leitura e botões Atualizar e Cancelar quando exibido no modo de edição. Em vez de exigir que o usuário clique em Editar, no entanto, podemos ter a renderização DetailsView em um estado "sempre editável" definindo a propriedade do DefaultMode controle DetailsView como Edit.

Com essas alterações, a marcação declarativa do controle DetailsView deve ser semelhante à seguinte:

<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>

Observe a adição do CommandField e da DefaultMode propriedade .

Vá em frente e teste esta página por meio de um navegador. Ao visitar com um usuário que tem um registro correspondente no UserProfiles, as configurações do usuário são exibidas em uma interface editável.

O DetailsView renderiza uma interface editável

Figura 15: o DetailsView renderiza uma interface editável (clique para exibir a imagem em tamanho real)

Tente alterar os valores e clicar no botão Atualizar. Parece que nada acontece. Há um postback e os valores são salvos no banco de dados, mas não há comentários visuais de que o salvamento ocorreu.

Para corrigir isso, retorne ao Visual Studio e adicione um controle Rótulo acima de DetailsView. Defina como SettingsUpdatedMessageID , sua Text propriedade como "Suas configurações foram atualizadas" e suas Visible propriedades e EnableViewState como false.

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

Precisamos exibir o SettingsUpdatedMessage Rótulo sempre que o DetailsView for atualizado. Para fazer isso, crie um manipulador de eventos para o evento DetailsView ItemUpdated e adicione o seguinte código:

protected void UserProfile_ItemUpdated(object sender, DetailsViewUpdatedEventArgs e)
{
     SettingsUpdatedMessage.Visible = true;
}

Retorne à AdditionalUserInfo.aspx página por meio de um navegador e atualize os dados. Desta vez, uma mensagem de status útil é exibida.

Uma mensagem curta é exibida quando as configurações são atualizadas

Figura 16: Uma mensagem curta é exibida quando as configurações são atualizadas (clique para exibir a imagem em tamanho real)

Observação

A interface de edição do controle DetailsView deixa muito a desejar. Ele usa caixas de texto de tamanho padrão, mas o campo Assinatura provavelmente deve ser uma caixa de texto de várias linhas. Um RegularExpressionValidator deve ser usado para garantir que a URL da home page, se inserida, comece com "http://" ou "https://". Além disso, como o controle DetailsView tem sua DefaultMode propriedade definida como Edit, o botão Cancelar não faz nada. Ele deve ser removido ou, quando clicado, redirecionar o usuário para alguma outra página (como ~/Default.aspx). Deixo esses aprimoramentos como um exercício para o leitor.

Atualmente, o site não fornece links para a AdditionalUserInfo.aspx página. A única maneira de acessá-la é inserir a URL da página diretamente na barra de endereços do navegador. Vamos adicionar um link a esta página na Site.master página master.

Lembre-se de que a página master contém um controle Web LoginView em seu LoginContent ContentPlaceHolder que exibe marcação diferente para visitantes autenticados e anônimos. Atualize os controles LoggedInTemplate LoginView para incluir um link para a AdditionalUserInfo.aspx página. Depois de fazer essas alterações, a marcação declarativa do controle LoginView deve ser semelhante à seguinte:

<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>

Observe a adição do lnkUpdateSettings controle HyperLink ao LoggedInTemplate. Com esse link em vigor, os usuários autenticados podem ir rapidamente para a página para exibir e modificar suas configurações de cidade natal, home page e assinatura.

Etapa 4: Adicionar novos comentários de guestbook

A Guestbook.aspx página é onde os usuários autenticados podem exibir o guestbook e deixar um comentário. Vamos começar com a criação da interface para adicionar novos comentários do guestbook.

Abra a Guestbook.aspx página no Visual Studio e construa uma interface do usuário que consiste em dois controles TextBox, um para o assunto do novo comentário e outro para seu corpo. Defina a propriedade do ID primeiro controle TextBox como Subject e sua Columns propriedade como 40; defina a segunda ID como Body, como TextModeMultiLinee suas Width propriedades e Rows como "95%" e 8, respectivamente. Para concluir a interface do usuário, adicione um controle Web button chamado PostCommentButton e defina sua Text propriedade como "Postar Seu Comentário".

Como cada comentário do guestbook requer um assunto e um corpo, adicione um RequiredFieldValidator para cada uma das TextBoxes. Defina a ValidationGroup propriedade desses controles como "EnterComment"; da mesma forma, defina a PostCommentButton propriedade do ValidationGroup controle como "EnterComment". Para obter mais informações sobre ASP. Os controles de validação do NET marcar validação de formulário em ASP.NET.

Depois de criar a interface do usuário, a marcação declarativa da página deve ser semelhante à seguinte:

<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>

Com a interface do usuário concluída, nossa próxima tarefa é inserir um novo registro na GuestbookComments tabela quando o PostCommentButton for clicado. Isso pode ser feito de várias maneiras: podemos escrever ADO.NET código no manipulador de eventos do Click Botão; podemos adicionar um controle SqlDataSource à página, configurar seu InsertCommande, em seguida, chamar seu Insert método do manipulador de Click eventos; ou podemos criar uma camada intermediária responsável por inserir novos comentários de guestbook e invocar essa funcionalidade do Click manipulador de eventos. Como examinamos o uso de um SqlDataSource na Etapa 3, vamos usar ADO.NET código aqui.

Observação

As classes ADO.NET usadas para acessar programaticamente dados de um banco de dados do Microsoft SQL Server estão localizadas no System.Data.SqlClient namespace. Talvez seja necessário importar esse namespace para a classe code-behind da sua página (ou seja, using System.Data.SqlClient;).

Crie um manipulador de eventos para o PostCommentButtonevento e Click adicione o seguinte código:

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;
}

O Click manipulador de eventos começa verificando se os dados fornecidos pelo usuário são válidos. Se não estiver, o manipulador de eventos será encerrado antes de inserir um registro. Supondo que os dados fornecidos sejam válidos, o valor do UserId usuário conectado no momento é recuperado e armazenado na currentUserId variável local. Esse valor é necessário porque devemos fornecer um UserId valor ao inserir um registro em GuestbookComments.

Depois disso, a cadeia de conexão do SecurityTutorials banco de dados é recuperada e Web.config a INSERT instrução SQL é especificada. Em SqlConnection seguida, um objeto é criado e aberto. Em seguida, um SqlCommand objeto é construído e os valores para os parâmetros usados na INSERT consulta são atribuídos. Em INSERT seguida, a instrução é executada e a conexão é fechada. No final do manipulador de eventos, as propriedades de SubjectText TextBoxes e Body são desmarcadas para que os valores do usuário não sejam persistidos no postback.

Vá em frente e teste esta página em um navegador. Como esta página está na Membership pasta, ela não pode ser acessada por visitantes anônimos. Portanto, você precisará primeiro fazer logon (se ainda não fez isso). Insira um valor no e Body textBoxes Subject e clique no PostCommentButton botão . Isso fará com que um novo registro seja adicionado a GuestbookComments. No postback, o assunto e o corpo fornecidos são apagados das Caixas de Texto.

Depois de clicar no PostCommentButton botão, não há comentários visuais de que o comentário foi adicionado ao guestbook. Ainda precisamos atualizar esta página para exibir os comentários de guestbook existentes, o que faremos na Etapa 5. Depois de fazer isso, o comentário adicionado será exibido na lista de comentários, fornecendo comentários visuais adequados. Por enquanto, confirme se o comentário da pasta de convidado foi salvo examinando o conteúdo da GuestbookComments tabela.

A Figura 17 mostra o conteúdo da GuestbookComments tabela após dois comentários terem sido deixados.

Você pode ver os comentários do guestbook na tabela GuestbookComments

Figura 17: Você pode ver os comentários do guestbook na GuestbookComments tabela (clique para exibir a imagem em tamanho real)

Observação

Se um usuário tentar inserir um comentário de guestbook que contenha marcação potencialmente perigosa , como HTML , ASP.NET lançará um HttpRequestValidationException. Para saber mais sobre essa exceção, por que ela é lançada e como permitir que os usuários enviem valores potencialmente perigosos, consulte o Whitepaper de Validação de Solicitação.

Etapa 5: Listando os comentários de guestbook existentes

Além de deixar comentários, um usuário que visita a Guestbook.aspx página também deve ser capaz de exibir os comentários existentes do guestbook. Para fazer isso, adicione um controle ListView chamado CommentList na parte inferior da página.

Observação

O controle ListView é novo para ASP.NET versão 3.5. Ele foi projetado para exibir uma lista de itens em um layout muito personalizável e flexível, mas ainda oferece edição interna, inserção, exclusão, paginação e funcionalidade de classificação, como o GridView. Se você estiver usando ASP.NET 2.0, precisará usar o controle DataList ou Repeater. Para obter mais informações sobre como usar o ListView, consulte a entrada de blog de Scott Guthrie, O controle asp:ListView e meu artigo Exibindo dados com o controle ListView.

Abra a Marca Inteligente do ListView e, na lista suspensa Escolher Fonte de Dados, associe o controle a uma nova fonte de dados. Como vimos na Etapa 2, isso iniciará o Assistente de Configuração da Fonte de Dados. Selecione o ícone Banco de Dados, nomeie o SqlDataSource CommentsDataSourceresultante e clique em OK. Em seguida, selecione a SecurityTutorialsConnectionString cadeia de conexão na lista suspensa e clique em Avançar.

Neste ponto da Etapa 2, especificamos os dados a serem consultados escolhendo a UserProfiles tabela na lista suspensa e selecionando as colunas a serem retornadas (consulte a Figura 9). Desta vez, no entanto, queremos criar uma instrução SQL que extraia não apenas os registros de GuestbookComments, mas também da cidade natal do comentarista, página inicial, assinatura e nome de usuário. Portanto, selecione o botão de opção "Especificar uma instrução SQL personalizada ou procedimento armazenado" e clique em Avançar.

Isso abrirá a tela "Definir instruções personalizadas ou procedimentos armazenados". Clique no botão Construtor de Consultas para criar graficamente a consulta. O Construtor de Consultas começa solicitando que especifiquemos as tabelas das quais queremos consultar. Selecione as GuestbookCommentstabelas , UserProfilese aspnet_Users e clique em OK. Isso adicionará todas as três tabelas à superfície de design. Como há restrições de chave estrangeira entre as GuestbookCommentstabelas , UserProfilese aspnet_Users , o Construtor de Consultas é automaticamente JOIN essas tabelas.

Tudo o que resta é especificar as colunas a serem retornadas. GuestbookComments Na tabela, selecione as Subjectcolunas , Bodye CommentDate ; retorne as HomeTowncolunas , HomepageUrle Signature da UserProfiles tabela e retorne UserName de aspnet_Users. Além disso, adicione "ORDER BY CommentDate DESC" ao final da SELECT consulta para que as postagens mais recentes sejam retornadas primeiro. Depois de fazer essas seleções, sua interface do Construtor de Consultas deve ser semelhante à captura de tela na Figura 18.

O JOINs de consulta construído o GuestbookComments, UserProfiles e tabelas de aspnet_Users

Figura 18: a consulta JOIN construída é as GuestbookCommentstabelas , UserProfilese aspnet_Users (clique para exibir a imagem em tamanho real)

Clique em OK para fechar a janela construtor de consultas e retornar à tela "Definir instruções personalizadas ou procedimentos armazenados". Clique em Avançar para avançar para a tela "Testar Consulta", em que você pode exibir os resultados da consulta clicando no botão Testar Consulta. Quando estiver pronto, clique em Concluir para concluir o assistente Configurar Fonte de Dados.

Quando concluímos o assistente Configurar Fonte de Dados na Etapa 2, a coleção do Fields controle DetailsView associada foi atualizada para incluir um BoundField para cada coluna retornada pelo SelectCommand. O ListView, no entanto, permanece inalterado; ainda precisamos definir seu layout. O layout do ListView pode ser construído manualmente por meio de sua marcação declarativa ou da opção "Configurar ListView" em sua Marca Inteligente. Geralmente prefiro definir a marcação manualmente, mas use qualquer método mais natural para você.

Acabei usando o seguinte LayoutTemplate, ItemTemplatee ItemSeparatorTemplate para meu controle ListView:

<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>

O LayoutTemplate define a marcação emitida pelo controle, enquanto o ItemTemplate renderiza cada item retornado pelo SqlDataSource. A ItemTemplatemarcação resultante é colocada no LayoutTemplatecontrole do .itemPlaceholder Além do itemPlaceholder, o LayoutTemplate inclui um controle DataPager, que limita o ListView a mostrar apenas 10 comentários de guestbook por página (o padrão) e renderiza uma interface de paginação.

Meu ItemTemplate exibe o assunto de cada comentário do guestbook em um <h4> elemento com o corpo situado abaixo do assunto. Observe que a sintaxe usada para exibir o corpo usa os dados retornados pela instrução de vinculação de dados, converte-os Eval("Body") em uma cadeia de caracteres e substitui quebras de linha pelo <br /> elemento . Essa conversão é necessária para mostrar as quebras de linha inseridas ao enviar o comentário, pois o espaço em branco é ignorado por HTML. A assinatura do usuário é exibida abaixo do corpo em itálico, seguido pela cidade natal do usuário, um link para sua home page, a data e a hora em que o comentário foi feito e o nome de usuário da pessoa que deixou o comentário.

Reserve um momento para exibir a página por meio de um navegador. Você deve ver os comentários que adicionou ao guestbook na Etapa 5 exibida aqui.

Guestbook.aspx agora exibe os comentários do guestbook

Figura 19: Guestbook.aspx Agora exibe os Comentários do Guestbook (clique para exibir a imagem em tamanho real)

Tente adicionar um novo comentário ao guestbook. Ao clicar no PostCommentButton botão, a página é posta de volta e o comentário é adicionado ao banco de dados, mas o controle ListView não é atualizado para mostrar o novo comentário. Isso pode ser corrigido por:

  • Atualizando o PostCommentButton manipulador de eventos do Click botão para que ele invoque o método do DataBind() controle ListView depois de inserir o novo comentário no banco de dados ou
  • Definindo a propriedade do EnableViewState controle ListView como false. Essa abordagem funciona porque, ao desabilitar o estado de exibição do controle, ela deve se associar novamente aos dados subjacentes em cada postback.

O site do tutorial que pode ser baixado neste tutorial ilustra ambas as técnicas. A propriedade do EnableViewState controle ListView para false e o código necessário para reassociar programaticamente os dados ao ListView está presente no Click manipulador de eventos, mas é comentado.

Observação

Atualmente, a AdditionalUserInfo.aspx página permite que o usuário exiba e edite suas configurações de cidade natal, home page e assinatura. Pode ser bom atualizar AdditionalUserInfo.aspx para exibir os comentários da pasta de convidado do usuário conectado. Ou seja, além de examinar e modificar suas informações, um usuário pode visitar a AdditionalUserInfo.aspx página para ver quais comentários de guestbook ela fez no passado. Eu deixo isso como um exercício para o leitor interessado.

Etapa 6: Personalizando o controle CreateUserWizard para incluir uma interface para a cidade inicial, página inicial e assinatura

A SELECT consulta usada pela Guestbook.aspx página usa um INNER JOIN para combinar os registros relacionados entre as GuestbookCommentstabelas , UserProfilese aspnet_Users . Se um usuário que não tem nenhum registro em UserProfiles fizer um comentário de guestbook, o comentário não será exibido no ListView porque o INNER JOIN único retorna GuestbookComments registros quando há registros correspondentes em UserProfiles e aspnet_Users. E, como vimos na Etapa 3, se um usuário não tiver um registro no UserProfiles , ela não poderá exibir ou editar suas configurações na AdditionalUserInfo.aspx página.

Desnecessário dizer que, devido às nossas decisões de design, é importante que cada conta de usuário no sistema de associação tenha um registro correspondente na UserProfiles tabela. O que queremos é que um registro correspondente seja adicionado sempre UserProfiles que uma nova conta de usuário associação for criada por meio do CreateUserWizard.

Conforme discutido no tutorial Criando Contas de Usuário , depois que a nova conta de usuário associação é criada, o controle CreateUserWizard gera seu CreatedUser evento. Podemos criar um manipulador de eventos para esse evento, obter o UserId para o usuário criado e, em seguida, inserir um registro na UserProfiles tabela com valores padrão para as HomeTowncolunas , HomepageUrle Signature . Além disso, é possível solicitar ao usuário esses valores personalizando a interface do controle CreateUserWizard para incluir TextBoxes adicionais.

Primeiro, vamos ver como adicionar uma nova linha à UserProfiles tabela no CreatedUser manipulador de eventos com valores padrão. Depois disso, veremos como personalizar a interface do usuário do controle CreateUserWizard para incluir campos de formulário adicionais para coletar a cidade natal, a home page e a assinatura do novo usuário.

Adicionando uma linha padrão aUserProfiles

No tutorial Criando Contas de Usuário , adicionamos um controle CreateUserWizard à CreatingUserAccounts.aspx página na Membership pasta . Para que o controle CreateUserWizard adicione um registro à UserProfiles tabela após a criação da conta de usuário, precisamos atualizar a funcionalidade do controle CreateUserWizard. Em vez de fazer essas alterações na CreatingUserAccounts.aspx página, vamos adicionar um novo controle CreateUserWizard à EnhancedCreateUserWizard.aspx página e fazer as modificações para este tutorial.

Abra a EnhancedCreateUserWizard.aspx página no Visual Studio e arraste um controle CreateUserWizard da Caixa de Ferramentas para a página. Defina a propriedade do ID controle CreateUserWizard como NewUserWizard. Como discutimos no tutorial Criando Contas de Usuário, a interface do usuário padrão do CreateUserWizard solicita ao visitante as informações necessárias. Depois que essas informações forem fornecidas, o controle criará internamente uma nova conta de usuário na estrutura Associação, tudo sem que precisemos escrever uma única linha de código.

O controle CreateUserWizard gera uma série de eventos durante seu fluxo de trabalho. Depois que um visitante fornece as informações de solicitação e envia o formulário, o controle CreateUserWizard inicialmente dispara seu CreatingUser evento. Se houver um problema durante o processo de criação, o CreateUserError evento será acionado; no entanto, se o usuário for criado com êxito, o CreatedUser evento será acionado. No tutorial Criando Contas de Usuário, criamos um manipulador de eventos para o CreatingUser evento para garantir que o nome de usuário fornecido não contivesse espaços à esquerda ou à direita e que o nome de usuário não aparecesse em nenhum lugar na senha.

Para adicionar uma linha na UserProfiles tabela para o usuário que acabou de criar, precisamos criar um manipulador de eventos para o CreatedUser evento. Quando o CreatedUser evento for acionado, a conta de usuário já foi criada na estrutura Associação, permitindo que recuperemos o valor UserId da conta.

Crie um manipulador de eventos para o NewUserWizardevento e CreatedUser adicione o seguinte código:

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();
     }
}

Os seres de código acima recuperando a UserId da conta de usuário que acabou de adicionar. Isso é feito usando o Membership.GetUser(username) método para retornar informações sobre um usuário específico e, em seguida, usando a ProviderUserKey propriedade para recuperar sua UserId. O nome de usuário inserido pelo usuário no controle CreateUserWizard está disponível por meio de sua UserName propriedade.

Em seguida, a cadeia de conexão é recuperada de Web.config e a INSERT instrução é especificada. Os objetos ADO.NET necessários são instanciados e o comando executado. O código atribui uma DBNull instância aos @HomeTownparâmetros , @HomepageUrle @Signature , que tem o efeito de inserir valores de banco de dados NULL para os HomeTowncampos , HomepageUrle Signature .

Visite a EnhancedCreateUserWizard.aspx página por meio de um navegador e crie uma nova conta de usuário. Depois de fazer isso, retorne ao Visual Studio e examine o conteúdo das aspnet_Users tabelas e UserProfiles (como fizemos na Figura 12). Você deve ver a nova conta de usuário em aspnet_Users e uma linha correspondente UserProfiles (com NULL valores para HomeTown, HomepageUrle Signature).

Uma nova conta de usuário e um registro UserProfiles foram adicionados

Figura 20: Uma nova conta de usuário e UserProfiles um registro foram adicionados (clique para exibir a imagem em tamanho real)

Depois que o visitante fornecer suas novas informações de conta e clicar no botão "Criar Usuário", a conta de usuário será criada e uma linha será adicionada à UserProfiles tabela. Em seguida, o CreateUserWizard exibe seu CompleteWizardStep, que exibe uma mensagem de êxito e um botão Continuar. Clicar no botão Continuar causa um postback, mas nenhuma ação é tomada, deixando o usuário preso na EnhancedCreateUserWizard.aspx página.

Podemos especificar uma URL para a qual enviar o usuário quando o botão Continuar for clicado por meio da propriedade do ContinueDestinationPageUrlcontrole CreateUserWizard. Defina a ContinueDestinationPageUrl propriedade como "~/Membership/AdditionalUserInfo.aspx". Isso leva o novo usuário para AdditionalUserInfo.aspx, onde ele pode exibir e atualizar suas configurações.

Personalizando a interface de CreateUserWizard para solicitar a cidade inicial, a home page e a assinatura do novo usuário

A interface padrão do controle CreateUserWizard é suficiente para cenários de criação de conta simples em que apenas as principais informações da conta de usuário, como nome de usuário, senha e email, precisam ser coletadas. Mas e se quiséssemos pedir ao visitante para entrar em sua cidade natal, home page e assinatura enquanto criava sua conta? É possível personalizar a interface do controle CreateUserWizard para coletar informações adicionais na inscrição, e essas informações podem ser usadas no CreatedUser manipulador de eventos para inserir registros adicionais no banco de dados subjacente.

O controle CreateUserWizard estende o controle assistente de ASP.NET, que é um controle que permite que um desenvolvedor de página defina uma série de ordenados WizardSteps. O controle assistente renderiza a etapa ativa e fornece uma interface de navegação que permite que o visitante passe por essas etapas. O controle do Assistente é ideal para dividir uma tarefa longa em várias etapas curtas. Para obter mais informações sobre o controle do Assistente, consulte Criando uma interface do usuário passo a passo com o controle do Assistente do ASP.NET 2.0.

A marcação padrão do controle CreateUserWizard define dois WizardSteps: CreateUserWizardStep e 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>

O primeiro WizardStep, CreateUserWizardStep, renderiza a interface que solicita o nome de usuário, a senha, o email e assim por diante. Depois que o visitante fornecer essas informações e clicar em "Criar Usuário", ela verá o CompleteWizardStep, que mostra a mensagem de êxito e um botão Continuar.

Para personalizar a interface do controle CreateUserWizard para incluir campos de formulário adicionais, podemos:

  • Criar um ou mais novosWizardSteps para conter os elementos adicionais da interface do usuário. Para adicionar um novo WizardStep ao CreateUserWizard, clique no link "Adicionar/Remover WizardSteps" de sua Marca Inteligente para iniciar o WizardStep Editor de Coleção. A partir daí, você pode adicionar, remover ou reordenar as etapas no assistente. Essa é a abordagem que usaremos para este tutorial.

  • Converter oCreateUserWizardStepem um editávelWizardStep. Isso substitui o CreateUserWizardStep por um equivalente WizardStep cuja marcação define uma interface do usuário que corresponde à CreateUserWizardStepde . Ao converter o CreateUserWizardStep em um WizardStep , podemos reposicionar os controles ou adicionar elementos adicionais da interface do usuário a esta etapa. Para converter o CreateUserWizardStep ou CompleteWizardStep em um editável WizardStep, clique no link "Personalizar Criar Etapa do Usuário" ou "Personalizar Etapa Completa" da Marca Inteligente do controle.

  • Use alguma combinação das duas opções acima.

Uma coisa importante a ter em mente é que o controle CreateUserWizard executa seu processo de criação de conta de usuário quando o botão "Criar Usuário" é clicado de dentro de seu CreateUserWizardStep. Não importa se há mais WizardStep s após o CreateUserWizardStep ou não.

Ao adicionar um personalizado WizardStep ao controle CreateUserWizard para coletar entradas adicionais do usuário, o personalizado WizardStep pode ser colocado antes ou depois do CreateUserWizardStep. Se ele vier antes do CreateUserWizardStep , a entrada de usuário adicional coletada do personalizado WizardStep estará disponível para o CreatedUser manipulador de eventos. No entanto, se o personalizado WizardStep vier depois CreateUserWizardStep do momento em que o personalizado WizardStep for exibido, a nova conta de usuário já foi criada e o CreatedUser evento já foi acionado.

A Figura 21 mostra o fluxo de trabalho quando o adicionado WizardStep precede o CreateUserWizardStep. Como as informações adicionais do usuário foram coletadas no momento em que o CreatedUser evento é acionado, tudo o que precisamos fazer é atualizar o CreatedUser manipulador de eventos para recuperar essas entradas e usá-las para os INSERT valores de parâmetro da instrução (em vez de DBNull.Value).

O fluxo de trabalho CreateUserWizard quando um assistente adicionalEtapa precede o CreateUserWizardStep

Figura 21: o fluxo de trabalho CreateUserWizard quando um adicional WizardStep precede o CreateUserWizardStep (clique para exibir a imagem em tamanho real)

No entanto, se o personalizado WizardStep for colocado após o CreateUserWizardStep, o processo de criação de conta de usuário ocorrerá antes que o usuário tenha a chance de entrar em sua cidade natal, home page ou assinatura. Nesse caso, essas informações adicionais precisam ser inseridas no banco de dados após a criação da conta de usuário, como ilustra a Figura 22.

O fluxo de trabalho CreateUserWizard quando um wizardstep adicional vem após CreateUserWizardStep

Figura 22: o fluxo de trabalho CreateUserWizard quando um adicional WizardStep vem após o CreateUserWizardStep (clique para exibir a imagem em tamanho real)

O fluxo de trabalho mostrado na Figura 22 aguarda para inserir um registro na tabela até que a UserProfiles Etapa 2 seja concluída. No entanto, se o visitante fechar o navegador após a etapa 1, teremos atingido um estado em que uma conta de usuário foi criada, mas nenhum registro foi adicionado ao UserProfiles. Uma solução alternativa é ter um registro com NULL ou valores padrão inseridos UserProfiles no manipulador de eventos (que é acionado após a CreatedUser etapa 1) e, em seguida, atualizar esse registro após a conclusão da etapa 2. Isso garante que um UserProfiles registro seja adicionado para a conta de usuário, mesmo que o usuário encerre o processo de registro no meio do caminho.

Para este tutorial, vamos criar um novo WizardStep que ocorre após o CreateUserWizardStep , mas antes do CompleteWizardStep. Primeiro, vamos colocar o WizardStep em vigor e configurá-lo e, em seguida, examinaremos o código.

Na Marca Inteligente do controle CreateUserWizard, selecione "Adicionar/Remover WizardStep s", que abre a WizardStep caixa de diálogo Editor de Coleção. Adicione um novo WizardStep, definindo como UserSettingsID , como Title "Suas Configurações" e como StepTypeStep. Em seguida, posicione-o para que ele venha após o CreateUserWizardStep ("Inscrever-se para sua nova conta") e antes do CompleteWizardStep ("Concluído"), conforme mostrado na Figura 23.

Adicionar um assistenteEtapa ao controle CreateUserWizard

Figura 23: Adicionar um novo WizardStep ao controle CreateUserWizard (clique para exibir a imagem em tamanho real)

Clique em OK para fechar a WizardStep caixa de diálogo Editor de Coleção. O novo WizardStep é evidenciado pela marcação declarativa atualizada do controle 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>

Observe o novo <asp:WizardStep> elemento. Precisamos adicionar a interface do usuário para coletar a cidade natal, a home page e a assinatura do novo usuário aqui. Você pode inserir esse conteúdo na sintaxe declarativa ou por meio do Designer. Para usar o Designer, selecione a etapa "Suas Configurações" na lista suspensa na Marca Inteligente para ver a etapa no Designer.

Observação

Selecionar uma etapa por meio da lista suspensa da Marca Inteligente atualiza a propriedade do ActiveStepIndexcontrole CreateUserWizard, que especifica o índice da etapa inicial. Portanto, se você usar essa lista suspensa para editar a etapa "Suas Configurações" no Designer, defina-a novamente como "Inscrever-se para sua nova conta" para que essa etapa seja mostrada quando os usuários visitarem a EnhancedCreateUserWizard.aspx página pela primeira vez.

Crie uma interface do usuário na etapa "Suas Configurações" que contém três controles TextBox chamados HomeTown, HomepageUrle Signature. Depois de construir essa interface, a marcação declarativa de CreateUserWizard deve ser semelhante à seguinte:

<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>

Acesse esta página por meio de um navegador e crie uma nova conta de usuário, especificando valores para a cidade natal, a home page e a assinatura. Depois de concluir, a CreateUserWizardStep conta de usuário é criada na estrutura Associação e o manipulador de eventos é executado, o CreatedUser que adiciona uma nova linha a UserProfiles, mas com um valor de banco de dados NULL para HomeTown, HomepageUrle Signature. Os valores inseridos para a cidade natal, home page e assinatura nunca são usados. O resultado líquido é uma nova conta de usuário com um UserProfiles registro cujos HomeTowncampos , HomepageUrle Signature ainda não foram especificados.

Precisamos executar o código após a etapa "Suas Configurações" que usa os valores de cidade natal, honepage e assinatura inseridos pelo usuário e atualiza o registro apropriado UserProfiles . Cada vez que o usuário se move entre as etapas em um controle do Assistente, o evento do ActiveStepChanged Assistente é acionado. Podemos criar um manipulador de eventos para esse evento e atualizar a UserProfiles tabela quando a etapa "Suas Configurações" for concluída.

Adicione um manipulador de eventos para o evento createUserWizard ActiveStepChanged e adicione o seguinte código:

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();
          }
     }
}

O código acima começa determinando se acabamos de chegar à etapa "Concluir". Como a etapa "Concluir" ocorre imediatamente após a etapa "Suas Configurações", quando o visitante atinge a etapa "Concluir", isso significa que ela acabou de concluir a etapa "Suas Configurações".

Nesse caso, precisamos referenciar programaticamente os controles TextBox dentro do UserSettings WizardStep. Isso é feito primeiro usando o FindControl método para referenciar programaticamente o UserSettings WizardStepe, em seguida, novamente para referenciar as TextBoxes de dentro do WizardStep. Depois que o TextBoxes tiver sido referenciado, estaremos prontos para executar a UPDATE instrução . A UPDATE instrução tem o mesmo número de parâmetros que a INSERT instrução no CreatedUser manipulador de eventos, mas aqui usamos os valores de cidade inicial, home page e assinatura fornecidos pelo usuário.

Com esse manipulador de eventos em vigor, visite a EnhancedCreateUserWizard.aspx página por meio de um navegador e crie uma nova conta de usuário especificando valores para a cidade natal, a home page e a assinatura. Depois de criar a nova conta, você deve ser redirecionado para a AdditionalUserInfo.aspx página, na qual as informações de cidade natal, home page e assinatura recém-inseridas são exibidas.

Observação

Nosso site atualmente tem duas páginas das quais um visitante pode criar uma nova conta: CreatingUserAccounts.aspx e EnhancedCreateUserWizard.aspx. O site sitemap e a página de logon apontam para a CreatingUserAccounts.aspx página, mas a CreatingUserAccounts.aspx página não solicita ao usuário suas informações de cidade natal, home page e assinatura e não adiciona uma linha correspondente a UserProfiles. Portanto, atualize a CreatingUserAccounts.aspx página para que ela ofereça essa funcionalidade ou atualize o mapa do site e a página de logon para fazer referência EnhancedCreateUserWizard.aspx em vez de CreatingUserAccounts.aspx. Se você escolher a última opção, atualize o Membership arquivo da Web.config pasta para permitir que usuários anônimos acessem a EnhancedCreateUserWizard.aspx página.

Resumo

Neste tutorial, examinamos técnicas de modelagem de dados relacionadas a contas de usuário na estrutura associação. Em particular, examinamos as entidades de modelagem que compartilham uma relação um-para-muitos com contas de usuário, bem como dados que compartilham uma relação um-para-um. Além disso, vimos como essas informações relacionadas poderiam ser exibidas, inseridas e atualizadas, com alguns exemplos usando o controle SqlDataSource e outros usando ADO.NET código.

Este tutorial conclui nossa olhada nas contas de usuário. A partir do próximo tutorial, voltaremos nossa atenção para as funções. Nos próximos tutoriais, examinaremos a estrutura Funções, veremos como criar novas funções, como atribuir funções aos usuários, como determinar a quais funções um usuário pertence e como aplicar a autorização baseada em função.

Programação feliz!

Leitura Adicional

Para obter mais informações sobre os tópicos discutidos neste tutorial, consulte os seguintes recursos:

Sobre o autor

Scott Mitchell, autor de vários livros do ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 Horas. Scott pode ser contatado em mitchell@4guysfromrolla.com ou através de seu blog em http://ScottOnWriting.NET.

Agradecimentos especiais a...

Esta série de tutoriais foi revisada por muitos revisores úteis. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, solte-me uma linha em mitchell@4GuysFromRolla.com.