**1.4.0**: Multi-schema support and table prefixing

This commit is contained in:
Tudor Stanciu 2025-07-30 01:00:35 +03:00
parent 28f1bdbd2e
commit aad1b9c39f
18 changed files with 551 additions and 55 deletions

View File

@ -1,6 +1,6 @@
namespace Netmash.Infrastructure.DatabaseMigration.Constants
{
public enum MetadataLocation
public enum MetadataStorage
{
XmlFile,
Database

View File

@ -1,13 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Netmash.Infrastructure.DatabaseMigration.Entities;
using Netmash.Infrastructure.DatabaseMigration.Entities.Configurations;
using Netmash.Infrastructure.DatabaseMigration.Services;
namespace Netmash.Infrastructure.DatabaseMigration.DbContexts
{
internal class MigrationDbContext : DbContext
{
public MigrationDbContext(DbContextOptions<MigrationDbContext> options) : base(options)
private readonly TableNamingService _tableNamingService;
public MigrationDbContext(DbContextOptions<MigrationDbContext> options, TableNamingService tableNamingService) : base(options)
{
_tableNamingService = tableNamingService;
}
public DbSet<MigrationSignature> MigrationSignatures { get; set; }
@ -16,9 +20,9 @@ namespace Netmash.Infrastructure.DatabaseMigration.DbContexts
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new MigratedScriptConfiguration());
modelBuilder.ApplyConfiguration(new MigratedVersionConfiguration());
modelBuilder.ApplyConfiguration(new MigrationSignatureConfiguration());
modelBuilder.ApplyConfiguration(new MigratedScriptConfiguration(_tableNamingService));
modelBuilder.ApplyConfiguration(new MigratedVersionConfiguration(_tableNamingService));
modelBuilder.ApplyConfiguration(new MigrationSignatureConfiguration(_tableNamingService));
}
}
}

View File

@ -9,6 +9,7 @@ using Netmash.Infrastructure.DatabaseMigration.Repositories;
using Netmash.Infrastructure.DatabaseMigration.Services;
using Netmash.Infrastructure.DatabaseMigration.Services.Abstractions;
using System;
using System.Text.RegularExpressions;
namespace Netmash.Infrastructure.DatabaseMigration
{
@ -18,13 +19,18 @@ namespace Netmash.Infrastructure.DatabaseMigration
public static void AddMigration(this IServiceCollection services,
DatabaseType databaseType = DatabaseType.SQLite,
MetadataLocation metadataLocation = MetadataLocation.XmlFile,
MetadataStorage metadataStorage = MetadataStorage.XmlFile,
string connectionName = "DatabaseConnection",
string workspace = "Workspace",
string scriptsDirectoryPath = "Scripts")
string scriptsDirectoryPath = "Scripts",
string metadataSchema = "migration",
string metadataPrefix = null)
{
var serviceConfiguration = new ServiceConfiguration(databaseType, metadataLocation, connectionName, workspace, scriptsDirectoryPath);
ValidateConfiguration(databaseType, metadataStorage, metadataSchema, metadataPrefix);
var serviceConfiguration = new ServiceConfiguration(databaseType, metadataStorage, connectionName, workspace, scriptsDirectoryPath, metadataSchema, metadataPrefix);
services.AddSingleton(serviceConfiguration);
services.AddSingleton<TableNamingService>();
services.AddDataAccess(serviceConfiguration);
services.AddSingleton<IMetadataLocationService, MetadataLocationService>();
services.AddSingleton<IMigrationSignaturesService, MigrationSignaturesService>();
@ -72,5 +78,38 @@ namespace Netmash.Infrastructure.DatabaseMigration
var migrationService = app.ApplicationServices.GetService<IMigrationService>();
migrationService.Execute();
}
private static void ValidateConfiguration(DatabaseType databaseType, MetadataStorage metadataStorage, string metadataSchema, string metadataPrefix)
{
// Validate metadataPrefix characters
if (!string.IsNullOrEmpty(metadataPrefix))
{
if (!Regex.IsMatch(metadataPrefix, @"^[a-zA-Z0-9_]+$"))
{
throw new ArgumentException("MetadataPrefix can only contain letters, numbers, and underscores.", nameof(metadataPrefix));
}
}
// SQLite doesn't support multiple schemas
if (databaseType == DatabaseType.SQLite && metadataStorage == MetadataStorage.Database)
{
if (!string.IsNullOrEmpty(metadataSchema) && metadataSchema != "migration")
{
throw new NotSupportedException("SQLite provider does not support multiple schemas. To differentiate migration tables between multiple services, use the metadataPrefix parameter instead.");
}
}
// Schema can only be used with SQL Server and Database storage
if (metadataStorage == MetadataStorage.XmlFile && !string.IsNullOrEmpty(metadataSchema) && metadataSchema != "migration")
{
throw new NotSupportedException("MetadataSchema can only be used when metadataStorage is Database. For XML file storage, use metadataPrefix to differentiate between services.");
}
// Schema should only be used with SQL Server
if (databaseType != DatabaseType.SQLServer && !string.IsNullOrEmpty(metadataSchema) && metadataSchema != "migration")
{
throw new NotSupportedException("MetadataSchema can only be used with SQL Server database type.");
}
}
}
}

View File

@ -1,13 +1,24 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Netmash.Infrastructure.DatabaseMigration.Services;
namespace Netmash.Infrastructure.DatabaseMigration.Entities.Configurations
{
internal class MigratedScriptConfiguration : IEntityTypeConfiguration<MigratedScript>
{
private readonly TableNamingService _tableNamingService;
public MigratedScriptConfiguration(TableNamingService tableNamingService)
{
_tableNamingService = tableNamingService;
}
public void Configure(EntityTypeBuilder<MigratedScript> builder)
{
builder.ToTable("MigratedScript", "migration").HasKey(z => z.Id);
var tableName = _tableNamingService.GetTableName("MigratedScript");
var schemaName = _tableNamingService.GetSchemaName();
builder.ToTable(tableName, schemaName).HasKey(z => z.Id);
builder.Property(z => z.Id).ValueGeneratedOnAdd();
}
}

View File

@ -1,13 +1,24 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Netmash.Infrastructure.DatabaseMigration.Services;
namespace Netmash.Infrastructure.DatabaseMigration.Entities.Configurations
{
internal class MigratedVersionConfiguration : IEntityTypeConfiguration<MigratedVersion>
{
private readonly TableNamingService _tableNamingService;
public MigratedVersionConfiguration(TableNamingService tableNamingService)
{
_tableNamingService = tableNamingService;
}
public void Configure(EntityTypeBuilder<MigratedVersion> builder)
{
builder.ToTable("MigratedVersion", "migration").HasKey(z => z.Id);
var tableName = _tableNamingService.GetTableName("MigratedVersion");
var schemaName = _tableNamingService.GetSchemaName();
builder.ToTable(tableName, schemaName).HasKey(z => z.Id);
builder.Property(z => z.Id).ValueGeneratedOnAdd();
builder.HasMany(z => z.Scripts).WithOne().HasForeignKey(z => z.VersionId);
}

View File

@ -1,13 +1,24 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Netmash.Infrastructure.DatabaseMigration.Services;
namespace Netmash.Infrastructure.DatabaseMigration.Entities.Configurations
{
internal class MigrationSignatureConfiguration : IEntityTypeConfiguration<MigrationSignature>
{
private readonly TableNamingService _tableNamingService;
public MigrationSignatureConfiguration(TableNamingService tableNamingService)
{
_tableNamingService = tableNamingService;
}
public void Configure(EntityTypeBuilder<MigrationSignature> builder)
{
builder.ToTable("MigrationSignature", "migration").HasKey(z => z.Id);
var tableName = _tableNamingService.GetTableName("MigrationSignature");
var schemaName = _tableNamingService.GetSchemaName();
builder.ToTable(tableName, schemaName).HasKey(z => z.Id);
builder.Property(z => z.Id).ValueGeneratedOnAdd();
builder.HasMany(z => z.MigratedVersions).WithOne().HasForeignKey(z => z.SignatureId);
}

View File

@ -5,18 +5,22 @@ namespace Netmash.Infrastructure.DatabaseMigration.Models
internal class ServiceConfiguration
{
public DatabaseType DatabaseType { get; }
public MetadataLocation MetadataLocation { get; }
public MetadataStorage MetadataStorage { get; }
public string ConnectionName { get; }
public string Workspace { get; }
public string ScriptsDirectory { get; }
public string MetadataSchema { get; }
public string MetadataPrefix { get; }
public ServiceConfiguration(DatabaseType databaseType, MetadataLocation metadataLocation, string connectionName, string workspace, string scriptsDirectory)
public ServiceConfiguration(DatabaseType databaseType, MetadataStorage metadataStorage, string connectionName, string workspace, string scriptsDirectory, string metadataSchema, string metadataPrefix)
{
DatabaseType = databaseType;
MetadataLocation = metadataLocation;
MetadataStorage = metadataStorage;
ConnectionName = connectionName;
Workspace = workspace;
ScriptsDirectory = scriptsDirectory;
MetadataSchema = metadataSchema;
MetadataPrefix = metadataPrefix;
}
}
}

View File

@ -7,10 +7,12 @@
<RepositoryUrl>https://lab.code-rove.com/gitea/bricks/netmash</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageTags>Netmash database migration</PackageTags>
<Version>1.3.0</Version>
<Version>1.4.0</Version>
<PackageReleaseNotes>1.1.0: From this version the migration service can keep its metadata in the database (SqlServer or Sqlite)
1.2.0: .NET 6 upgrade
1.3.0: .NET 8 upgrade</PackageReleaseNotes>
1.3.0: .NET 8 upgrade
1.4.0: Multi-schema support, table prefixing</PackageReleaseNotes>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
@ -38,4 +40,11 @@
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.3" />
</ItemGroup>
<ItemGroup>
<None Update="README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,356 @@
# Netmash.Infrastructure.DatabaseMigration
A comprehensive database migration library for .NET applications that supports multiple database providers and flexible metadata storage options.
## Features
- **Multi-Database Support**: SQL Server and SQLite
- **Flexible Metadata Storage**: XML files or database tables
- **Multi-Schema Support**: Separate migration metadata for multiple services
- **Version-Based Migrations**: Organized script execution based on semantic versioning
- **Table Prefixing**: Support for prefixed table names to avoid conflicts
- **Automatic Schema Creation**: Creates migration tables and schemas automatically
- **Logging Integration**: Comprehensive logging of migration activities
- **ASP.NET Core Integration**: Easy dependency injection setup
## Installation
Add the package to your project:
```bash
dotnet add package Netmash.Infrastructure.DatabaseMigration
```
## Quick Start
### 1. Basic Setup
```csharp
// In Startup.cs or Program.cs
services.AddMigration();
// In Configure method or app startup
app.UseMigration();
```
### 2. Configuration
Add connection string to `appsettings.json`:
```json
{
"ConnectionStrings": {
"DatabaseConnection": "Server=localhost;Database=MyApp;Trusted_Connection=true;"
}
}
```
### 3. Organize Migration Scripts
Create directory structure:
```
Scripts/
├── 1.0.0/
│ ├── 01.CreateUserTable.sql
│ └── 02.CreateIndexes.sql
├── 1.0.1/
│ ├── 01.AddUserRoles.sql
│ └── 02.UpdateUserData.sql
└── 1.1.0/
└── 01.AddAuditFields.sql
```
## Configuration Options
### AddMigration Parameters
| Parameter | Type | Default | Description |
| ---------------------- | ----------------- | ---------------------- | ------------------------------------------------------ |
| `databaseType` | `DatabaseType` | `SQLite` | Database provider (SQLite, SQLServer) |
| `metadataStorage` | `MetadataStorage` | `XmlFile` | Where to store migration metadata |
| `connectionName` | `string` | `"DatabaseConnection"` | Connection string name in configuration |
| `workspace` | `string` | `"Workspace"` | Working directory for XML files |
| `scriptsDirectoryPath` | `string` | `"Scripts"` | Directory containing migration scripts |
| `metadataSchema` | `string` | `"migration"` | Database schema for migration tables (SQL Server only) |
| `metadataPrefix` | `string` | `null` | Prefix for table names or XML file names |
## Usage Examples
### SQL Server with Database Metadata Storage
```csharp
services.AddMigration(
databaseType: DatabaseType.SQLServer,
metadataStorage: MetadataStorage.Database,
connectionName: "DefaultConnection",
scriptsDirectoryPath: "Database/Migrations"
);
```
### Multi-Service Setup with Schema Separation
```csharp
// Service 1
services.AddMigration(
databaseType: DatabaseType.SQLServer,
metadataStorage: MetadataStorage.Database,
metadataSchema: "service1_migrations"
);
// Service 2
services.AddMigration(
databaseType: DatabaseType.SQLServer,
metadataStorage: MetadataStorage.Database,
metadataSchema: "service2_migrations"
);
```
### SQLite with Table Prefixing
```csharp
services.AddMigration(
databaseType: DatabaseType.SQLite,
metadataStorage: MetadataStorage.Database,
metadataPrefix: "myservice_"
);
// Creates tables: myservice_MigrationSignature, myservice_MigratedVersion, myservice_MigratedScript
```
### XML File Storage with Prefixing
```csharp
services.AddMigration(
metadataStorage: MetadataStorage.XmlFile,
metadataPrefix: "userservice_",
workspace: "C:\\MyApp\\Data"
);
// Creates file: C:\MyApp\Data\userservice_MigrationSignatures.xml
```
## Database Schema Creation
### SQL Server
The library automatically creates:
- Schema (if specified): `CREATE SCHEMA [your_schema]`
- Migration tables in the specified schema
- Proper foreign key relationships
### SQLite
The library automatically creates:
- Migration tables (with optional prefix)
- Proper foreign key relationships
## Migration Script Organization
Scripts are organized by version directories and executed in alphabetical order:
```
Scripts/
├── 1.0.0/ # Version 1.0.0 scripts
│ ├── 01.Users.sql
│ └── 02.Roles.sql
├── 1.0.1/ # Version 1.0.1 scripts
│ └── 01.Permissions.sql
└── 2.0.0/ # Version 2.0.0 scripts
├── 01.RefactorTables.sql
└── 02.MigrateData.sql
```
## Connection String Workspace Placeholder
Use `{Workspace}` placeholder in connection strings for dynamic paths:
```json
{
"ConnectionStrings": {
"DatabaseConnection": "Data Source={Workspace}\\myapp.db"
}
}
```
## Metadata Storage Options
### XML File Storage
- **Pros**: Simple, file-based, easy to backup
- **Cons**: Not suitable for distributed deployments
- **Use Case**: Single-instance applications, development environments
### Database Storage
- **Pros**: Centralized, supports distributed deployments, transactional
- **Cons**: Requires database access, slightly more complex
- **Use Case**: Production environments, multiple service instances
## Validation Rules
The library enforces several validation rules:
1. **Prefix Validation**: `metadataPrefix` can only contain letters, numbers, and underscores
2. **SQLite Schema Limitation**: SQLite doesn't support custom schemas - use `metadataPrefix` instead
3. **XML File Schema Limitation**: Custom schemas only work with database storage
4. **Provider Compatibility**: Custom schemas only supported with SQL Server
## Migration Tables Schema
### MigrationSignature
- `Id` (int, primary key)
- `MigrationDate` (datetime)
- `MachineName` (varchar)
- `LastVersion` (varchar)
### MigratedVersion
- `Id` (int, primary key)
- `SignatureId` (int, foreign key)
- `Version` (varchar)
### MigratedScript
- `Id` (int, primary key)
- `VersionId` (int, foreign key)
- `Script` (varchar)
## Logging
The library provides comprehensive logging:
```csharp
// Configuration logging
[INFO] Database migration configured with DatabaseType: SQLServer, MetadataStorage: Database
[INFO] Using database schema: microservice1
[INFO] Using metadata prefix: ms1_
// Migration execution logging
[INFO] Starting migration...
[INFO] Running script pack '1.0.1' with 3 scripts:
[INFO] Running sql script: 'Scripts\1.0.1\01.CreateTable.sql'
```
## Error Handling
Common exceptions and solutions:
### Invalid Prefix
```
ArgumentException: MetadataPrefix can only contain letters, numbers, and underscores.
```
**Solution**: Use only alphanumeric characters and underscores in prefix.
### SQLite Schema Error
```
NotSupportedException: SQLite provider does not support multiple schemas.
```
**Solution**: Use `metadataPrefix` instead of `metadataSchema` with SQLite.
### XML Schema Error
```
NotSupportedException: MetadataSchema can only be used when metadataStorage is Database.
```
**Solution**: Use database storage or remove custom schema parameter.
## Advanced Configuration
### Custom Workspace with Dynamic Connection String
```csharp
// appsettings.json
{
"ConnectionStrings": {
"DatabaseConnection": "Data Source={Workspace}\\myapp.db"
}
}
// Startup
services.AddMigration(
databaseType: DatabaseType.SQLite,
workspace: "C:\\MyAppData"
);
// Results in: Data Source=C:\MyAppData\myapp.db
```
### Multiple Services Sharing Database
```csharp
// User Service
services.AddMigration(
databaseType: DatabaseType.SQLServer,
metadataStorage: MetadataStorage.Database,
metadataSchema: "user_migrations",
scriptsDirectoryPath: "UserService/Migrations"
);
// Order Service
services.AddMigration(
databaseType: DatabaseType.SQLServer,
metadataStorage: MetadataStorage.Database,
metadataSchema: "order_migrations",
scriptsDirectoryPath: "OrderService/Migrations"
);
```
## Best Practices
1. **Version Naming**: Use semantic versioning (1.0.0, 1.0.1, 1.1.0)
2. **Script Naming**: Use numbered prefixes (01.CreateTable.sql, 02.AddIndex.sql)
3. **Idempotent Scripts**: Write scripts that can be run multiple times safely
4. **Backup Strategy**: Always backup databases before running migrations
5. **Testing**: Test migrations on development/staging environments first
6. **Rollback Planning**: Plan rollback strategies for complex migrations
## Migration Process Flow
1. **Initialization**: Check and create migration metadata storage
2. **Version Detection**: Determine last applied migration version
3. **Script Discovery**: Find unapplied script versions
4. **Execution**: Run scripts in version and alphabetical order
5. **Tracking**: Record successful migrations in metadata
6. **Logging**: Log all activities and results
## Troubleshooting
### Migration Not Running
- Verify connection string is correct
- Check that Scripts directory exists and contains version folders
- Ensure proper permissions on database/file system
### Metadata Table Missing
- Verify database connection
- Check that the application has database creation permissions
- Review logs for schema creation errors
### Version Skipping
- Ensure version folders follow semantic versioning
- Check for missing or incorrectly named version directories
- Verify script execution order and dependencies
## License
This project is part of the Netmash framework. See the main repository for license information.
## Contributing
Contributions are welcome! Please follow the established patterns and include appropriate tests.
## Version History
- **1.4.0**: Multi-schema support, table prefixing
- **1.3.0**: .NET 8 upgrade
- **1.2.0**: .NET 6 upgrade
- **1.1.0**: Database metadata storage support
- **1.0.0**: Initial release with XML file storage

View File

@ -2,6 +2,7 @@
using Netmash.Infrastructure.DatabaseMigration.Constants;
using Netmash.Infrastructure.DatabaseMigration.DbContexts;
using Netmash.Infrastructure.DatabaseMigration.Entities;
using Netmash.Infrastructure.DatabaseMigration.Services;
using System;
using System.Linq;
using System.Threading.Tasks;
@ -11,10 +12,12 @@ namespace Netmash.Infrastructure.DatabaseMigration.Repositories
internal class MigrationRepository : IMigrationRepository
{
private readonly MigrationDbContext _dbContext;
private readonly TableNamingService _tableNamingService;
public MigrationRepository(MigrationDbContext dbContext)
public MigrationRepository(MigrationDbContext dbContext, TableNamingService tableNamingService)
{
_dbContext = dbContext;
_tableNamingService = tableNamingService;
}
public async Task ExecuteSqlRaw(string sqlRaw)
@ -24,10 +27,13 @@ namespace Netmash.Infrastructure.DatabaseMigration.Repositories
public async Task<bool> MigrationTablesAreSet(DatabaseType databaseType)
{
var tableName = _tableNamingService.GetTableName("MigrationSignature");
var schemaName = _tableNamingService.GetSchemaName();
var query = databaseType switch
{
DatabaseType.SQLServer => "select count(1) from sys.objects where name = 'MigrationSignature' and type = 'U' and SCHEMA_NAME(schema_id)='migration'",
DatabaseType.SQLite => "select count(1) from sqlite_master where type='table' and name='MigrationSignature';",
DatabaseType.SQLServer => $"select count(1) from sys.objects where name = '{tableName}' and type = 'U' and SCHEMA_NAME(schema_id)='{schemaName}'",
DatabaseType.SQLite => $"select count(1) from sqlite_master where type='table' and name='{tableName}';",
_ => throw new NotImplementedException($"DatabaseMigration type {databaseType} is not implemented"),
};

View File

@ -1,4 +1,4 @@
if not exists (select top 1 1 from sys.schemas where name = 'migration')
if not exists (select top 1 1 from sys.schemas where name = '{MetadataSchema}')
begin
EXEC ('CREATE SCHEMA [migration] AUTHORIZATION [dbo]')
EXEC ('CREATE SCHEMA [{MetadataSchema}] AUTHORIZATION [dbo]')
end

View File

@ -1,30 +1,30 @@
if not exists (select top 1 1 from sys.objects where name = 'MigrationSignature' and type = 'U' and SCHEMA_NAME(schema_id) = 'migration')
if not exists (select top 1 1 from sys.objects where name = '{TablePrefix}MigrationSignature' and type = 'U' and SCHEMA_NAME(schema_id) = '{MetadataSchema}')
begin
create table migration.MigrationSignature
create table {MetadataSchema}.{TablePrefix}MigrationSignature
(
Id int identity(1, 1) constraint PK_MigrationSignature primary key,
Id int identity(1, 1) constraint PK_{TablePrefix}MigrationSignature primary key,
MigrationDate Datetime not null,
MachineName varchar(30) not null,
LastVersion varchar(20) not null
)
end
if not exists (select top 1 1 from sys.objects where name = 'MigratedVersion' and type = 'U' and SCHEMA_NAME(schema_id) = 'migration')
if not exists (select top 1 1 from sys.objects where name = '{TablePrefix}MigratedVersion' and type = 'U' and SCHEMA_NAME(schema_id) = '{MetadataSchema}')
begin
create table migration.MigratedVersion
create table {MetadataSchema}.{TablePrefix}MigratedVersion
(
Id int identity(1, 1) constraint PK_MigratedVersion primary key,
SignatureId int constraint FK_MigratedVersion_MigrationSignature foreign key references migration.MigrationSignature(Id),
Id int identity(1, 1) constraint PK_{TablePrefix}MigratedVersion primary key,
SignatureId int constraint FK_{TablePrefix}MigratedVersion_{TablePrefix}MigrationSignature foreign key references {MetadataSchema}.{TablePrefix}MigrationSignature(Id),
[Version] varchar(20) not null
)
end
if not exists (select top 1 1 from sys.objects where name = 'MigratedScript' and type = 'U' and SCHEMA_NAME(schema_id) = 'migration')
if not exists (select top 1 1 from sys.objects where name = '{TablePrefix}MigratedScript' and type = 'U' and SCHEMA_NAME(schema_id) = '{MetadataSchema}')
begin
create table migration.MigratedScript
create table {MetadataSchema}.{TablePrefix}MigratedScript
(
Id int identity(1, 1) constraint PK_MigratedScript primary key,
VersionId int constraint FK_MigratedScript_MigratedVersion foreign key references migration.MigratedVersion(Id),
Id int identity(1, 1) constraint PK_{TablePrefix}MigratedScript primary key,
VersionId int constraint FK_{TablePrefix}MigratedScript_{TablePrefix}MigratedVersion foreign key references {MetadataSchema}.{TablePrefix}MigratedVersion(Id),
Script varchar(250) not null
)
end

View File

@ -1,7 +1,7 @@
CREATE TABLE "MigrationSignature" (
CREATE TABLE "{TablePrefix}MigrationSignature" (
"Id" INTEGER NOT NULL,
"MigrationDate" TEXT NOT NULL,
"MachineName" TEXT NOT NULL,
"LastVersion" TEXT NOT NULL,
CONSTRAINT "PK_MigrationSignature" PRIMARY KEY("Id" AUTOINCREMENT)
CONSTRAINT "PK_{TablePrefix}MigrationSignature" PRIMARY KEY("Id" AUTOINCREMENT)
);

View File

@ -1,7 +1,7 @@
CREATE TABLE "MigratedVersion" (
CREATE TABLE "{TablePrefix}MigratedVersion" (
"Id" INTEGER NOT NULL,
"SignatureId" INTEGER,
"Version" TEXT NOT NULL,
CONSTRAINT "PK_MigratedVersion" PRIMARY KEY("Id" AUTOINCREMENT),
CONSTRAINT "FK_MigratedVersion_MigrationSignature" FOREIGN KEY("SignatureId") REFERENCES "MigrationSignature"("Id")
CONSTRAINT "PK_{TablePrefix}MigratedVersion" PRIMARY KEY("Id" AUTOINCREMENT),
CONSTRAINT "FK_{TablePrefix}MigratedVersion_{TablePrefix}MigrationSignature" FOREIGN KEY("SignatureId") REFERENCES "{TablePrefix}MigrationSignature"("Id")
);

View File

@ -1,7 +1,7 @@
CREATE TABLE "MigratedScript" (
CREATE TABLE "{TablePrefix}MigratedScript" (
"Id" INTEGER NOT NULL,
"VersionId" INTEGER,
"Script" TEXT NOT NULL,
CONSTRAINT "PK_MigratedScript" PRIMARY KEY("Id" AUTOINCREMENT),
CONSTRAINT "FK_MigratedScript_MigratedVersion" FOREIGN KEY("VersionId") REFERENCES "MigratedVersion"("Id")
CONSTRAINT "PK_{TablePrefix}MigratedScript" PRIMARY KEY("Id" AUTOINCREMENT),
CONSTRAINT "FK_{TablePrefix}MigratedScript_{TablePrefix}MigratedVersion" FOREIGN KEY("VersionId") REFERENCES "{TablePrefix}MigratedVersion"("Id")
);

View File

@ -17,26 +17,28 @@ namespace Netmash.Infrastructure.DatabaseMigration.Services
private readonly ServiceConfiguration _configuration;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<MetadataLocationService> _logger;
private readonly TableNamingService _tableNamingService;
public MetadataLocationService(ServiceConfiguration configuration, IServiceProvider serviceProvider, ILogger<MetadataLocationService> logger)
public MetadataLocationService(ServiceConfiguration configuration, IServiceProvider serviceProvider, ILogger<MetadataLocationService> logger, TableNamingService tableNamingService)
{
_configuration=configuration;
_serviceProvider=serviceProvider;
_logger=logger;
_tableNamingService = tableNamingService;
}
public async Task Check()
{
switch (_configuration.MetadataLocation)
switch (_configuration.MetadataStorage)
{
case MetadataLocation.XmlFile:
case MetadataStorage.XmlFile:
CheckWorkspace();
break;
case MetadataLocation.Database:
case MetadataStorage.Database:
await CheckMigrationTables();
break;
default:
throw new NotImplementedException($"Metadata location {_configuration.MetadataLocation} is not implemented.");
throw new NotImplementedException($"Metadata storage {_configuration.MetadataStorage} is not implemented.");
}
}
@ -74,7 +76,8 @@ namespace Netmash.Infrastructure.DatabaseMigration.Services
}
var resourceContent = GetManifestResourceContent(assembly, resource);
await _repository.ExecuteSqlRaw(resourceContent);
var processedContent = _tableNamingService.ProcessSqlScript(resourceContent);
await _repository.ExecuteSqlRaw(processedContent);
}
}
}

View File

@ -25,36 +25,39 @@ namespace Netmash.Infrastructure.DatabaseMigration.Services
public MigrationSignaturesService(ServiceConfiguration configuration, IServiceProvider serviceProvider)
{
_migrationSignaturesFilePath = Path.Combine(configuration.Workspace, _migrationSignaturesFileName);
var fileName = !string.IsNullOrEmpty(configuration.MetadataPrefix)
? $"{configuration.MetadataPrefix}{_migrationSignaturesFileName}"
: _migrationSignaturesFileName;
_migrationSignaturesFilePath = Path.Combine(configuration.Workspace, fileName);
_configuration = configuration;
_serviceProvider = serviceProvider;
}
public async Task<MigrationSignature> GetLastMigrationSignature()
{
switch (_configuration.MetadataLocation)
switch (_configuration.MetadataStorage)
{
case MetadataLocation.XmlFile:
case MetadataStorage.XmlFile:
return GetLastMigrationSignatureFromFile();
case MetadataLocation.Database:
case MetadataStorage.Database:
return await GetLastMigrationSignatureFromDatabase();
default:
throw new NotImplementedException($"Metadata location {_configuration.MetadataLocation} is not implemented.");
throw new NotImplementedException($"Metadata storage {_configuration.MetadataStorage} is not implemented.");
}
}
public async Task SaveMigrationSignature(MigrationSignature migrationSignature)
{
switch (_configuration.MetadataLocation)
switch (_configuration.MetadataStorage)
{
case MetadataLocation.XmlFile:
case MetadataStorage.XmlFile:
SaveMigrationSignatureToFile(migrationSignature);
break;
case MetadataLocation.Database:
case MetadataStorage.Database:
await SaveMigrationSignatureToDatabase(migrationSignature);
break;
default:
throw new NotImplementedException($"Metadata location {_configuration.MetadataLocation} is not implemented.");
throw new NotImplementedException($"Metadata storage {_configuration.MetadataStorage} is not implemented.");
}
}

View File

@ -0,0 +1,39 @@
using Netmash.Infrastructure.DatabaseMigration.Constants;
using Netmash.Infrastructure.DatabaseMigration.Models;
namespace Netmash.Infrastructure.DatabaseMigration.Services
{
internal class TableNamingService
{
private readonly ServiceConfiguration _configuration;
public TableNamingService(ServiceConfiguration configuration)
{
_configuration = configuration;
}
public string GetTableName(string baseTableName)
{
return !string.IsNullOrEmpty(_configuration.MetadataPrefix)
? $"{_configuration.MetadataPrefix}{baseTableName}"
: baseTableName;
}
public string GetSchemaName()
{
return _configuration.DatabaseType == DatabaseType.SQLServer
? _configuration.MetadataSchema
: null;
}
public string ProcessSqlScript(string sqlContent)
{
var tablePrefix = _configuration.MetadataPrefix ?? string.Empty;
var metadataSchema = _configuration.MetadataSchema ?? "migration";
return sqlContent
.Replace("{TablePrefix}", tablePrefix)
.Replace("{MetadataSchema}", metadataSchema);
}
}
}