Поделиться через


Начальные данные в базе данных с помощью .NET.NET Aspire

Из этой статьи вы узнаете, как настроить .NET.NET Aspire проекты для записи данных в базе данных при инициализации приложения. .NET Aspire позволяет инициировать данные с помощью скриптов базы данных или Entity Framework Core для общих платформ, таких как SQL Server, PostgreSQL и MySQL.

Когда следует заполнять данные

Предварительное заполнение данных наполняет таблицы баз данных строками, чтобы они были готовы к тестированию через ваше приложение. Вы можете использовать начальные данные для следующих сценариев:

  • Вручную разрабатывайте и тестируйте различные функции приложения на основе понятного набора данных, например каталога продуктов или списка клиентов.
  • Запустите наборы тестов, чтобы убедиться, что функции ведут себя определенным образом с заданным набором данных.

Ручное внесение данных утомительно и занимает много времени, поэтому по возможности следует автоматизировать этот процесс. Используйте тома для запуска скриптов базы данных для .NET.NET Aspire проектов во время запуска. Вы также можете заполнить базу данных с помощью таких инструментов, как Entity Framework Core, которые решают множество базовых задач для вас.

Общие сведения о контейнерных базах данных

По умолчанию интеграция с базами данных .NET.NET Aspire зависит от контейнерных баз данных, которые создают следующие проблемы при попытке заполнения данных:

  • .NET .NET Aspire уничтожает и повторно создает контейнеры при каждом перезапуске приложения, что означает, что по умолчанию необходимо повторно заполнить базу данных при каждом перезапуске приложения.
  • В зависимости от выбранной технологии базы данных новый экземпляр контейнера может или не может создать базу данных по умолчанию, что означает, что вам также придется создать саму базу данных.
  • Даже если база данных по умолчанию существует, скорее всего, не будет иметь требуемое имя или схему для конкретного приложения.

.NET .NET Aspire дает возможность решать эти задачи с помощью объемов и нескольких конфигураций для эффективного наполнения данными.

Начальные данные с помощью томов и скриптов SQL

Рекомендуемый способ автоматического заполнения контейнеризированных баз данных с использованием SQL-скриптов — это использование томов. Тома могут хранить данные для нескольких контейнеров одновременно, обеспечивать высокую производительность и их легко можно создавать резервные копии или мигрировать. При использовании .NET.NET Aspireвы настраиваете том для каждого контейнера ресурсов с помощью метода ContainerResourceBuilderExtensions.WithBindMount, который принимает три параметра:

  • Источник: исходный путь монтажа тома, который является физическим расположением на вашем хосте.
  • Целевой: путь назначения в контейнере данных, которые нужно удерживать.

Обратите внимание на следующий код конфигурации тома из файла Program.cs в примере проекта AppHost:

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

В этом примере параметры метода .WithBindMount настраивают следующие параметры:

  • ../DatabaseContainers.ApiService/data/postgres задает путь к SQL-скрипту в вашем локальном проекте, который вы хотите запустить в контейнере для инициализации данных.
  • /docker-entrypoint-initdb.d задает путь к точке входа в контейнере, чтобы скрипт выполнялся во время запуска контейнера.

Указанный сценарий SQL, расположенный в ../DatabaseContainers.ApiService/data/postgres, создаёт и заполняет таблицу 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;

Скрипт выполняется во время запуска при каждом создании нового экземпляра контейнера.

Примеры заполнения базы данных

В следующих примерах показано, как заполнять данные с помощью скриптов и конфигураций SQL, применяемых с помощью метода .WithBindMount для различных технологий базы данных:

Заметка

Перейдите к приложению-примеру контейнера базы данных , чтобы ознакомиться с полной структурой проекта и файлов для каждого варианта базы данных.

Код конфигурации в файле .AppHostProgram.cs монтирует необходимые файлы и папки базы данных и настраивает точку входа, чтобы они запускались при старте системы.

// 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);

Скрипт entrypoint.sh находится в подключенной папке проекта ./sqlserverconfig и запускается при запуске контейнера. Скрипт запускает SQL Server и проверяет, запущен ли он.

#!/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

Скрипт init.sql SQL, который находится в подключенной папке проекта ../DatabaseContainers.ApiService/data/sqlserver, создает базу данных и таблицы.

-- 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

Начальные данные с помощью Entity Framework Core

Вы также можете добавлять начальные данные в проекты .NET Aspire с помощью Entity Framework Core, запуская миграции во время старта. Entity Framework Core обрабатывает базовые подключения к базе данных и создание схемы, что устраняет необходимость использования томов или запуска скриптов SQL во время запуска контейнера.

Важный

Эти типы конфигураций должны выполняться только во время разработки, поэтому обязательно добавьте условие, которое проверяет текущий контекст среды.

Добавьте следующий код в файл проекта службы 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();
    }
}

Дальнейшие действия

Заполнение базы данных полезно в различных сценариях разработки приложений. Попробуйте объединить эти методы с реализациями ресурсов, показанными в следующих руководствах: