Compartilhar via


Inserir dados em um banco de dados usando .NET.NET Aspire

Neste artigo, você aprenderá a configurar projetos .NET.NET Aspire para propagar dados em um banco de dados durante a inicialização do aplicativo. .NET Aspire permite que você semente dados usando scripts de banco de dados ou Entity Framework Core para plataformas comuns, como SQL Server, PostgreSQL e MySQL.

Quando semear dados

A propagação de dados preenche previamente tabelas de banco de dados com linhas de dados para que elas estejam prontas para teste por meio do seu aplicativo. Talvez você queira inicializar dados para os seguintes cenários:

  • Desenvolva e teste manualmente diferentes recursos do seu aplicativo em relação a um conjunto significativo de dados, como um catálogo de produtos ou uma lista de clientes.
  • Execute pacotes de teste para verificar se os recursos se comportam de maneira específica com um determinado conjunto de dados.

A propagação manual de dados é entediante e demorada, portanto, você deve automatizar o processo quando possível. Use volumes para executar scripts de banco de dados para projetos .NET.NET Aspire durante a inicialização. Você também pode inicializar seu banco de dados usando ferramentas como Entity Framework Core, que lidam com muitas preocupações subjacentes para você.

Entender bancos de dados em contêineres

Por padrão, .NET.NET Aspire integrações de banco de dados dependem de bancos de dados em contêineres, que criam os seguintes desafios ao tentar inicializar dados:

  • .NET .NET Aspire destrói e recria contêineres sempre que o aplicativo é reiniciado, o que significa que, por padrão, você precisa seedar novamente o banco de dados toda vez que o aplicativo reiniciar.
  • Dependendo da tecnologia de banco de dados selecionada, a nova instância de contêiner pode ou não criar um banco de dados padrão, o que significa que você também pode ter que criar o próprio banco de dados.
  • Mesmo que exista um banco de dados padrão, ele provavelmente não terá o nome ou esquema desejados para seu aplicativo específico.

.NET .NET Aspire permite que você resolva esses desafios usando volumes e algumas configurações para semear dados efetivamente.

Dados de semente usando volumes e scripts SQL

Volumes são a maneira recomendada de inicializar automaticamente bancos de dados containerizados ao usar scripts SQL. Os volumes podem armazenar dados para vários contêineres de cada vez, oferecer alto desempenho e são fáceis de fazer backup ou migrar. Com .NET.NET Aspire, você configura um volume para cada contêiner de recursos usando o método ContainerResourceBuilderExtensions.WithBindMount, que aceita três parâmetros:

  • Origem: o caminho de origem da montagem do volume, que é o local físico em seu host.
  • de destino: o caminho de destino no contêiner dos dados que você deseja manter.

Considere o seguinte código de configuração de volume de um arquivo "Program.cs" em um projeto "" AppHost "" de exemplo:

var todosDbName = "Todos";
var todosDb = builder.AddPostgres("postgres")
    .WithEnvironment("POSTGRES_DB", todosDbName)
    .WithBindMount(
        "../DatabaseContainers.ApiService/data/postgres",
        "/docker-entrypoint-initdb.d")
    .AddDatabase(todosDbName);

Neste exemplo, os parâmetros do método .WithBindMount configuram o seguinte:

  • ../DatabaseContainers.ApiService/data/postgres define um caminho para o script SQL em seu projeto local que você deseja executar no contêiner para propagar dados.
  • /docker-entrypoint-initdb.d define o caminho para um ponto de entrada no contêiner para que o script seja executado durante a inicialização do contêiner.

O script SQL referenciado localizado em ../DatabaseContainers.ApiService/data/postgres cria e propaga uma tabela Todos:

-- Postgres init script

-- Create the Todos table
CREATE TABLE IF NOT EXISTS Todos
(
    Id SERIAL PRIMARY KEY,
    Title text UNIQUE NOT NULL,
    IsComplete boolean NOT NULL DEFAULT false
);

-- Insert some sample data into the Todos table
INSERT INTO Todos (Title, IsComplete)
VALUES
    ('Give the dog a bath', false),
    ('Wash the dishes', false),
    ('Do the groceries', false)
ON CONFLICT DO NOTHING;

O script é executado durante a inicialização sempre que uma nova instância de contêiner é criada.

Exemplos de inicialização de banco de dados

Os exemplos a seguir demonstram como propagar dados usando scripts SQL e configurações aplicadas usando o método .WithBindMount para diferentes tecnologias de banco de dados:

Nota

Visite o aplicativo de exemplo de contêiner de banco de dados e veja o projeto completo e a estrutura de arquivos para cada banco de dados.

O código de configuração no . AppHostProgram.cs arquivo monta os arquivos e pastas de banco de dados necessários e configura um ponto de entrada para que eles sejam executados durante a inicialização.

// SQL Server container is configured with an auto-generated password by default
// but doesn't support any auto-creation of databases or running scripts on startup so we have to do it manually.
var sqlserver = builder.AddSqlServer("sqlserver")
    // Mount the init scripts directory into the container.
    .WithBindMount("./sqlserverconfig", "/usr/config")
    // Mount the SQL scripts directory into the container so that the init scripts run.
    .WithBindMount("../DatabaseContainers.ApiService/data/sqlserver", "/docker-entrypoint-initdb.d")
    // Run the custom entrypoint script on startup.
    .WithEntrypoint("/usr/config/entrypoint.sh")
    // Configure the container to store data in a volume so that it persists across instances.
    .WithDataVolume()
    // Keep the container running between app host sessions.
    .WithLifetime(ContainerLifetime.Persistent);

O script entrypoint.sh reside na pasta de projeto ./sqlserverconfig montada e é executado quando o contêiner é iniciado. O script inicia SQL Server e verifica se ele está em execução.

#!/bin/bash

# Adapted from: https://github.com/microsoft/mssql-docker/blob/80e2a51d0eb1693f2de014fb26d4a414f5a5add5/linux/preview/examples/mssql-customize/entrypoint.sh

# Start the script to create the DB and user
/usr/config/configure-db.sh &

# Start SQL Server
/opt/mssql/bin/sqlservr

O script SQL init.sql que reside na pasta de projeto ../DatabaseContainers.ApiService/data/sqlserver montada cria o banco de dados e as tabelas.

-- SQL Server init script

-- Create the AddressBook database
IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = N'AddressBook')
BEGIN
  CREATE DATABASE AddressBook;
END;
GO

USE AddressBook;
GO

-- Create the Contacts table
IF OBJECT_ID(N'Contacts', N'U') IS NULL
BEGIN
    CREATE TABLE Contacts
    (
        Id        INT PRIMARY KEY IDENTITY(1,1) ,
        FirstName VARCHAR(255) NOT NULL,
        LastName  VARCHAR(255) NOT NULL,
        Email     VARCHAR(255) NULL,
        Phone     VARCHAR(255) NULL
    );
END;
GO

-- Ensure that either the Email or Phone column is populated
IF OBJECT_ID(N'chk_Contacts_Email_Phone', N'C') IS NULL
BEGIN
    ALTER TABLE Contacts
    ADD CONSTRAINT chk_Contacts_Email_Phone CHECK
    (
        Email IS NOT NULL OR Phone IS NOT NULL
    );
END;
GO

-- Insert some sample data into the Contacts table
IF (SELECT COUNT(*) FROM Contacts) = 0
BEGIN
    INSERT INTO Contacts (FirstName, LastName, Email, Phone)
    VALUES
        ('John', 'Doe', 'john.doe@example.com', '555-123-4567'),
        ('Jane', 'Doe', 'jane.doe@example.com', '555-234-5678');
END;
GO

Dados iniciais usando Entity Framework Core

Você também pode propagar dados em projetos .NET Aspire usando Entity Framework Core executando explicitamente migrações durante a inicialização. Entity Framework Core lida com conexões de banco de dados subjacentes e criação de esquema para você, o que elimina a necessidade de usar volumes ou executar scripts SQL durante a inicialização do contêiner.

Importante

Esses tipos de configurações só devem ser feitos durante o desenvolvimento, portanto, adicione uma condicional que verifique o contexto atual do ambiente.

Adicione o código a seguir ao arquivo do seu projeto do Serviço de API .

// Register DbContext class
builder.AddSqlServerDbContext<TicketContext>("sqldata");

var app = builder.Build();

app.MapDefaultEndpoints();

if (app.Environment.IsDevelopment())
{
    // Retrieve an instance of the DbContext class and manually run migrations during startup
    using (var scope = app.Services.CreateScope())
    {
        var context = scope.ServiceProvider.GetRequiredService<TicketContext>();
        context.Database.Migrate();
    }
}

Próximas etapas

O preenchimento de banco de dados é útil em diversos cenários de desenvolvimento de aplicativos. Tente combinar essas técnicas com as implementações de recursos demonstradas nos seguintes tutoriais: