ASP.NET- Inserindo Um milhão de registros no SQL Server


 Neste artigo vou mostrar como inserir 1 milhão de registros em uma tabela de um banco de dados SQL Server usando o Bogus e o EF Core em um projeto minimal API.

As minimal APIs ou APIs mínimas foram introduzidas no .NET 6 e são mais leves que os controladores sendo uma abordagem simplificada para criar APIs HTTP rápidas com ASP .NET Core.

Vamos criar um projeto minimal API chamado ApiDoMilhao no .NET 8 usando o template ASP.NET Core Web API.

A seguir vamos incluir os pacotes nugets:

O Bogus é uma biblioteca criada por Brian Chavez que se propõe a gerar dados fake para realização de testes e você pode consultar o seu repositório aqui.

Para usar a distribuição Nuget basta instalar o pacote no seu projeto :  install-package Bogus

A seguir vamos criar as pastas Entities e Context no projeto e na pasta Entities definir a entidade Produto :

public class Produto
{
 
public int Id { get; set; }
 
public string? Codigo { get; set; }
 
public string? Descricao { get; set; }
 
public string? Categoria { get; set; }
 
public decimal Preco { get; set; }
}

Na pasta Context vamos criar a classe de contexto AppDbContext:
 
public class AppDbContext : DbContext
{
 
public AppDbContext(DbContextOptions options) : base(options)
  {}

 
public DbSet<Produto> Produtos { get; set; }
}

No arquivo appsettings.json vamos definir a string de conexão com a base de dados:

{
"ConnectionStrings": {  "DefaultConnection": "Data Source=DESKTOP-DK57UNP\\sqlexpress;Initial Catalog=ProdutosFakeDB;Integrated Security=True;TrustServerCertificate=True;"
},
...

Com isso temos tudo pronto para gerar o banco e incluir os dados.

Definindo os endpoints e inserindo um milhão de registros

Na classe Program vamos incluir o código abaixo:

using ApiDoMilhao.Context;
using ApiDoMilhao.Entities;
using Bogus;
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
using System.Diagnostics;
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.UseSqlServer(connectionString));
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    await PopulaDatabase(dbContext);
}
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.MapGet("/produtos/{id}", async (AppDbContext _context, int id) =>
{
    var produto = await _context.Produtos.FirstOrDefaultAsync(p => p.Id == id);
    if (produto != null)
        return Results.Ok(produto);
    else
        return Results.NotFound();
})
    .WithName("GetProdutoPorId")
    .WithOpenApi(x => new OpenApiOperation(x)
    {
        Summary = "Obtém um produto pelo seu Id",
        Description = "Retorna a informação de um produto.",
        Tags = new List<OpenApiTag> { new OpenApiTag { Name = "Meus Produtos" } }
    });
app.Run();
static async Task PopulaDatabase(AppDbContext context)
{
    // cria o banco de dados
    await context.Database.EnsureDeletedAsync();
    await context.Database.EnsureCreatedAsync();
    // setup bogus 
    var faker = new Faker<Produto>();
    faker.RuleFor(p => p.Codigo, f => f.Commerce.Ean13());
    faker.RuleFor(p => p.Descricao, f => f.Commerce.ProductName());
    faker.RuleFor(p => p.Categoria, f => f.Commerce.Categories(1)[0]);
    faker.RuleFor(p => p.Preco, f => f.Random.Decimal(1, 1000));
    // gera 1 milhão de produtos
    var produtos = faker.Generate(1_000_000);
    var lotes = produtos
        .Select((p, i) => (Produto: p, Index: i))
        .GroupBy(x => x.Index / 100_000)
        .Select(g => g.Select(x => x.Produto).ToList())
        .ToList();
    // inclui lotes
    var stopwatch = new Stopwatch();
    stopwatch.Start();
    var contador = 0;
    foreach (var lote in lotes)
    {
        contador++;
        Console.WriteLine($"Inserindo lote {contador} de {lotes.Count}...");
        await context.Produtos.AddRangeAsync(lote);
        await context.SaveChangesAsync();
    }
    stopwatch.Stop();
    Console.WriteLine($"Tempo gasto : {stopwatch.Elapsed}");
}

Vamos entender as principais partes deste código:

  1. Configuração do Serviço e Banco de Dados:
  2. População do Banco de Dados com Dados Falsos:
  3. Configuração e Definição de Rotas:
  4. Uso do Swagger para Documentação:
  5. População do Banco de Dados no Início da Aplicação:
  6. Uso de Endpoints e Async/Await:

Agora é só alegria...

Executando o projeto teremos a execução dos comandos :

Após alguns minutos teremos a interface do Swagger exibindo o endpoint:

Vamos consultar o registro com Id igual  999999 :

Teremos o resultado a seguir:

Só para confirmar vamos abrir o banco no SQL Server Management Studio e exibir os últimos 10 registros da tabela Produtos:

Vemos o registro com id 1000000 e assim confirmamos que nossa tabela possui 1000000 de registros.

Pegue o código do projeto aqui:  ApiDoMilhao.zip

"Ninguém vos engane com palavras vãs; porque por estas coisas vem a ira de Deus sobre os filhos da desobediência. Portanto, não sejais seus companheiros. Porque noutro tempo éreis trevas, mas agora sois luz no Senhor; andai como filhos da luz"
Efésios 5:6-8

Referências:


José Carlos Macoratti