ASP .NET Core 3.1 - Usando o Identity de cabo a rabo - XII


Hoje vamos continuar a série de artigos mostrando como usar o ASP .NET Core Identiy na versão 3.1 da ASP .NET .Core e do EF Core.

Continuando a décima primeira parte do artigo veremos como estender a classe IdentityUser.

Estendendo o IdentityUser

A ASP .NET Core utiliza a classe padrão IdentityUser que possui um conjunto de propriedades como Id, Username, Email, PasswordHash, etc. 

Abaixo temos o código fonte desta classe:

public class IdentityUser<TKey> where TKey : IEquatable<TKey>
{
    public IdentityUser() { }
    public IdentityUser(string userName) : this()
    {
        UserName = userName;
    }
    [PersonalData]
    public virtual TKey Id { get; set; }
    [ProtectedPersonalData]
    public virtual string UserName { get; set; }
    public virtual string NormalizedUserName { get; set; }
    [ProtectedPersonalData]
    public virtual string Email { get; set; }
    public virtual string NormalizedEmail { get; set; }
    [PersonalData]
    public virtual bool EmailConfirmed { get; set; }
    public virtual string PasswordHash { get; set; }
    public virtual string SecurityStamp { get; set; }
    public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();
    [ProtectedPersonalData]
    public virtual string PhoneNumber { get; set; }
    [PersonalData]
    public virtual bool PhoneNumberConfirmed { get; set; }
    [PersonalData]
    public virtual bool TwoFactorEnabled { get; set; }
    public virtual DateTimeOffset? LockoutEnd { get; set; }
    public virtual bool LockoutEnabled { get; set; }
    public virtual int AccessFailedCount { get; set; }
    public override string ToString()
        => UserName;
}

E, se eu quiser armazenar dados adicionais do usuário como Sexo, Cidade, Pais, etc. ?

Note que a classe IdentityUser não possui essas propriedades.

Como faremos ?

Para armazenar dados personalizados do usuário teremos que estender a classe IdentityUser.

Temos que criar uma classe que estende a classe IdentityUser, e, podemos usar qualquer nome para esta classe, mas o nome habitualmente usado  é ApplicationUser.

No exemplo abaixo, a classe ApplicationUser estende a classe IdentityUser, e, nela incluímos apenas a propriedade Cidade que desejamos incluir,  mas você pode incluir quantas propriedades desejar.

using Microsoft.AspNetCore.Identity;

namespace FuncionariosWeb.Models
{
     public class ApplicationUser : IdentityUser
     {
          public string Cidade { get; set; }
      }
}

A seguir localiza todas as referência da classe IdentityUser e substitua cada referência pela nossa classe ApplicationUser.

A maneira mais fácil de fazer isso é, clique com o botão direito do mouse na classe IdentityUser e selecione "Localizar todas as referências" no menu de contexto.

Navegue para cada referência na lista e substitua a classe IdentityUser pela classe ApplicationUser.

Especifique a classe ApplicationUser como o argumento genérico da classe IdentityDbContext no arquivo AppDbContext:

public class AppDbContext : IdentityDbContext<ApplicationUser>
{
     public AppDbContext(DbContextOptions<AppDbContext> options)
     : base(options)
      { }

     public DbSet<Funcionario> Funcionarios { get; set; }
}

É assim que a classe IdentityDbContext sabe que precisa trabalhar com nossa classe de usuário personalizada (neste caso, classe ApplicationUser) em vez da classe IdentityUser padrão.

Com esta etapa concluído podemos agora aplicar o Migrations para gerar a coluna Cidade na tabela AspNetUsers.

No VS 2019 abra a janela do Gerenciador de Pacotes e digite o comando: Add-Migration Estender_Identity

Não ocorrendo erros podemos concluir aplicando o script gerado emitindo o comando: Update-Database

Podemos abrir a tabela AspNetUsers no SQL Server Management Studio e verificar a nova coluna Cidade incluída:

Agora temos que incluir em nossa aplicação os ajustes que vão permitir o usuário informar o nome da cidade e ela ser persistida na tabela.

Para isso temos que incluir a propriedade na classe RegisterViewModel

 public class RegisterViewModel
    {
        [Required(ErrorMessage="O email é obrigatório")]
        [EmailAddress(ErrorMessage="Email inválido")]
        [Remote(action: "IsEmailInUse", controller: "Account")]
        public string Email { get; set; }
        [Required(ErrorMessage = "A senha é obrigatória")]
        [DataType(DataType.Password)]
        public string Password { get; set; }
        [DataType(DataType.Password)]
        [Display(Name = "Confirme a senha")]
        [Compare("Password",ErrorMessage = "As senhas não conferem")]
        public string ConfirmPassword { get; set; }

        public string Cidade { get; set; }
    }

A seguir alterar a view Register.cshtml :

@model RegisterViewModel
@{
    ViewBag.Title = "User Registration";
}
<h3>Registrar Usuário</h3>
<div class="row">
    <div class="col-md-12">
        <form method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
              ...
            <div class="form-group">
                <label asp-for="Cidade"></label>
                <input asp-for="Cidade" class="form-control" />
            </div>
            <button type="submit" class="btn btn-primary">Registrar</button>
        </form>
    </div>
</div>

E para concluir temos que alterar o método Action HttpPost Register do controlador AccountController:

        ...
        [HttpPost]
        public async Task<IActionResult> Register(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                // Copia os dados do RegisterViewModel para o ApplicationUser
                var user = new ApplicationUser
                {
                    UserName = model.Email,
                    Email = model.Email,
                    Cidade = model.Cidade
                };
                ...
               }
            return View(model);
        }

Neste código estamos atribuindo à propriedade Cidade da instância ApplicationUser que é passada para o método CreateAsync() da classe UserManager.

Os dados na instância ApplicationUser são salvos na tabela AspNetUsers pela classe IdentityDbContext.

Executando o projeto novamente e fazendo um novo registro teremos:

Podemos agora registrar um novo usuário e informar a cidade do usuário persistindo os dados na tabela AspNetUsers.

No próximo artigo veremos como criar roles na ASP .NET Core usando a API Identiy.

"Quem tem o Filho tem a vida; quem não tem o Filho de Deus não tem a vida."
1 João 5:12

Referências:


José Carlos Macoratti