Minimal APIs - Populando dados com EF Core
 Hoje veremos como popular com dados iniciais tabelas de um projeto usando o recurso minimal APIs.

Vamos partir de um projeto Web API simples que realiza o CRUD em um banco de dados PostgreSQL que foi criado usando a abordagem das minimal APIs e onde já foi incluído o recurso da OpenAPI.

No projeto já temos também a referência ao provedor NpgSql.EntityFrameworkCore.PostgreSQL, a string de conexão e o arquivo de contexto - AppDbContext :

using Api_SeedData.Models;
using Microsoft.EntityFrameworkCore;

namespace Api_SeedData.Context;

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

    public DbSet<Student>? Students { get; set; }

    protected override void OnModelCreating(ModelBuilder mb)
    {
        mb.Entity<Student>().HasKey(c => c.StudentId);

        mb.Entity<Student>().Property(c => c.Name)
                            .HasMaxLength(100)
                            .IsRequired();

        mb.Entity<Student>().Property(c => c.Email)
                            .HasMaxLength(150)
                            .IsRequired();
    }
}

Temos também definida no projeto a entidade Student:

public class Student
{
    public int StudentId { get; set; }
    public string? Name { get; set; }
    public string? Email { get; set; }
    public int Age { get; set; }
}

E no arquivo Program já registramos o serviço do contexto no container :

using Api_SeedData.Context;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var connectionString = builder.Configuration
                                .GetConnectionString("DefaultConnection"); 

builder.Services.AddDbContext<AppDbContext>(options =>
                             options.UseNpgsql(connectionString));

var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();

app.Run();

Assim podemos usar a ferramenta EF Core Tools para aplicar o Migrations e gerar o banco e a tabela Students no PostgreSQL:

dotnet ef migrations add Initial

Após conferir o arquivo de script gerado podemos aplicar a migração usando o comando:

dotnet ef database update

Agora já temos a tabela criada porém ela esta vazia. Vamos então popular a tabela com dados iniciais

Populando a tabela com dados iniciais

Para isso vamos criar uma classe chamada SeedData com o código abaixo:

using Api_SeedData.Context;

namespace Api_SeedData.Models;

public class SeedData
{
    private readonly AppDbContext context;

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

    public void Seed()
    {
        if (!context.Students.Any())
        {
            var students = new List<Student>()
                {
                        new Student()
                        {
                            StudentId = 1,
                            Name = "Carlos",
                            Email = "carlos@email.com",            
                            Age = 17

                        },
                        new Student()
                        {
                            StudentId = 2,
                            Name = "Maria",
                            Email = "maria@email.com",
                            Age = 16
                        }
                };

            context.Students.AddRange(students);
            context.SaveChanges();

        }
    }
}

Neste código estamos usando uma instância do nosso contexto - AppDbContext - e criando dois objetos students e a seguir usando o método AddRange estamos incluindo os dados na tabela.

Agora podemos incluir na classe Program o código abaixo para fazer a chamada a esta classe e usar o método Seed() via linha de comando.

using Api_SeedData.Context;
using Api_SeedData.Models;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddTransient<SeedData>();

var connectionString = builder.Configuration
                       .GetConnectionString("DefaultConnection");

builder.Services.AddDbContext<AppDbContext>(options =>
                  options.UseNpgsql(connectionString));

var app = builder.Build();

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

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

app.Run();

void SeedDataStudent(IHost app)
{
    var scopedFactory = app.Services.GetService<IServiceScopeFactory>();
    using (var scope = scopedFactory.CreateScope())
    {
        var service = scope.ServiceProvider.GetService<SeedData>();
        service.Seed();
    }
}

Neste código:

1- Fizemos o registro do serviço SeedDatabuilder.Services.AddTransient<SeedData>();

2- A seguir definimos o código para chamar o método SeedDataStudent() passando uma instância de app na linha de comando :

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

Neste código temos que passar apenas um argumento na linha de comando e o primeiro argumento deve ser a string 'seeddata'.

3- A seguir o método SeedDataStudent(IHost app) vai receber a instância de app e usando o padrão service locator vai localizar o serviço registrado da classe SeedData e invocar o método Seed() para popular a tabela com dados.

Após isso podemos executar o projeto usando o comando : dotnet run seeddata

Examinando a tabela Students no PostgreSQL usando o PgAdmin temos:



Com isso temos os dados incluídos na tabela conforme o esperado.

Podemos agora definir alguns endpoints na classe Program para realizar o CRUD básico:

using Api_SeedData.Context;
using Api_SeedData.Models;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddTransient<SeedData>();

var connectionString = builder.Configuration
                       .GetConnectionString("DefaultConnection");

builder.Services.AddDbContext<AppDbContext>(options =>
                  options.UseNpgsql(connectionString));

var app = builder.Build();

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

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

app.MapGet("/students", async (AppDbContext db) => await db.Students.ToListAsync());

app.MapGet("/students/{id:int}", async (int id, AppDbContext db)
    =>
{
    return await db.Students.FindAsync(id)
                 is Student student
                 ? Results.Ok(student)
                 : Results.NotFound();
});

app.MapPut("/students/{id:int}", async (int id, Student student, AppDbContext db) =>
{

    if (student.StudentId != id)
    {
        return Results.BadRequest();
    }

    var studentDB = await db.Students.FindAsync(id);

    if (studentDB is null) return Results.NotFound();

    studentDB.Name = student.Name;
    studentDB.Email = student.Email;
    studentDB.Age = student.Age;

    await db.SaveChangesAsync();

    return Results.Ok(studentDB);
});

app.MapDelete("/students/{id:int}", async (int id, AppDbContext db) =>
{
    var student = await db.Students.FindAsync(id);

    if (student is null)
    {
        return Results.NotFound();
    }

    db.Students.Remove(student);
    await db.SaveChangesAsync();

    return Results.NoContent();
});

app.Run();

void SeedDataStudent(IHost app)
{
    var scopedFactory = app.Services.GetService<IServiceScopeFactory>();
    using (var scope = scopedFactory.CreateScope())
    {
        var service = scope.ServiceProvider.GetService<SeedData>();
        service.Seed();
    }
}

Executando o projeto teremos na interface do Swagger os endpoints mapeados:

Pegue o projeto aqui :  Api_SeedData.zip

"O ímpio tem muitas dores, mas àquele que confia no Senhor a misericórdia o cercará."
Salmos 32:10

E estamos conversados.

Referências:


José Carlos Macoratti