Minimal APIs - Populando tabelas com EF Core
 Hoje veremos como popular tabelas usando o EF Core em um projeto Minimal API.

Vamos partir de um projeto ASP .NET Core usando as minimal APIs que acessa uma base SQL Server e possui a seguinte estrutura:

Modelo de domínio: Product

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace SeedDataEF.Models;

public class Product
{
    public int ProductId { get; set; }
    [MaxLength(100)]
    public string? Name { get; set; }

    [Column(TypeName = "decimal(10, 2)")]
    public decimal Price { get; set; }     
}

Arquivo de contexto : AppDbContext

using Microsoft.EntityFrameworkCore;

namespace SeedDataEF.Models;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> opt): base(opt)
    {}
    public DbSet<Product>? Products { get; set; }
}

Arquivo appsettings.json :

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=sqlexpress;Initial Catalog=DemoDB;Integrated Security=True"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Arquivo Program:

using Microsoft.EntityFrameworkCore;
using SeedDataEF.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var conn = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<AppDbContext>(x => x.UseSqlServer(conn));

var app = builder.Build();

PopulaDatabase(app);

app.MapGet("/produtos", async AppDbContext dbContext) =>
await dbContext.Products.ToListAsync());

app.MapGet("/produtos/{id}", async (int id, AppDbContext dbContext) =>
await dbContext.Products.FirstOrDefaultAsync(a => a.ProductId == id));

app.UseSwagger();
app.UseSwaggerUI();

app.Run();

Temos definidos apenas dois endpoints para listar produtos e um produto pelo id.

Com isso podemos aplicar o EF Core Migrations para gerar o banco e as tabelas executando os comandos :

- donet ef migrations add Inicial
- donet ef database update

Nota: Para executar estes comandos o projeto precisa ter uma referência ao pacote nuget : Microsoft.EntityFrameworkCore.Tools

Ao final teremos o banco e a tabela criados mas não teremos nenhuma informação incluída na tabela e executando o projeto teremos o seguinte resultado:

Populando a tabela com dados

Para popular a tabela Products com dados iniciais vamos criar uma classe chamada SeedData na pasta Models:

namespace SeedDataEF.Models;

public class SeedData
{
    private readonly AppDbContext _context;

    public SeedData(AppDbContext context)
    {
        _context = context;
    }

    public void Seed()
    {
        if (!_context.Products.Any())
        {
            var products = new List<Product>()
            {
                    new Product()
                    {
                        Price = 52.45M,
                        Name = "Camera"
                    },
                    new Product()
                    {
                        Price = 3.15M,
                        Name = "Mouse"
                    }

            };

            _context.Products.AddRange(products);       
            _context.SaveChanges();
        }
    }
}

A título de exemplo estamos incluindo dois produtos na tabela Products onde não informarmos o valor da coluna Identity.

A seguir vamos alterar o código da classe Program registrando o serviço para a classe que popula a tabela como AddTransient e criando uma função local onde vamos usar a instância deste serviço para chamar o método para popular a tabela.

using Microsoft.EntityFrameworkCore;
using SeedDataEF.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var conn = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<AppDbContext>(x => x.UseSqlServer(conn));
builder.Services.AddTransient<SeedData>();

var app = builder.Build();

if (args.Length == 1 && args[0].ToLower() == "popular")
    PopulaDatabase(app);

app.MapGet("/produtos", async (AppDbContext dbContext) =>
      await dbContext.Products.ToListAsync());

app.MapGet("/produtos/{id}", async (int id, AppDbContext dbContext) =>
      await dbContext.Products.FirstOrDefaultAsync(a => a.ProductId == id));

app.UseSwagger();
app.UseSwaggerUI();

app.Run();

void PopulaDatabase(IHost app)
{
    var scopedFactory = app.Services.GetService<IServiceScopeFactory>();

    using (var scope = scopedFactory.CreateScope())
    {
        var service = scope.ServiceProvider.GetService<SeedData>();
        service.Seed();
    }
}

Na linha de código:

if (args.Length == 1 && args[0].ToLower() == "popular")
       PopulaDatabase(app);

Podemos chamar a função para popular a tabela usando a linha de comando e passando o argumento 'popular'.

Executando o projeto veremos que a tabela Products vai ser preenchida com os dados dos dois produtos.

Pegue o projeto aqui :  SeedDataEF.zip (sem as referências)

"E não vos embriagueis com vinho, em que há contenda, mas enchei-vos do Espírito;
Falando entre vós em salmos, e hinos, e cânticos espirituais; cantando e salmodiando ao Senhor no vosso coração;"
Efésios 5:18,19

Referências:


José Carlos Macoratti