-
Notifications
You must be signed in to change notification settings - Fork 0
Docs (RU)
- Для быстрого разворачивания микросервиса, можно воспользоваться моим проектом для инициализации
.NET 8 Web Api Onion Architecture Microservice: NET8-Onion-Architecture-Microservice-Template. - Для удобного использования советую сразу в проект устанавливать все 3 библиотеки, т.к. каждая из них может пригодиться в той или иной степени.
- Перед добавлением библиотек советую удалить автоматически добавляемый NuGet
Swashbuckle.AspNetCoreв проектеASP .NET 8 Web API, т.к. данный NuGet пакет содержится вAspNetCoreMicroserviceInitializer.TradingDesk. - Для настройки микросервиса (его серверной части) и добавления модулей из библиотеки используется новый класс -
WebApplicationFacade, который служит "оберткой" дляWebApplicationиWebApplicationBuilderи позволяет гибко настроить желаемые модули.
public class Program
{
public static void Main(string[] args)
{
var modules = new List<WebApplicationModules>
{
WebApplicationModules.Database,
WebApplicationModules.Settings,
WebApplicationModules.Services,
WebApplicationModules.Serilog,
WebApplicationModules.HealthChecks
};
var dockerComposeModules = new List<DockerComposeFileModules>
{
DockerComposeFileModules.Server,
DockerComposeFileModules.PostgreSql,
DockerComposeFileModules.Adminer
};
var app = new WebApplicationFacade(modules)
.InitBaseConfig()
.InitBaseDockerComposeFiles(dockerComposeModules)
.AddAdditionalModules(builder =>
{
builder.Services.AddGrpc();
builder.Services.AddScoped<ICatsFactRepository, CatsFactRepository>();
})
.CreateApplication();
app.MapGrpcServices();
app.Run();
}
}- Все модули, которые можно добавить в микросервис с помощью библиотек, а так же условия их добавления ("summary" и "remarks") описаны ниже:
/// <summary>
/// Модули веб-приложения.
/// </summary>
public enum WebApplicationModules
{
/// <summary>
/// Модуль для автоматической регистрации настроек конфига.
///
/// Для корректной работы данного модуля необходимо:
/// 1. Добавить в приложение модель настроек конфига.
/// 2. Создать в конфиге элемент модели настроек (название класса и название элемента конфига должны совпадать).
/// 3. Присвоить моделям настроек атрибут <see cref="AutoRegisterConfigSettingsAttribute"/>.
/// </summary>
/// <remarks>Регистрация моделей будет произведена автоматически, используя атрибут <see cref="AutoRegisterConfigSettingsAttribute"/>.</remarks>
Settings = 0,
/// <summary>
/// Модуль для автоматической регистрации сервисов.
///
/// Для корректной работы данного модуля необходимо:
/// 1. Создать сервис и присвоить ему атрибут <see cref="AutoRegisterServiceAttribute"/>.
/// </summary>
/// <remarks>
/// При необходимости добавления фабричной функции во время регистрации сервиса, необходимо унаследовать созданный сервис от <see cref="ServiceBase"/>,
/// и переопределить в созданном сервисе метод <see cref="ServiceBase.ImplementationFactory"/>.
/// необходимо
/// </remarks>
Services = 1,
/// <summary>
/// Модуль добавления в приложение модуля для работы с Sql базами данных.
///
/// Для корректной работы данного модуля необходимо:
/// 1. Создать модели <see cref="DbContext"/>.
/// 2. Создать репозитории для работы с <see cref="DbContext"/>.
/// 3. Присвоить моделям <see cref="DbContext"/> атрибут <see cref="AutoRegisterDbContextAttribute"/>.
/// 4. Присвоить моделям репозиториев <see cref="AutoRegisterRepositoryAttribute"/>.
/// </summary>
/// <remarks>Регистрация моделей будет произведена автоматически, используя атрибуты <see cref="AutoRegisterDbContextAttribute"/> и <see cref="AutoRegisterRepositoryAttribute"/> (регистрация репозиториев производится как AddScoped).</remarks>
SqlDatabase = 2,
/// <summary>
/// Модуль для автоматической регистрации HealthChecks.
///
/// Для корректной работы данного модуля необходимо:
/// 1. Добавить в приложение классы HealthChecks, унаследованные от <see cref="IHealthCheck"/> и присвоить им атрибут <see cref="AutoRegisterHealthCheckAttribute"/>.
/// 2. Создать в конфиге элемент модели настроек Health Checks <see cref="HealthChecksSettings"/>.
/// </summary>
/// <remarks>1. Регистрация моделей будет произведена автоматически, используя реализацию интерфейса <see cref="IHealthCheck"/> и атрибут <see cref="AutoRegisterHealthCheckAttribute"/>.
/// 2. Если в настройках конфига включен параметр <see cref="HealthChecksSettings.UIEnable"/>, то получить доступ к UI оболочке можно по URL: /healthchecks-ui</remarks>
HealthChecks = 3,
/// <summary>
/// Модуль автоматической регистрации <see cref="AutoMapper"/>.
///
/// Для корректной работы данного модуля необходимо:
/// 1. Создать базовую модель.
/// 2. Создать модель DTO.
/// 3. Создать профиль, унаследованный от <see cref="Profile"/> для маппинга моделей.
/// 4. Присвоить модели DTO атрибут <see cref="AutoRegisterProfileAttribute"/> и передать в его параметры необходимые типы моделей.
/// </summary>
/// <remarks>Регистрация моделей будет произведена автоматически, используя атрибут <see cref="AutoRegisterProfileAttribute"/>.</remarks>
AutoMappers = 4,
/// <summary>
/// Модуль автоматической регистрации политики Cors.
///
/// Для корректной работы данного модуля необходимо:
/// 1. Создать в конфиге элемент модели настроек <see cref="CorsSettings"/>.
/// </summary>
Cors = 5,
/// <summary>
/// Модуль для работы с фоновыми задачами Hangfire.
///
/// Для корректной работы данного модуля необходимо:
/// 1. Создать фоновые задачи, реализующие интерфейс <see cref="IHangfireBackgroundTask"/>.
/// 2. Создать в конфиге элементы моделей настроек задач, которые должны быть унаследованны от <see cref="HangfireTaskSettingsBase"/> для каждой задачи.
/// 3. Создать в конфиге элемент модели настроек Hangfire <see cref="HangfireSettings"/>.
/// 4. Создать в конфиге элемент модели настроек дашборда Hangfire <see cref="HangfireDashboardSettings"/>.
/// 5. При необходимости создать фильтр авторизации для дашборда Hangfire, унаследованный от <see cref="IDashboardAuthorizationFilter"/> или же использовать существующие фильтры (<see cref="AllAuthorizationFilter"/>).
/// 6. Присвоить задачам атрибут <see cref="AutoRegisterHangfireTaskAttribute"/> и передать в его параметры необходимый типы модели настроек.
/// </summary>
/// <remarks>Регистрация моделей будет произведена автоматически, используя атрибут <see cref="AutoRegisterHangfireTaskAttribute"/>.</remarks>
Hangfire = 6,
/// <summary>
/// Модуль Swagger.
/// </summary>
Swagger = 7,
/// <summary>
/// Модуль Serilog.
///
/// Настроить модуль можно в конфиге appsettings.json. Базовый конфиг для настроек Serilog можно проинициализировать, используя метод .InitBaseConfig() у WebApplicationFacade.
/// Обратиться к логгеру можно как используя интерфейс <see cref="ILogger{TCategoryName}"/>, так и используя статический класс <see cref="Serilog.Log"/>.
/// </summary>
Serilog = 8,
/// <summary>
/// Модуль переменных окружения.
/// </summary>
EnvironmentVariables = 9,
/// <summary>
/// Модуль конфигурации ApiExplorer (служба Minimal APIs).
/// </summary>
EndpointsApiExplorer = 10,
/// <summary>
/// Модуль инициализации мигратора <see cref="Migrator"/> (применяет созданные миграции к БД) и проведения миграций при старте приложения с использованием <see cref="MigrationHostedService"/>.
///
/// Для корректной работы мигратора необходимо:
/// 1. Создать модели <see cref="DbContext"/>.
/// 2. Присвоить моделям <see cref="DbContext"/> атрибут <see cref="AutoRegisterDbContextAttribute"/>.
/// 3. Создать миграции, используя команду <code>dotnet ef migrations add InitialCreate --project your-project/your-project.csproj --startup-project your-project/your-project.csproj --output-dir Migrations</code>.
/// </summary>
EFMigrations = 11,
/// <summary>
/// Модуль контроллеров.
/// </summary>
Controllers = 12,
/// <summary>
/// Модуль базы данных MongoDb.
///
/// Для корректной работы модуля необходимо:
/// 1. Создать модели репозиториев, унаследованных от MongoRepositoryBase.cs
/// 2. Создать модели настроек для каждого репозитория. Модели необходимо унаследовать от <see cref="MongoSettingsBase"/> и присвоить им атрибут <see cref="AutoRegisterConfigSettingsAttribute"/>.
/// 3. Создать автоматически или заполнить вручную модели настроек MongoDb в файле appsettings.json.
/// </summary>
MongoDatabase = 13,
/// <summary>
/// Модуль базы данных Redis.
///
/// 1. Создать модели репозиториев, унаследованных от RedisRepositoryBase.cs
/// 2. Создать модели настроек для каждого репозитория. Модели необходимо унаследовать от <see cref="RedisSettingsBase"/> и присвоить им атрибут <see cref="AutoRegisterConfigSettingsAttribute"/>.
/// 3. Создать автоматически или заполнить вручную модели настроек Redis в файле appsettings.json.
/// </summary>
RedisDatabase = 14
}- Новые атрибуты, которые используются для инициализации модулей:
[AutoRegisterConfigSettingsAttribute] - атрибут для автоматической регистрации настроек конфига в DI для дальнейшего получения их с использованием IOptions.
[AutoRegisterDbContextAttribute] - атрибут для автоматической регистрации контекста БД.
[AutoRegisterHangfireTaskAttribute] - атрибут для регистрации фоновых задач Hangfire.
[AutoRegisterHealthCheckAttribute] - атрибут для автоматической регистрации IHealthCheck.
[AutoRegisterProfileAttribute] - атрибут автоматической регистрации профилей Profile в маппере IMapper.
[AutoRegisterRepositoryAttribute] - атрибут для автоматической регистрации репозиториев в DI.
[AutoRegisterServiceAttribute] - атрибут для автоматической регистрации сервисов в DI.- Конкретные примеры использования модулей можно посмотреть в проекте AspNetCoreMicroserviceInitializer.Examples. Данный проект делался просто для тестов модулей, поэтому о "красоте кода" в нем я не задумывался. Как будет дописан мой MVP-проект с 3-мя микросервисами, которые используют эту библиотеку, то обновлю документацию и приложу здесь на него ссылку, т.к. там код намного чище и красивей.
Эта библиотека добавляет в Ваш проект класс WebApplicationFacade. Он расширяет возможности стандартного WebApplicationBuilder, добавляя новый функционал, такой как:
- Добавление любого модуля из
WebApplicationModules (enum, который описан в самом начале), который производит автоматическую регистрацию какого-либо элемента.
var modules = new List<WebApplicationModules>
{
WebApplicationModules.Database,
WebApplicationModules.Settings,
WebApplicationModules.Services,
WebApplicationModules.Serilog,
WebApplicationModules.HealthChecks
};
var app = new WebApplicationFacade(modules)
.CreateApplication();
app.Run();- Добавление дополнительной конфигурации к
WebApplicationBuilder, с помощью использования метода.AddAdditionalModules().
var app = new WebApplicationFacade(modules)
.AddAdditionalModules(builder =>
{
builder.Services.AddGrpc();
builder.Services.AddScoped<ICatsFactRepository, CatsFactRepository>();
})
.CreateApplication();- Добавление дополнительной конфигурации
Serilog, если используется данный модуль с помощью метода.AddAdditionalSerilogConfiguration().
var app = new WebApplicationFacade(modules)
.AddAdditionalSerilogConfiguration((builder, serviceProvider, configuration) =>
{
configuration.Filter.ByExcluding(Matching.WithProperty<string>("RequestPath", path =>
"/health".Equals(path, StringComparison.OrdinalIgnoreCase)));
})
.CreateApplication();- Инициализация базового конфига
appsettings.jsonс помощью метода.InitBaseConfig(). При использовании данного метода, в конфиг добавятся секции моделей настроек со значениями по умолчанию, если этой секции не существует. У моделей настроек должен быть атрибут<see cref="AutoRegisterConfigSettingsAttribute"/>.
По умолчанию используется путь до
appsetting.json, но если Ваша конфигурация находится в другом файле, вы можете изменить путь, передав его в параметр метода. Путь должен быть либо абсолютным, либо относительным от исполняемого файла (*{your-project-path}\bin\Debug\net8.0*).
ВАЖНО! Не используйте этот метод при разворачивании приложения внутри Docker. Предполагается, что сначала поучаются файлы, настраиваются и после уже контейнер разворачивается в
Docker, а там не требуется использование этого метода, т.к. все уже должно быть настроено.
var app = new WebApplicationFacade(modules)
.InitBaseConfig()
.CreateApplication();/// <summary>
/// Модель настроек для Cats Facts Api.
/// </summary>
[AutoRegisterConfigSettings]
public class CatsFactsApiSettings
{
/// <summary>
/// Endpoint для Health Check.
/// </summary>
public required string HealthCheckEndpoint { get; set; }
/// <summary>
/// Endpoint для получения информации.
/// </summary>
public required string Endpoint { get; set; }
/// <summary>
/// Endpoint для получения страницы.
/// </summary>
public required string PageEndpoint { get; set; }
/// <summary>
/// Индекс максимальной страницы (для генерации списка случайных фактов).
/// </summary>
public required int MaxPage { get; set; }
/// <summary>
/// Максимальный лимит страницы (для генерации списка случайных фактов).
/// </summary>
public required int MaxLimit { get; set; }
}"CatsFactsApiSettings": {
"Endpoint": "https://catfact.ninja/fact",
"HealthCheckEndpoint": "https://catfact.ninja",
"PageEndpoint": "https://catfact.ninja/facts",
"MaxPage": 20,
"MaxLimit": 10
}- Инициализация стандартных файлов
docker-composeс помощью метода.InitBaseDockerComposeFiles().
Данный метод инициализирует файлы:
develop.env- файл конфигурации дляDocker. Инициализируется с помощью конфигаappsettings.json. Метод переносит все json объекты из файлаappsettings.jsonи записывает их в виде конфигурации, воспринимаемой Docker.docker-compose.yml- файл конфигурацииdocker-compose. Добавляет шаблонные блоки для инициализации нужных сервисов вdocker-compose.
Созданные файлы, после запуска приложения сохраняются рядом с исполняемым файлом:
{your-project-path}\bin\Debug\net8.0\DockerTemplates. После запуска приложения, можно скопировать сформированные файлы в удобное место.
var dockerComposeModules = new List<DockerComposeFileModules>
{
DockerComposeFileModules.Server,
DockerComposeFileModules.PostgreSql,
DockerComposeFileModules.Adminer
};
var app = new WebApplicationFacade(modules)
.InitBaseConfig()
.InitBaseDockerComposeFiles(dockerComposeModules)
.CreateApplication();/// <summary>
/// Модули файлов docker-compose.
/// </summary>
public enum DockerComposeFileModules
{
/// <summary>
/// Модуль сервера (само приложение ASP.NET API).
/// </summary>
Server = 0,
/// <summary>
/// Модуль клиента (если для приложения будет создано frontend-приложение).
/// </summary>
Client = 1,
/// <summary>
/// Модуль Adminer для обращения к базам данных внутри docker-compose.
/// </summary>
Adminer = 2,
/// <summary>
/// Модуль базы данных MongoDB.
/// </summary>
MongoDb = 3,
/// <summary>
/// Модуль MongoExpress для обращения к MongoDb внутри docker-compose.
/// </summary>
MongoExpress = 4,
/// <summary>
/// Модуль базы данных ClickHouse.
/// </summary>
ClickHouse = 5,
/// <summary>
/// Модуль базы данных MySql.
/// </summary>
MySql = 6,
/// <summary>
/// Модуль базы данных Redis.
/// </summary>
Redis = 7,
/// <summary>
/// Модуль базы данных Elasticsearch.
/// </summary>
Elasticsearch = 8,
/// <summary>
/// Модуль Kibana для обращения к Elasticsearch внутри docker-compose.
/// </summary>
Kibana = 9,
/// <summary>
/// Модуль базы данных Cassandra.
/// </summary>
Cassandra = 10,
/// <summary>
/// Модуль брокера распределенных сообщений RabbitMq.
/// </summary>
RabbitMq = 11,
/// <summary>
/// Модуль системы мониторинга Prometheus.
/// </summary>
Prometheus = 12,
/// <summary>
/// Модуль системы мониторинга Grafana.
/// </summary>
Grafana = 13,
/// <summary>
/// Модуль веб-сервера Nginx.
/// </summary>
Nginx = 14,
/// <summary>
/// Модуль базы данных PostgreSql.
/// </summary>
PostgreSql = 15
}# Путь до .env файла относительно docker-compose (используется для прописывания .env файлов в сервисы docker-compose).
ENV_FILE=develop.env
# Временная зона приложений.
TIME_ZONE=Europe/Moscow
FactsMicroserviceDbContextSettings__ConnectionString=Host=localhost:5432; Database=microservice; Username=postgres; Password=postgres
FactsMicroserviceDbContextSettings__Schema=FactsMicroservice
FactsMicroserviceDbContextSettings__MigrationsTableName=__EFMigrationsHistory
FactsMicroserviceDbContextSettings__MigrationsSchema=FactsMicroservice
CatsFactsApiSettings__Endpoint=https://catfact.ninja/fact
CatsFactsApiSettings__HealthCheckEndpoint=https://catfact.ninja
CatsFactsApiSettings__PageEndpoint=https://catfact.ninja/facts
CatsFactsApiSettings__MaxPage=20
CatsFactsApiSettings__MaxLimit=10
HealthChecksSettings__Endpoint=/health
HealthChecksSettings__UIEnable=True
HealthChecksSettings__EndpointFullUrl=https://localhost:7071/health
HealthChecksSettings__UIEvaluationTimeInSeconds=15
HealthChecksSettings__UIApiMaxActiveRequests=1
Serilog__Using__0=Serilog.Sinks.Console
Serilog__Using__1=Serilog.Sinks.PostgreSQL.Alternative
Serilog__MinimumLevel__Default=Information
Serilog__MinimumLevel__Override__Microsoft=Warning
Serilog__MinimumLevel__Override__System=Warning
Serilog__MinimumLevel__Override__HealthChecks=Warning
Serilog__MinimumLevel__Override__AspNetCore.HealthChecks.UI=Warning
Serilog__MinimumLevel__Override__AspNetCore.HealthChecks.UI.Client=Warning
Serilog__MinimumLevel__Override__AspNetCore.HealthChecks.UI.InMemory.Storage=Warning
Serilog__WriteTo__0__Name=Console
Serilog__WriteTo__0__OutputTemplate=[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}
Serilog__WriteTo__0__Args=null
Serilog__WriteTo__1__Name=PostgreSQL
Serilog__WriteTo__1__OutputTemplate=null
Serilog__WriteTo__1__Args__connectionString=Host=localhost:5432; Database=microservice; Username=postgres; Password=postgres
Serilog__WriteTo__1__Args__schemaName=FactsMicroservice
Serilog__WriteTo__1__Args__tableName=ServerLogs
Serilog__WriteTo__1__Args__needAutoCreateTable=True
Serilog__Properties__ApplicationName=FactsMicroserviceversion: "3.9"
networks:
app-network:
services:
server:
build:
# Можно либо обратиться к конкретному image, либо к Dockerfile.
#image:
#context:
#dockerfile:
container_name: server
environment:
TZ: ${TIME_ZONE}
ports:
- "8000:8000"
env_file: ${ENV_FILE}
networks:
- app-network
adminer:
image: adminer:latest
container_name: adminer
ports:
- "8002:8002"
environment:
TZ: ${TIME_ZONE}
networks:
- app-network
postgres:
image: postgres:latest
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_DB: postgres
TZ: ${TIME_ZONE}
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 5s
timeout: 5s
retries: 5
env_file: ${ENV_FILE}
networks:
- app-network- enum WebApplicationModules
- class WebApplicationFacade
- enum DockerComposeFileModules
public class Program
{
public static void Main(string[] args)
{
var modules = new List<WebApplicationModules>
{
WebApplicationModules.Database,
WebApplicationModules.Settings,
WebApplicationModules.Services,
WebApplicationModules.Serilog,
WebApplicationModules.HealthChecks
};
var dockerComposeModules = new List<DockerComposeFileModules>
{
DockerComposeFileModules.Server,
DockerComposeFileModules.PostgreSql,
DockerComposeFileModules.Adminer
};
var app = new WebApplicationFacade(modules)
.InitBaseConfig()
.InitBaseDockerComposeFiles(dockerComposeModules)
.AddAdditionalModules(builder =>
{
builder.Services.AddGrpc();
builder.Services.AddScoped<ICatsFactRepository, CatsFactRepository>();
})
.CreateApplication();
app.MapGrpcServices();
app.Run();
}
}/// <summary>
/// Модель настроек контекста БД.
/// </summary>
[AutoRegisterConfigSettings]
public class FactsMicroserviceDbContextSettings : DbContextSettings
{
}/// <summary>
/// Health Check для <see cref="ICatsFactsApiClient"/>.
/// </summary>
[AutoRegisterHealthCheck]
public class CatsFactsApiHealthCheck : IHealthCheck
{
/// <summary>
/// Клиент.
/// </summary>
private readonly ICatsFactsApiClient _catsFactsApiClient;
public CatsFactsApiHealthCheck(
ICatsFactsApiClient catsFactsApiClient)
{
_catsFactsApiClient = catsFactsApiClient;
}
/// <summary>
/// Асинхронный метод проверки.
/// </summary>
/// <param name="context">Контекст проверки работоспособности.</param>
/// <param name="cancellationToken">Токен отмены.</param>
/// <returns>Результат проверки работоспособности.</returns>
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
if (await _catsFactsApiClient.Ping())
{
return HealthCheckResult.Healthy();
}
else
{
return HealthCheckResult.Unhealthy();
}
}
}/// <summary>
/// Сервис для взаимодействия с Cats Facts Api.
/// </summary>
[AutoRegisterService(ServiceLifetime.Transient, typeof(ICatsFactsApiClient))]
public class CatsFactsApiClient : ICatsFactsApiClient
{
// Implementation
}- Метод
.InitBaseConfig()инициализирует Json Configuration в момент запуска приложения. Так что, если вы хотите, чтобы Ваша конфигурация была автоматически проинициализирована, нужно: 1) Запустить приложение. 2) Выключить его. 3) Заполнить конфигурацию, после инициализации. 4) Запустить приложение снова. - Метод
.InitBaseDockerComposeFiles()создает файлы при каждом запуске приложения, так что после первого использования стоит вынести эти файлы из{your-project-path}\bin\Debug\net8.0\DockerTemplatesв то место, где они не могут быть изменены автоматически. - Некоторые модули имеют зависимости друг от друга и если, к примеру, вы добавите
WebApplicationModule.EFMigrationsи не добавите модульWebApplicationModule.SqlDatabaseиWebApplicationModule.Settings, они будут добавлены автоматически (внутренняя логика библиотеки, для того, чтобы избежать возможные ошибки). - Модуль
WebApplicationModules.Swaggerработает только при сборке в Debug. - Модули сортируются в
WebApplicationFacade, поэтому порядок их добавления не важен. - Логика добавления модулей в
IServiceCollectionскрыта за рамкамиWebApplicationFacadeи ее нельзя изменить снаружи.
Эта библиотека содержит инвентарь для AspNetCoreMicroserviceInitializer.Registrations, а именно:
- Атрибуты, описанные в начале документации.
- Enums, описанные в начале документации и в разделе "Что делает эта библиотека" у AspNetCoreMicroserviceInitializer.Registrations.
-
Exceptions -
AttributeException, которое выбрасывается в случае ошибки в каком-либо атрибуте. -
HangfireFilters -
AllAuthorizationFilter(фильтр для автоматической авторизации всех запросов, при запросе дашбордаHangfire. Заполняется в конфигеappsettings.json).
"HangfireDashboardSettings": {
"EnableCustomAuthorization": true,
"FilterName": "AllAuthorizationFilter"
}/// <summary>
/// Фильтр для автоматической авторизации всех запросов, при запросе дашборда <see cref="Hangfire"/>.
/// </summary>
public class AllAuthorizationFilter : IDashboardAuthorizationFilter
{
/// <summary>
/// Метод авторизации.
/// </summary>
/// <param name="context">Контекст дашборда.</param>
/// <returns><see langword="true"/> для всех случаев.</returns>
public bool Authorize([NotNull] DashboardContext context) => true;
}-
Helpers - различные помощники для работы с модулями. Среди них:
DockerComposeFilesHelper,AssemblyHelper,JsonHelper. -
Interfaces -
IHangfireBackgroundTask,IMigrator(используется только для внутренней реализации мигратора),IServiceImplementationFactory{TService}- интерфейс добавления фабричной функции для создания экземпляра сервиса внутри Dependency Injection контейнера.
[AutoRegisterHangfireTask(typeof(DateTimeTaskSettings))]
public class DateTimeTask : IHangfireBackgroundTask
{
private readonly DateTimeService _dateTimeService;
private readonly ILogger<DateTimeTask> _logger;
public DateTimeTask(
DateTimeService dateTimeService,
ILogger<DateTimeTask> logger)
{
_dateTimeService = dateTimeService;
_logger = logger;
}
public async Task ExecuteAsync()
{
var currentDateTime = await _dateTimeService.GetDateTimeWithMessageAsync();
_logger.LogInformation(currentDateTime);
}
}[AutoRegisterService(ServiceLifetime.Singleton, typeof(IRandomWordService))]
public class RandomWordService : IRandomWordService, IServiceImplementationFactory<RandomWordService>
{
...
/// <summary>
/// A factory function,which is added using <see cref="IServiceImplementationFactory{TService}"/> that will be used when registering the service and return <see cref="RandowShortWordService"/>.
/// </summary>
/// <remarks>
/// For a test, you can comment out this line and remove the interface from the class. And you will be able to observe the generation of random words of more than 5 characters.
/// </remarks>
public Func<IServiceProvider, RandomWordService> ImplementationFactory => (sp) => new RandowShortWordService();
...
}public class RandowShortWordService : RandomWordService
{
protected override int MaxWordLength => 5;
public override string GetRandomWord()
{
return base.GetRandomWord();
}
}- Migrations - классы и HostedService для модуля миграций.
-
Settings - абстрактные классы настроек (
DbContextSettingsBase,DbSettingsBase,HangfireTaskSettingsBase,MongoSettingsBase,RedisSettingsBase) для наследования и удобного использования и определения настроек, а также модели настроек для модулей (содержат четкие настройки какого-либо модуля). Абстрактные настройки можно расширить по усмотрению, конкретные настройки нужно будет только заполнить в файлеappsettings.json.
/// <summary>
/// Модель настроек Health Checks из конфига appsettings.
/// </summary>
[AutoRegisterConfigSettings]
[ConfigSettingsModule(WebApplicationModules.HealthChecks)]
public class HealthChecksSettings
{
/// <summary>
/// Endpoint Health Checks.
///
/// Пример: /health.
/// </summary>
public required string Endpoint { get; init; } = "/health";
/// <summary>
/// Включить UI для отслеживание Health Checks.
///
/// Пример: true/false.
/// </summary>
public bool UIEnable { get; init; } = true;
/// <summary>
/// Полный Url Health Check endpoint'а для запросов из Health Checks UI.
///
/// Пример: http://mydomain:80/health.
/// </summary>
public string? EndpointFullUrl { get; init; } = "http://localhost:8000/health";
/// <summary>
/// Частота опроса в секундах Health Checks для UI.
///
/// Пример: 10.
/// </summary>
public int? UIEvaluationTimeInSeconds { get; init; } = 5;
/// <summary>
/// Максимальное кол-во активных (одновременных) запросов на UI Health Checks Api.
///
/// Пример: 2.
/// </summary>
public int? UIApiMaxActiveRequests { get; init; } = 2;
}/// <summary>
/// Настройки мигратора.
/// </summary>
[AutoRegisterConfigSettings]
[ConfigSettingsModule(WebApplicationModules.EFMigrations)]
public class MigratorSettings
{
/// <summary>
/// Флаг, отвечающий за то, нужно ли выключить приложение после выполнения миграций.
/// </summary>
public bool IsStopApplicationAfterApplyMigrations { get; init; } = false;
}/// <summary>
/// Настройки Cors.
/// </summary>
[AutoRegisterConfigSettings]
[ConfigSettingsModule(WebApplicationModules.Cors)]
public class CorsSettings
{
/// <summary>
/// Флаг включения Cors с именованной политикой.
///
/// Пример: true/false.
/// </summary>
public bool EnableCors { get; init; } = true;
/// <summary>
/// Наименование политики Cors.
///
/// Пример: AllowAccessFrontendQueries.
/// </summary>
public string? PolicyName { get; init; } = "AllowAccessFrontendQueries";
/// <summary>
/// Список доменов для политики Cors.
///
/// Пример: http://localhost:8082.
/// </summary>
public string[ ]? AllowedOrigins { get; init; }
}Данная библиотека не имеет функциональных особенностей, а просто содержит инвентарь для работы основной библиотеки: AspNetCoreMicroserviceInitializer.Registrations.
Перечислены в разделе "Что делает эта библиотека?"
Перечислены в разделе "Что делает эта библиотека?"
- Абстрактные настройки можно расширить по усмотрению, конкретные настройки нужно будет только заполнить в файле
appsettings.json. - Атрибуты необходимо использовать в соответствии с добавленными модулями. Одно без другого работать не будет.
- AspNetCore.HealthChecks.UI (>= 8.0.2)
- AspNetCore.HealthChecks.UI.Client (>= 8.0.1)
- AspNetCore.HealthChecks.UI.InMemory.Storage (>= 8.0.1)
- AutoMapper (>= 13.0.1)
- Hangfire.AspNetCore (>= 1.8.15)
- Hangfire.Core (>= 1.8.15)
- Hangfire.MemoryStorage (>= 1.8.1.1)
- Hangfire.PostgreSql (>= 1.20.10)
- Microsoft.Extensions.Configuration (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection (>= 9.0.0)
- Microsoft.Extensions.Hosting (>= 9.0.0)
- Microsoft.Extensions.Logging (>= 9.0.0)
- Microsoft.Extensions.Options (>= 9.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.0)
- Newtonsoft.Json (>= 13.0.3)
- Serilog.AspNetCore (>= 8.0.3)
- Serilog.Sinks.SQLite (>= 6.0.0)
- Swashbuckle.AspNetCore (>= 7.0.0)
Эта библиотека содержит инвентарь для AspNetCoreMicroserviceInitializer.Registrations, который нужен для взаимодействия с базами данных. В ней описаны базовые репозитории, от которых можно наследоваться и использовать методы базового репозитория в репозиториях своих проектов.
- Содержит интерфейсы сущностей.
public interface IEntity
{
/// <summary>
/// Дата создания сущности.
/// </summary>
DateTime CreationDate { get; set; }
/// <summary>
/// Дата последнего обновления сущности.
/// </summary>
DateTime LastUpdateDate { get; set; }
/// <summary>
/// Флаг показывающий, удалена ли сущность.
/// </summary>
bool IsDeleted { get; set; }
}/// <summary>
/// Интерфейс базовой Sql сущности.
/// </summary>
/// <typeparam name="TId">Тип Id.</typeparam>
public interface ISqlEntity<TId> : IEntity
where TId : struct
{
/// <summary>
/// Идентификатор.
/// </summary>
TId Id { get; set; }
}/// <summary>
/// Интерфейс базовой сущности Redis.
/// </summary>
public interface IRedisEntity
{
/// <summary>
/// Ключ.
/// </summary>
string Key { get; set; }
/// <summary>
/// Значение в формате строки или Json.
/// </summary>
string Value { get; set; }
}/// <summary>
/// Интерфейс базовой MongoDb сущности.
/// </summary>
public interface IMongoEntity : IEntity
{
/// <summary>
/// Идентификатор.
/// </summary>
[BsonId]
ObjectId Id { get; set; }
}- Содержит интерфейсы репозиториев.
/// <summary>
/// Интерфейс репозитория для чтения данных из Redis.
/// </summary>
/// <typeparam name="TEntity">Тип сущности.</typeparam>
public interface IRedisReadRepository<TEntity>
where TEntity : IRedisEntity
{
/// <summary>
/// Получить записи по ключам.
/// </summary>
/// <param name="keys">Массив ключей типа <see cref="IEnumerable{string}"/>.</param>
/// <returns>Коллекцию типа <see cref="IEnumerable{TEntity}"/>.</returns>
Task<IEnumerable<TEntity>> GetByKeysAsync(
IEnumerable<string> keys,
CancellationToken cancellationToken = default);
/// <summary>
/// Получить запись по ключу.
/// </summary>
/// <param name="key">Ключ.</param>
/// <returns>Модель типа <see cref="TEntity"/>, <see langword="null"/> если не найдена.</returns>
Task<TEntity?> GetByKeyAsync(
string key,
CancellationToken cancellationToken = default);
/// <summary>
/// Проверить существование элемента.
/// </summary>
/// <param name="key">Ключ.</param>
/// <returns><see langword="true"/> если элемент существует, <see langword="false"/> если элемент не существует.</returns>
Task<bool> IsExistsAsync(
string key,
CancellationToken cancellationToken = default);
}/// <summary>
/// Интерфейс репозитория записи данных в MongoDb.
/// </summary>
/// <typeparam name="TEntity">Тип сущности.</typeparam>
public interface IMongoWriteRepository<TEntity>
where TEntity : IMongoEntity
{
/// <summary>
/// Метод вставки записи в таблицу.
/// </summary>
/// <param name="entity">Сущность типа <see cref="TEntity"/>.</param>
Task InsertAsync(
TEntity entity,
CancellationToken cancellationToken = default);
/// <summary>
/// Метод вставки нескольких записей в таблицу.
/// </summary>
/// <param name="entities">Массив сущностей типа <see cref="IEnumerable{TEntity}"/>.</param>
Task InsertBatchAsync(
IEnumerable<TEntity> entities,
CancellationToken cancellationToken = default);
/// <summary>
/// Обновить запись в таблице.
/// </summary>
/// <param name="entity">Сущность типа <see cref="TEntity"/>.</param>
Task UpdateAsync(
TEntity entity,
CancellationToken cancellationToken = default);
/// <summary>
/// Метод обновления нескольких записей в таблице.
/// </summary>
/// <param name="entities">Массив значений типа <see cref="IEnumerable{TEntity}"/>.</param>
Task UpdateBatchAsync(
IEnumerable<TEntity> entities,
CancellationToken cancellationToken = default);
/// <summary>
/// Метод удаления записи по идентификатору.
/// </summary>
/// <param name="id">Идентификатор.</param>
Task DeleteByIdAsync(
ObjectId id,
CancellationToken cancellationToken = default);
/// <summary>
/// Метод удаления записей по их идентификаторам.
/// </summary>
/// <param name="ids">Массив идентификаторов типа <see cref="IEnumerable{ObjectId}"/>.</param>
/// <returns>Количество успешно удаленных записей.</returns>
Task DeleteByIdsAsync(
IEnumerable<ObjectId> ids,
CancellationToken cancellationToken = default);
}/// <summary>
/// Базовый интерфейс репозитория MongoDb.
/// </summary>
public interface IMongoRepository<TEntity> :
IMongoReadRepository<TEntity>,
IMongoWriteRepository<TEntity>
where TEntity : IMongoEntity
{
}/// <summary>
/// Интерфейс базового репозитория Redis.
/// </summary>
public interface IRedisRepository<TEntity> :
IRedisReadRepository<TEntity>,
IRedisWriteRepository<TEntity>
where TEntity : IRedisEntity
{
}/// <summary>
/// Интерфейс базового репозитория.
/// </summary>
/// <typeparam name="TEntity">Тип сущности.</typeparam>
public interface ISqlRepository<TEntity> :
ISqlReadRepository<TEntity>,
ISqlWriteRepository<TEntity>
where TEntity : ISqlEntity<long>
{
}- Содержит базовые абстрактные классы репозиториев.
/// <summary>
/// Абстрактный класс базового MongoDb репозитория.
/// </summary>
public abstract class MongoRepositoryBase<TEntity> : IMongoRepository<TEntity>
where TEntity : class, IMongoEntity
{
/// <summary>
/// Клиент для рабооты с MongoDb.
/// </summary>
protected readonly IMongoClient _client;
/// <summary>
/// База данных для рабооты с MongoDb.
/// </summary>
protected readonly IMongoDatabase _database;
/// <summary>
/// Коллекция MongoDb.
/// </summary>
protected readonly IMongoCollection<TEntity> _collection;
/// <summary>
/// Конструктор <see cref="MongoRepositoryBase{TEntity}"/>.
/// </summary>
/// <param name="clientFactory">Фабрика для получения клиента MongoDb.</param>
/// <param name="connectionString">Строка подключения к базе данных.</param>
/// <param name="databaseName">Имя базы данных.</param>
protected MongoRepositoryBase(
IMongoClientFactory clientFactory,
string connectionString,
string databaseName)
{
_client = clientFactory.GetClientByConnectionString(connectionString);
_database = _client.GetDatabase(databaseName);
var collectionName = MongoCollectionNameResolver.GetCollectionName<TEntity>();
_collection = _database.GetCollection<TEntity>(collectionName);
}
// Implementation
}/// <summary>
/// Абстрактный класс базового Redis репозитория.
/// </summary>
public abstract class RedisRepositoryBase<TEntity> : IRedisRepository<TEntity>
where TEntity : class, IRedisEntity, new()
{
/// <summary>
/// База данных.
/// </summary>
protected readonly IDatabase _database;
/// <summary>
/// Префикс ключа для унификации.
/// </summary>
protected readonly string _keyPrefix;
/// <summary>
/// Модель для подключения к БД Redis.
/// </summary>
protected readonly IConnectionMultiplexer _multiplexer;
/// <summary>
/// Конструктор репозитория.
/// </summary>
/// <param name="clientFactory">Фабрика для получения клиента Redis.</param>
/// <param name="connectionString">Строка подключения.</param>
/// <param name="keyPrefix">Префикс ключа для унификации.</param>
protected RedisRepositoryBase(
IRedisClientFactory clientFactory,
string connectionString,
string keyPrefix)
{
_multiplexer = clientFactory.GetClientByConnectionString(connectionString);
_database = _multiplexer.GetDatabase();
_keyPrefix = keyPrefix;
}
//Implementation
}/// <summary>
/// Абстрактный класс базового Sql репозитория.
/// </summary>
public abstract class SqlRepositoryBase<TEntity> : ISqlRepository<TEntity>
where TEntity : class, ISqlEntity<long>
{
/// <summary>
/// Контекст БД.
/// </summary>
protected readonly DbContext _dbContext;
/// <summary>
/// Конструктор <see cref="SqlRepositoryBase{TEntity}"/>.
/// </summary>
/// <param name="dbContext">Контекст БД.</param>
protected SqlRepositoryBase(DbContext dbContext)
{
_dbContext = dbContext;
}
// Implementation
}- Содержит атрибуты для сущностей.
/// <summary>
/// Атрибут для указания имени коллекции в строчном формате.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class MongoCollectionAttribute : Attribute
{
/// <summary>
/// Имя коллекции.
/// </summary>
public string CollectionName { get; }
/// <summary>
/// Конструктор атрибута.
/// </summary>
/// <param name="collectionName">Имя коллекции.</param>
public MongoCollectionAttribute(string collectionName)
{
CollectionName = collectionName;
}
}- Содержит сервисы для внутреннего использования, но они с модификатором public:
MongoClientWithConnectionString,RedisClientWithConnectionString,MongoClientFactory,RedisClientFactory.
- Entities interfaces.
- Repositories interfaces.
- Abstract repositories classes.
- Attributes.
/// <summary>
/// Внутренняя модель факта для сохранения в БД.
/// </summary>
public class CatsFact : IEntity<long>
{
/// <summary>
/// Идентификатор модели.
/// </summary>
public long Id { get; set; }
/// <summary>
/// Текст факта.
/// </summary>
public required string Text { get; set; }
/// <summary>
/// Дата создания модели.
/// </summary>
public DateTime CreationDate { get; set ; }
/// <summary>
/// Флаг, удалена ли модель.
/// </summary>
public bool IsDeleted { get; set; }
}/// <summary>
/// Репозиторий для работы с <see cref="CatsFact"/>.
/// </summary>
[AutoRegisterRepository]
public class CatsFactRepository :
RepositoryBase<CatsFact>,
ICatsFactRepository
{
/// <summary>
/// Контекст БД.
/// </summary>
private readonly FactsMicroserviceDbContext _microserviceDbContext;
public CatsFactRepository(FactsMicroserviceDbContext dbContext) : base(dbContext)
{
_microserviceDbContext = dbContext;
}
/// <summary>
/// Метод удаления записи по идентификатору.
/// </summary>
/// <param name="id">Идентификатор записи.</param>
public override async Task DeleteByIdAsync(long id)
{
var model = await GetByIdAsync(id);
if (model is null)
{
throw new Exception("Couldn't get the model by ID to delete.");
}
model.IsDeleted = true;
await UpdateAsync(model);
}
/// <summary>
/// Метод получения количества записей в таблице.
/// </summary>
/// <returns>Количество записей в таблице.</returns>
public async Task<int> GetCountAsync()
{
return await _microserviceDbContext.Facts.CountAsync();
}
}[MongoCollection("TestCollection")]
public class DummyMongoDbEntity : IMongoEntity
{
[BsonId]
public ObjectId Id { get; set; }
public DateTime CreationDate { get; set; }
public DateTime LastUpdateDate { get; set; }
public bool IsDeleted { get; set; }
}[AutoRegisterRepository(interfaceType: typeof(IMongoRepository<DummyMongoDbEntity>))]
public class DummyFirstMongoDbRepository : MongoRepositoryBase<DummyMongoDbEntity>
{
public DummyFirstMongoDbRepository(IMongoClientFactory factory, IOptions<DummyFirstMongoSettings> settings)
: base(factory, settings.Value.ConnectionString, settings.Value.DatabaseName)
{
}
}[AutoRegisterRepository]
public class DummyRedisSecondRepository : RedisRepositoryBase<DummyRedisEntity>
{
public DummyRedisSecondRepository(IRedisClientFactory factory, IOptions<DummyRedisSecondSettings> settings)
: base(factory, settings.Value.ConnectionString, "test_repository_redis_second")
{
}
}[AutoRegisterRepository(interfaceType: typeof(ISqlRepository<DummyModel>))]
public class DummyRepository : SqlRepositoryBase<DummyModel>
{
public DummyRepository(DummyDbContext dbContext) : base(dbContext)
{
}
}public class DatabaseController : ControllerBase
{
private readonly ISqlRepository<DummyModel> _dummyRepository;
// Implementation.
}- Если при инициализации WebApplicationFacade Вы используете
WebApplicationModules.MongoDatabaseилиWebApplicationModules.RedisDatabase, то сервисыIRedisClientFactoryиIMongoClientFactoryавтоматически зарегистрируются в контейнере DI и Вам не нужно будет их как-то дополнительно регистрировать, достаточно будет просто передать в конструктор репозитория. Если же вы не используетеWebApplicationFacade, то Вам самостоятельно нужно будет зарегистрировать эти сервисы в DI.
services.AddSingleton<IMongoClientWithConnectionString>(sp =>
{
return new MongoClientWithConnectionString(mongoSettings.ConnectionString);
});
services.AddTransient<IMongoClientFactory, MongoClientFactory>();services.AddSingleton<IRedisClientWithConnectionString>(sp =>
{
return new RedisClientWithConnectionString(redisSettings.ConnectionString);
});
services.AddTransient<IRedisClientFactory, RedisClientFactory>();- Для автоматической регистрации
IMongoClientFactoryиIRedisClientFactoryнеобходимо следовать инструкциям у WebApplicationModules на соответствующих модулях.
/// <summary>
/// Модуль базы данных MongoDb.
///
/// Для корректной работы модуля необходимо:
/// 1. Создать модели репозиториев, унаследованных от MongoRepositoryBase.cs
/// 2. Создать модели настроек для каждого репозитория. Модели необходимо унаследовать от <see cref="MongoSettingsBase"/> и присвоить им атрибут <see cref="AutoRegisterConfigSettingsAttribute"/>.
/// 3. Создать автоматически или заполнить вручную модели настроек MongoDb в файле appsettings.json.
/// </summary>
MongoDatabase = 13,
/// <summary>
/// Модуль базы данных Redis.
///
/// 1. Создать модели репозиториев, унаследованных от RedisRepositoryBase.cs
/// 2. Создать модели настроек для каждого репозитория. Модели необходимо унаследовать от <see cref="RedisSettingsBase"/> и присвоить им атрибут <see cref="AutoRegisterConfigSettingsAttribute"/>.
/// 3. Создать автоматически или заполнить вручную модели настроек Redis в файле appsettings.json.
/// </summary>
RedisDatabase = 14- Фабричный метод у репозиториев используется для тех случаев, когда в рамках одного микросервиса нужно подключаться к разным базам одного типа. К примеру к 2-ум MongoDB, которые расположены по разным Url'ам. В таком случае нужно создать соответствующее количество настроек,
- Унаследовать их от
RedisSettingsBaseилиMongoSettingsBase - Следовать инструкциям из прошлого пункта.
- Унаследовать их от
[AutoRegisterConfigSettings]
public class DummyFirstMongoSettings : MongoSettingsBase
{
}
[AutoRegisterConfigSettings]
public class DummySecondMongoSettings : MongoSettingsBase
{
}var modulens = new List<WebApplicationModules>
{
WebApplicationModules.MongoDatabase
};
var app = new WebApplicationFacade(modulens)
.CreateApplication();
app.Run();"DummyFirstMongoSettings": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "TestDatabaseFirst"
},
"DummySecondMongoSettings": {
"ConnectionString": "mongodb://localhost:27018",
"DatabaseName": "TestDabaseSecond"
}- Коллекция в MongoDb будет называться также, как и класс сущности
IMongoEntity, если на ней не используется атрибутMongoCollectionAttribute.
- Microsoft.EntityFrameworkCore (>= 9.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 9.0.0)
- MongoDB.Driver (>= 3.0.0)
- StackExchange.Redis (>= 2.8.22)
Repository with the basic Onion-Architecture-Microservice template:
All NuGet packages:
- AspNetCoreMicroserviceInitializer.Registrations
- AspNetCoreMicroserviceInitializer.TradingDesk
- AspNetCoreMicroserviceInitializer.Database
My contacts (if you have any questions):