diff --git a/Directory.Packages.props b/Directory.Packages.props
index 0830b7f..951fe88 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -18,7 +18,9 @@
+
+
diff --git a/Nocr.Users.sln b/Nocr.Users.sln
index 1785430..87886bc 100644
--- a/Nocr.Users.sln
+++ b/Nocr.Users.sln
@@ -20,6 +20,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Contracts", "Contracts", "{
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nocr.Users.Persistence", "src\Nocr.Users.Persistence\Nocr.Users.Persistence.csproj", "{51C01BA8-E3E1-45F4-9DDF-6E08DAF5BB46}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nocr.Users.Migrator", "src\Nocr.Users.Migrator\Nocr.Users.Migrator.csproj", "{BA022CCE-FCAA-41E6-993D-B0794BBD0ECA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -46,6 +48,10 @@ Global
{51C01BA8-E3E1-45F4-9DDF-6E08DAF5BB46}.Debug|Any CPU.Build.0 = Debug|Any CPU
{51C01BA8-E3E1-45F4-9DDF-6E08DAF5BB46}.Release|Any CPU.ActiveCfg = Release|Any CPU
{51C01BA8-E3E1-45F4-9DDF-6E08DAF5BB46}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BA022CCE-FCAA-41E6-993D-B0794BBD0ECA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BA022CCE-FCAA-41E6-993D-B0794BBD0ECA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BA022CCE-FCAA-41E6-993D-B0794BBD0ECA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BA022CCE-FCAA-41E6-993D-B0794BBD0ECA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{4DDFB05F-DFC7-4BD6-B593-AD52CF1B002E} = {CA3AA87B-9217-4A2D-9EBA-BFC415B63818}
diff --git a/src/Nocr.Users.Migrator/AddMigration.sh b/src/Nocr.Users.Migrator/AddMigration.sh
new file mode 100755
index 0000000..bcd67f2
--- /dev/null
+++ b/src/Nocr.Users.Migrator/AddMigration.sh
@@ -0,0 +1,2 @@
+export DOTNET_ENVIRONMENT=Development
+dotnet ef migrations add $1
diff --git a/src/Nocr.Users.Migrator/DesignTimeTextMatcherContextFactory.cs b/src/Nocr.Users.Migrator/DesignTimeTextMatcherContextFactory.cs
new file mode 100644
index 0000000..b6500ab
--- /dev/null
+++ b/src/Nocr.Users.Migrator/DesignTimeTextMatcherContextFactory.cs
@@ -0,0 +1,37 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Design;
+using Microsoft.Extensions.Configuration;
+using Nocr.Users.Persistence;
+
+namespace Nocr.Users.Migrator;
+
+public class DesignTimeTextMatcherContextFactory : IDesignTimeDbContextFactory
+{
+ private const string EnvironmentVariableKey = "DOTNET_ENVIRONMENT";
+
+ public UsersContext CreateDbContext(string[] args)
+ {
+ var optionsBuilder = new DbContextOptionsBuilder();
+
+ var configuration = new ConfigurationBuilder()
+ .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
+ .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
+ .AddEnvironmentVariables()
+ .Build();
+
+ var environment = configuration.GetValue(EnvironmentVariableKey);
+ if (string.IsNullOrWhiteSpace(environment))
+ {
+ throw new InvalidOperationException($"Отсутствует переменная окружения {EnvironmentVariableKey}");
+ }
+
+ var connectionString = configuration.GetConnectionString(environment);
+ if (string.IsNullOrWhiteSpace(connectionString))
+ throw new InvalidOperationException($"ConnectionString for environment `{environment}` not found");
+
+ optionsBuilder.UseMySql(connectionString, new MariaDbServerVersion(MariaDbServerVersion.LatestSupportedServerVersion),
+ builder => builder.MigrationsAssembly("Nocr.Users.Migrator"));
+
+ return new UsersContext(optionsBuilder.Options);
+ }
+}
\ No newline at end of file
diff --git a/src/Nocr.Users.Migrator/Dockerfile b/src/Nocr.Users.Migrator/Dockerfile
new file mode 100644
index 0000000..054cf8f
--- /dev/null
+++ b/src/Nocr.Users.Migrator/Dockerfile
@@ -0,0 +1,19 @@
+FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base
+WORKDIR /app
+
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+WORKDIR /src
+COPY . .
+
+RUN dotnet nuget add source -n "musk.fun.nocr" "https://gitea.musk.fun/api/packages/nocr/nuget/index.json"
+RUN dotnet restore "src/Nocr.Users.Migrator/Nocr.Users.Migrator.csproj"
+WORKDIR "/src/src/Nocr.Users.Migrator"
+RUN dotnet build "Nocr.Users.Migrator.csproj" -c Release -o /app/build
+
+FROM build AS publish
+RUN dotnet publish "Nocr.Users.Migrator.csproj" -c Release -o /app/publish
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "Nocr.Users.Migrator.dll"]
diff --git a/src/Nocr.Users.Persistence/Migrations/20240330070727_InitialMigration.Designer.cs b/src/Nocr.Users.Migrator/Migrations/20240330083951_InitialMigration.Designer.cs
similarity index 97%
rename from src/Nocr.Users.Persistence/Migrations/20240330070727_InitialMigration.Designer.cs
rename to src/Nocr.Users.Migrator/Migrations/20240330083951_InitialMigration.Designer.cs
index 464c975..1f6e9b9 100644
--- a/src/Nocr.Users.Persistence/Migrations/20240330070727_InitialMigration.Designer.cs
+++ b/src/Nocr.Users.Migrator/Migrations/20240330083951_InitialMigration.Designer.cs
@@ -8,10 +8,10 @@ using Nocr.Users.Persistence;
#nullable disable
-namespace Nocr.Users.Persistence.Migrations
+namespace Nocr.Users.Migrator.Migrations
{
[DbContext(typeof(UsersContext))]
- [Migration("20240330070727_InitialMigration")]
+ [Migration("20240330083951_InitialMigration")]
partial class InitialMigration
{
///
diff --git a/src/Nocr.Users.Persistence/Migrations/20240330070727_InitialMigration.cs b/src/Nocr.Users.Migrator/Migrations/20240330083951_InitialMigration.cs
similarity index 98%
rename from src/Nocr.Users.Persistence/Migrations/20240330070727_InitialMigration.cs
rename to src/Nocr.Users.Migrator/Migrations/20240330083951_InitialMigration.cs
index 0ce86a2..f575d34 100644
--- a/src/Nocr.Users.Persistence/Migrations/20240330070727_InitialMigration.cs
+++ b/src/Nocr.Users.Migrator/Migrations/20240330083951_InitialMigration.cs
@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
-namespace Nocr.Users.Persistence.Migrations
+namespace Nocr.Users.Migrator.Migrations
{
///
public partial class InitialMigration : Migration
diff --git a/src/Nocr.Users.Persistence/Migrations/UsersContextModelSnapshot.cs b/src/Nocr.Users.Migrator/Migrations/UsersContextModelSnapshot.cs
similarity index 98%
rename from src/Nocr.Users.Persistence/Migrations/UsersContextModelSnapshot.cs
rename to src/Nocr.Users.Migrator/Migrations/UsersContextModelSnapshot.cs
index 353423a..5bd08d9 100644
--- a/src/Nocr.Users.Persistence/Migrations/UsersContextModelSnapshot.cs
+++ b/src/Nocr.Users.Migrator/Migrations/UsersContextModelSnapshot.cs
@@ -7,7 +7,7 @@ using Nocr.Users.Persistence;
#nullable disable
-namespace Nocr.Users.Persistence.Migrations
+namespace Nocr.Users.Migrator.Migrations
{
[DbContext(typeof(UsersContext))]
partial class UsersContextModelSnapshot : ModelSnapshot
diff --git a/src/Nocr.Users.Migrator/Nocr.Users.Migrator.csproj b/src/Nocr.Users.Migrator/Nocr.Users.Migrator.csproj
new file mode 100644
index 0000000..f4f9028
--- /dev/null
+++ b/src/Nocr.Users.Migrator/Nocr.Users.Migrator.csproj
@@ -0,0 +1,37 @@
+
+
+
+ Exe
+ false
+ Linux
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+ .dockerignore
+
+
+ Always
+
+
+
+
+
+
+
+
diff --git a/src/Nocr.Users.Migrator/Program.cs b/src/Nocr.Users.Migrator/Program.cs
new file mode 100644
index 0000000..1cfc3d0
--- /dev/null
+++ b/src/Nocr.Users.Migrator/Program.cs
@@ -0,0 +1,22 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace Nocr.Users.Migrator;
+
+public class Program
+{
+ public static async Task Main(string[] args)
+ {
+ var contextFactory = new DesignTimeTextMatcherContextFactory();
+ using (var context = contextFactory.CreateDbContext(Array.Empty()))
+ {
+ await context.Database.MigrateAsync();
+ await context.SaveChangesAsync();
+ var appliedMigrations = await context.Database.GetAppliedMigrationsAsync();
+ Console.WriteLine("Applied migrations:");
+ foreach (var appliedMigration in appliedMigrations)
+ {
+ Console.WriteLine(appliedMigration);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Nocr.Users.Migrator/Readme.md b/src/Nocr.Users.Migrator/Readme.md
new file mode 100644
index 0000000..205ee9e
--- /dev/null
+++ b/src/Nocr.Users.Migrator/Readme.md
@@ -0,0 +1,10 @@
+# Nocr.TextMatcher.Migrator
+
+Проект содержит консольное приложение для применения миграций к БД.
+Для выбора строки подключения при запуске необходимо передать переменную окружения `DOTNET_ENVIRONMENT`. Для локальной разработки следует использовать `DOTNET_ENVIRONMENT=Development`.
+
+## Добавление миграций
+
+В приложении реализована логика выбора строки подключения, а dotnet ef не позволяет передать переменную окружения. Поэтому был создан скрипт AddMigration, который устанавливает значение `DOTNET_ENVIRONMENT=Development`.
+
+Пример использования: `./AddMigration MyMigration`
\ No newline at end of file
diff --git a/src/Nocr.Users.Migrator/appsettings.json b/src/Nocr.Users.Migrator/appsettings.json
new file mode 100644
index 0000000..2cfb132
--- /dev/null
+++ b/src/Nocr.Users.Migrator/appsettings.json
@@ -0,0 +1,6 @@
+{
+ "ConnectionStrings": {
+ "Development": "server=localhost;port=3307;database=nocr_users;uid=root;pwd=toor",
+ "DockerCompose": "server=nocr-users-db;port=3306;database=nocr_users;uid=root;pwd=toor"
+ }
+}
\ No newline at end of file
diff --git a/src/Nocr.Users.Persistence/DesignTimeTextMatcherContextFactory.cs b/src/Nocr.Users.Persistence/DesignTimeTextMatcherContextFactory.cs
deleted file mode 100644
index 4706c3c..0000000
--- a/src/Nocr.Users.Persistence/DesignTimeTextMatcherContextFactory.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Design;
-using Microsoft.Extensions.Configuration;
-
-namespace Nocr.Users.Persistence;
-
-public class DesignTimeTextMatcherContextFactory : IDesignTimeDbContextFactory
-{
- public UsersContext CreateDbContext(string[] args)
- {
- var optionsBuilder = new DbContextOptionsBuilder();
- var configuration = new ConfigurationBuilder()
- .AddJsonFile("appsettings.json")
- .Build();
-
- var connectionString = configuration.GetConnectionString("MariaLocal");
- optionsBuilder.UseMySql(connectionString, new MariaDbServerVersion(MariaDbServerVersion.LatestSupportedServerVersion));
-
- return new UsersContext(optionsBuilder.Options);
- }
-}
\ No newline at end of file
diff --git a/src/Nocr.Users.Persistence/Nocr.Users.Persistence.csproj b/src/Nocr.Users.Persistence/Nocr.Users.Persistence.csproj
index 021cc7b..1abff41 100644
--- a/src/Nocr.Users.Persistence/Nocr.Users.Persistence.csproj
+++ b/src/Nocr.Users.Persistence/Nocr.Users.Persistence.csproj
@@ -9,18 +9,10 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
- Always
-
-
diff --git a/src/Nocr.Users.Persistence/appsettings.json b/src/Nocr.Users.Persistence/appsettings.json
deleted file mode 100644
index 62bb122..0000000
--- a/src/Nocr.Users.Persistence/appsettings.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "ConnectionStrings": {
- "MariaLocal": "server=localhost;port=3307;database=nocr_users;uid=root;pwd=toor"
- }
-}
\ No newline at end of file