Modelos, DbContext e migrations
ResumoCriamos as entidades do domínio (
CategoriaeProduto), oDbContextna camada de repositório, e geramos a migration inicial com dados de seed usando o EF Core.
1. Objetivos de Aprendizagem#
- Modelar entidades com um relacionamento pai/filho.
- Configurar o
DbContexte o mapeamento via Fluent API. - Gerar migrations e semear dados iniciais.
2. Pré-requisitos#
- Estrutura de camadas do tópico 17.1.
3. Conceito#
O EF Core é um ORM: mapeia classes C# para tabelas. O DbContext representa uma sessão com o banco e expõe DbSet<T> por entidade. Migrations versionam a evolução do schema como código. O porquê de manter isso na camada de repositório: infraestrutura não deve vazar para o domínio.
Modelo do curso: uma Categoria tem muitos Produtos (relacionamento 1:N).
4. Mão na Massa#
4.1. Setup#
dotnet add Catalog.Repository package Microsoft.EntityFrameworkCore.SqlServer
dotnet add Catalog.Api package Microsoft.EntityFrameworkCore.Design
dotnet tool install --global dotnet-ef # se ainda não tiver
4.2. Implementação Passo a Passo#
- Entidades em
Catalog.Entities/Models:
namespace Catalog.Entities.Models;
public class Categoria
{
public Guid Id { get; set; }
public string Nome { get; set; } = default!;
public ICollection<Produto> Produtos { get; set; } = new List<Produto>();
}
public class Produto
{
public Guid Id { get; set; }
public string Nome { get; set; } = default!;
public decimal Preco { get; set; }
public Guid CategoriaId { get; set; }
public Categoria? Categoria { get; set; }
}
DbContextemCatalog.Repository:
using Catalog.Entities.Models;
using Microsoft.EntityFrameworkCore;
namespace Catalog.Repository;
public class RepositoryContext : DbContext
{
public RepositoryContext(DbContextOptions<RepositoryContext> options) : base(options) { }
public DbSet<Categoria> Categorias { get; set; } = default!;
public DbSet<Produto> Produtos { get; set; } = default!;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Produto>()
.HasOne(p => p.Categoria)
.WithMany(c => c.Produtos)
.HasForeignKey(p => p.CategoriaId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Categoria>().HasData(
new Categoria { Id = Guid.Parse("d2f1..."), Nome = "Periféricos" });
}
}
- Registre no
Program.cs:
builder.Services.AddDbContext<RepositoryContext>(opts =>
opts.UseSqlServer(builder.Configuration.GetConnectionString("SqlConnection")));
appsettings.json:
{
"ConnectionStrings": {
"SqlConnection": "Server=localhost;Database=CatalogDb;Trusted_Connection=True;TrustServerCertificate=True;"
}
}
4.3. Executando#
dotnet ef migrations add InitialCreate --project Catalog.Repository --startup-project Catalog.Api
dotnet ef database update --project Catalog.Repository --startup-project Catalog.Api
5. Exemplo Completo#
Registro do contexto via método de extensão:
public static void ConfigureSqlContext(this IServiceCollection services, IConfiguration config) =>
services.AddDbContext<RepositoryContext>(o =>
o.UseSqlServer(config.GetConnectionString("SqlConnection")));
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
| Definir chaves/relacionamentos via Fluent API | Confiar só em convenções para relacionamentos complexos |
Usar valores fixos e determinísticos no HasData | Usar Guid.NewGuid() no seed (gera migrations instáveis) |
| Guardar a connection string fora do código | Comitar credenciais reais no appsettings.json |
Atenção
DbContexté registrado comoScopedporAddDbContext. Nunca o injete em umSingleton— geraria captive dependency e não é thread-safe.
7. Segurança e Produção#
- Connection strings de produção pertencem a user-secrets (dev) e variáveis de ambiente/secret store (prod), nunca ao repositório.
TrustServerCertificate=Trueé aceitável só em dev; em produção use certificados válidos.
8. Exercícios#
- Fácil: adicione a propriedade
Estoque(int) emProdutoe crie uma nova migration. - Médio: semeie três produtos vinculados à categoria existente.
- Desafio: troque
DeleteBehavior.CascadeporRestricte explique o impacto ao deletar uma categoria com produtos.
9. Resumo#
Modelamos Categoria/Produto, configuramos o RepositoryContext na camada de repositório e geramos migrations com seed determinístico. O DbContext fica isolado da apresentação.
10. Próximos Passos#
A seguir aplicamos o Repository Pattern e a Service Layer sobre esse contexto.
11. Referências#
- Microsoft Learn — Primeiros passos com EF Core
- Microsoft Learn — Migrations do EF Core
- Microsoft Learn — Data seeding