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
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
, Email
e 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 aGuestbookComments
tabela
Para capturar os comentários do guestbook, precisamos criar uma tabela de banco de dados chamada GuestbookComments
que tenha colunas como CommentId
, Subject
, Body
e 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.
Figura 1: Adicionar uma nova tabela ao SecurityTutorials
banco de dados (clique para exibir a imagem em tamanho real)
Em seguida, defina as GuestbookComments
colunas 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.
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 UserName
aspnet_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 GuestbookComments
como .
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.
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.
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.
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 ao
aspnet_Users
Ouaspnet_Membership
Tabelas. Eu não recomendaria essa abordagem porque ela modifica o esquema usado peloSqlMembershipProvider
. Essa decisão pode voltar para assombrá-lo na estrada. Por exemplo, e se uma versão futura do ASP.NET usar um esquema diferenteSqlMembershipProvider
. A Microsoft pode incluir uma ferramenta para migrar os dados do ASP.NET 2.0SqlMembershipProvider
para o novo esquema, mas se você tiver modificado o esquema ASP.NET 2.0SqlMembershipProvider
, 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 peloSqlProfileProvider
(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 noWeb.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, oSqlProfileProvider
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 e
aspnet_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 uniqueidentifier
como . 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.
Figura 6: Criar a UserProfiles
tabela (clique para exibir a imagem em tamanho real)
Salve a tabela e nomeie-a UserProfiles
como . 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
.
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.
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.
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.
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.
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 HomeTown
campos , HomepageUrl
e 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.
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.
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.
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.
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 SettingsUpdatedMessage
ID
, 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.
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.
Adicionando um link àAdditionalUserInfo.aspx
página na página mestra
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 TextMode
MultiLine
e 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 InsertCommand
e, 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 PostCommentButton
evento 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 Subject
Text
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.
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 CommentsDataSource
resultante 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 GuestbookComments
tabelas , UserProfiles
e 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 GuestbookComments
tabelas , UserProfiles
e 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 Subject
colunas , Body
e CommentDate
; retorne as HomeTown
colunas , HomepageUrl
e 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.
Figura 18: a consulta JOIN
construída é as GuestbookComments
tabelas , UserProfiles
e 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
, ItemTemplate
e 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 ItemTemplate
marcação resultante é colocada no LayoutTemplate
controle 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.
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 doClick
botão para que ele invoque o método doDataBind()
controle ListView depois de inserir o novo comentário no banco de dados ou - Definindo a propriedade do
EnableViewState
controle ListView comofalse
. 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 GuestbookComments
tabelas , UserProfiles
e 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 HomeTown
colunas , HomepageUrl
e 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 NewUserWizard
evento 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 @HomeTown
parâmetros , @HomepageUrl
e @Signature
, que tem o efeito de inserir valores de banco de dados NULL
para os HomeTown
campos , HomepageUrl
e 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
, HomepageUrl
e Signature
).
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 ContinueDestinationPageUrl
controle 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 novos
WizardStep
s para conter os elementos adicionais da interface do usuário. Para adicionar um novoWizardStep
ao CreateUserWizard, clique no link "Adicionar/RemoverWizardSteps
" de sua Marca Inteligente para iniciar oWizardStep
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 o
CreateUserWizardStep
em um editávelWizardStep
. Isso substitui oCreateUserWizardStep
por um equivalenteWizardStep
cuja marcação define uma interface do usuário que corresponde àCreateUserWizardStep
de . Ao converter oCreateUserWizardStep
em umWizardStep
, podemos reposicionar os controles ou adicionar elementos adicionais da interface do usuário a esta etapa. Para converter oCreateUserWizardStep
ouCompleteWizardStep
em um editávelWizardStep
, 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
).
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.
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 UserSettings
ID
, como Title
"Suas Configurações" e como StepType
Step
. 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.
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 ActiveStepIndex
controle 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
, HomepageUrl
e 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
, HomepageUrl
e 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 HomeTown
campos , HomepageUrl
e 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 WizardStep
e, 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:
- Acessando e atualizando dados no ASP.NET 2.0
- Controle do Assistente do ASP.NET 2.0
- Criando uma interface do usuário passo a passo com o controle do Assistente do ASP.NET 2.0
- Criando parâmetros de controle datasource personalizados
- Personalizando o controle CreateUserWizard
- Inícios Rápidos do Controle DetailsView
- Exibindo dados com o controle ListView
- Dissecando os controles de validação no ASP.NET 2.0
- Editando inserir e excluir dados
- Validação de formulário no ASP.NET
- Coletando informações de registro de usuário personalizadas
- Perfis no ASP.NET 2.0
- O controle asp:ListView
- Início Rápido de Perfis de Usuário
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.