Identity - Crud integrado
Hoje veremos como fazer o CRUD e uma entidade Aluno integrando a entidade com o Identity. |
O cenário é o seguinte :
"Você deseja criar uma aplicação WEB para gerenciar informações de alunos usando a ASP.NET Core , o EF Core e os recursos do Identity de forma integrada."
Assim você vai cadastrar, alterar e excluir alunos em uma tabela Alunos e vai também incluir o aluno nas tabela do Identity.
Criando o projeto Web
No VS 2022 Community crie um novo projeto ASP.NET Core Web App (Model-View-Controller) chamado AlunosWebMvc e a seguir instale os seguintes pacotes:
1. Microsoft.AspNetCore.Identity
2. Microsoft.AspNetCore.Identity.EntityFrameworkCore
3. Microsoft.EntityFrameworkCore.Design
4. Microsoft.EntityFrameworkCore.SqlServer
Instale também a ferramenta EF Core Tools no seu ambiente usando o seguinte
comando :
dotnet tool install --global dotnet-ef
No projeto criado inclua uma pasta Entities e nesta pasta crie a classe
Aluno que herda de IdentityUser:
public
class
Aluno
: IdentityUser { [MaxLength(100)] |
A tabela "AspNetUsers" já é criada automaticamente pelo Identity quando você configura a autenticação em sua aplicação. A ideia de estender a classe "IdentityUser" é adicionar suas próprias propriedades personalizadas à tabela "AspNetUsers", que será usada para armazenar informações do usuário, além das informações padrão fornecidas pelo Identity, como o email, nome de usuário e senha.
A seguir crie uma pasta Context e nesta pasta crie o arquivo AppDbContext:
public
class
AppDbContext
: IdentityDbContext<Aluno> { // construtor public AppDbContext(DbContextOptions<AppDbContext> options) : base(options){} protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.Entity<Aluno>() .HasKey(u => u.Id); builder.Entity<Aluno>() .ToTable("AspNetUsers"); } } |
Observe que
estamos mapeando a tabela Aluno para
AspNetUsers do Identity.
Configurando a conexão e o Identity
No arquivo appsetings.json inclua a string de conexão definindo o nome do banco de dados:
{ "ConnectionStrings": { "DefaultConnection": "Server=DESKTOP-DK57UNP\\SQLEXPRESS;Database=AlunosWebDB;Trusted_Connection=True; TrustServerCertificate=True;MultipleActiveResultSets=true;" }, ... |
A seguir na classe Program do projeto inclua o código a seguir:
... var builder = WebApplication.CreateBuilder(args);// Add services to the container. builder.Services.AddControllersWithViews(); var connection = builder.Configuration.GetConnectionString("DefaultConnection");builder.Services.AddDbContext<AppDbContext>(options => builder.Services.AddIdentity<Aluno, IdentityRole>() app.UseAuthentication(); |
Aplicando Migrations
Vamos aplicar o migrations para gerar o banco de dados e as tabelas.
Para isso vamos usar a ferramenta dotnet ef.
1- Para criar o script de migração abra a janela Package Manager Console e execute o comando:
dotnet ef migrations add Inicial --project AlunosWebMvc -s AlunosWebMvc --verbose
2- A seguir , após você verificar o script , execute o seguinte comando para aplicar o script de migração:
dotnet ef database update --project AlunosWebMvc --startup-project AlunosWebMvc --verbose
Com isso teremos o banco de dados e as tabelas geradas com o seguinte relacionamento no SQL Server:
A seguir temos o papel de cada tabela gerada:
_EFMigrationsHistory – contém todas as migrações anteriores realizadas.
AspNetRoleClaims – armazena as declarações (claims) por perfis ou roles.
AspNetRoles – armazena todas as roles
AspNetUserClaims – armazena declarações(claims) de usuários.
AspNetUserLogins – armazena o tempo de login dos usuários.
AspNetUserRoles – armazena funções de usuários.
AspNetUsers – armazena todos os usuários.
AspNetUserTokens – armazena tokens de autenticação externos.
Criando o controlador e as views
Vamos criar na pasta Controllers o controlador AlunosController com o código abaixo:
public class AlunosController :
Controller { private readonly AppDbContext _context;
public AlunosController(AppDbContext context)
public IActionResult Index()
public IActionResult Create()
[HttpPost]
public async Task<IActionResult> Edit(string id)
var aluno = await _context.Users.FindAsync(id);
[HttpPost]
if (ModelState.IsValid)
public async Task<IActionResult> Details(string id)
var aluno = await _context.Users.FindAsync(id);
return View(aluno);
public async Task<IActionResult> Delete(string id)
var aluno = await _context.Users.FirstOrDefaultAsync(m => m.Id == id);
return View(aluno);
[HttpPost, ActionName("Delete")]
private bool AlunoExists(string id) |
Agora para cada método Action basta criar uma view correspondente. Vou mostrar o código da view Index que apresenta uma visão dos alunos cadastrados:
@model IEnumerable<Aluno> <h2>Alunos</h2> <p> <table class="table"> |
As demais views você pode obter no código do projeto.
Executando o projeto teremos o seguinte resultado:
Pode ocorrer na edição dos dados a exceção : DbUpdateConcurrencyException : database operation expected to affect 1 row(s) but actually affected 0 row(s)
O problema provavelmente ocorre devido à otimização de concorrência do Entity Framework Core e do Identity Framework. Quando você edita os dados de um usuário usando o Identity Framework, o Entity Framework Core atualiza automaticamente o valor do ConcurrencyStamp na tabela AspNetUsers para garantir que as alterações sejam sincronizadas corretamente. Isso é necessário para impedir que um usuário edite os dados de outro usuário enquanto ambos estiverem editando os mesmos dados.
Quando você tenta salvar as alterações usando o método SaveChangesAsync, o Entity Framework Core verifica o valor do ConcurrencyStamp para garantir que o registro ainda não tenha sido alterado por outro usuário. Se o valor do ConcurrencyStamp no banco de dados for diferente do valor que você está tentando salvar, o Entity Framework Core lançará uma exceção de concorrência (ConcurrencyException).
Para resolver esse problema, você pode recarregar os dados do usuário do banco de dados antes de aplicar as alterações e salvar as alterações em uma transação separada. Isso garantirá que o ConcurrencyStamp seja atualizado corretamente e que as alterações sejam salvas sem erros de concorrência.
Adotei uma solução mais simples só para permitir a execução:
[HttpPost] public async Task<IActionResult> Edit(string id, Aluno aluno) { if (id != aluno.Id) { return NotFound(); }
var alunoDb = await _context.Users.FindAsync(id); alunoDb.Curso = aluno.Curso; if (ModelState.IsValid) |
Pegue o projeto aqui : AlunosWebMvc.zip ...
"Todavia para nós há um só Deus, o Pai, de quem é tudo e para quem nós
vivemos; e um só Senhor, Jesus Cristo, pelo qual são todas as coisas, e nós por
ele."
Coríntios 8:6
Referências:
NET - Unit of Work - Padrão Unidade de ...