From 3441b6b76f70a390240b7d6ff57bbae8ea0cac74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stano=20Pe=C5=A5ko?= Date: Fri, 26 Jul 2019 22:41:44 +0200 Subject: [PATCH 1/2] Updated Kros.KORM and used new KormConnectionSettings --- src/DatabaseFactory.cs | 6 +- src/KormBuilder.cs | 49 ++++--------- src/Kros.KORM.Extensions.Asp.csproj | 2 +- src/Properties/Resources.Designer.cs | 9 +++ src/Properties/Resources.resx | 3 + src/ServiceCollectionExtensions.cs | 83 +++------------------- tests/KormBuilderShould.cs | 16 ++--- tests/KormBuilderWithDatabaseShould.cs | 5 +- tests/ServiceCollectionExtensionsShould.cs | 26 +++---- 9 files changed, 58 insertions(+), 141 deletions(-) diff --git a/src/DatabaseFactory.cs b/src/DatabaseFactory.cs index 0665dda..a5080ad 100644 --- a/src/DatabaseFactory.cs +++ b/src/DatabaseFactory.cs @@ -23,7 +23,7 @@ private static Dictionary AddBuildersDictionary(IServiceCol /// The name of the database builder. /// Database builder. /// , if this was the first builder added, otherwise . - internal static bool AddBuilder(IServiceCollection services, string name, KormBuilder builder) + internal static void AddBuilder(IServiceCollection services, string name, KormBuilder builder) { Dictionary builders = AddBuildersDictionary(services); if (builders.ContainsKey(name)) @@ -31,10 +31,6 @@ internal static bool AddBuilder(IServiceCollection services, string name, KormBu throw new ArgumentException(string.Format(Resources.DuplicateDatabaseName, name), nameof(name)); } builders.Add(name, builder); - - // We need to know if it was the first builder added. - // The first builder is added into the service container also as IDatabase dependency. - return builders.Count == 1; } private readonly ConcurrentDictionary _databases = new ConcurrentDictionary(); diff --git a/src/KormBuilder.cs b/src/KormBuilder.cs index 6611eab..f1dc9ab 100644 --- a/src/KormBuilder.cs +++ b/src/KormBuilder.cs @@ -1,4 +1,5 @@ using Kros.Data; +using Kros.KORM.Extensions.Asp.Properties; using Kros.KORM.Migrations; using Kros.KORM.Migrations.Middleware; using Kros.KORM.Migrations.Providers; @@ -18,32 +19,15 @@ public class KormBuilder /// public const string DefaultConnectionStringName = "DefaultConnection"; - internal const string DefaultProviderName = Kros.Data.SqlServer.SqlServerDataHelper.ClientId; - internal const bool DefaultAutoMigrate = false; - private readonly IDatabaseBuilder _builder; /// - /// Initializes a new instance of the class. Automatic migrations are off and - /// Microsoft SQL Server KORM provider is used. + /// Initializes a new instance of the class. /// /// The service collection. /// The database connection string. public KormBuilder(IServiceCollection services, string connectionString) - : this(services, connectionString, DefaultAutoMigrate, DefaultProviderName) - { - } - - /// - /// Initializes a new instance of the class. Microsoft SQL Server KORM provider is used. - /// - /// The service collection. - /// The database connection string. - /// - /// Value for setting if automatic migrations () are allowed or not. - /// - public KormBuilder(IServiceCollection services, string connectionString, bool autoMigrate) - : this(services, connectionString, autoMigrate, DefaultProviderName) + : this(services, new KormConnectionSettings(connectionString)) { } @@ -51,20 +35,16 @@ public KormBuilder(IServiceCollection services, string connectionString, bool au /// Initializes a new instance of the class. /// /// The service collection. - /// The database connection string. - /// - /// Value for setting if automatic migrations () are allowed or not. - /// - /// KORM provider value. - public KormBuilder(IServiceCollection services, string connectionString, bool autoMigrate, string kormProvider) + /// The database connection settings. + public KormBuilder(IServiceCollection services, KormConnectionSettings connectionSettings) { Services = Check.NotNull(services, nameof(services)); - ConnectionString = Check.NotNullOrWhiteSpace(connectionString, nameof(connectionString)); - KormProvider = Check.NotNullOrWhiteSpace(kormProvider, nameof(kormProvider)); - AutoMigrate = autoMigrate; + ConnectionSettings = Check.NotNull(connectionSettings, nameof(connectionSettings)); + Check.NotNullOrWhiteSpace( + connectionSettings.ConnectionString, nameof(connectionSettings), Resources.EmptyConnectionStringInSettings); _builder = Database.Builder; - _builder.UseConnection(connectionString, kormProvider); + _builder.UseConnection(connectionSettings); } /// @@ -72,9 +52,7 @@ public KormBuilder(IServiceCollection services, string connectionString, bool au /// public IServiceCollection Services { get; } - internal string ConnectionString { get; } - internal string KormProvider { get; } - internal bool AutoMigrate { get; } + internal KormConnectionSettings ConnectionSettings { get; } /// /// Use database configuration. @@ -104,7 +82,8 @@ public KormBuilder UseDatabaseConfiguration(DatabaseConfigurationBase databaseCo /// This instance. public KormBuilder InitDatabaseForIdGenerator() { - IIdGeneratorFactory factory = IdGeneratorFactories.GetFactory(ConnectionString, KormProvider); + IIdGeneratorFactory factory = IdGeneratorFactories.GetFactory( + ConnectionSettings.ConnectionString, ConnectionSettings.KormProvider); using (IIdGenerator idGenerator = factory.GetGenerator(string.Empty)) { idGenerator.InitDatabaseForIdGenerator(); @@ -123,7 +102,7 @@ public KormBuilder AddKormMigrations(Action setupAction = null .AddMemoryCache() .AddTransient((Func)(s => { - var database = new Database(ConnectionString, KormProvider); + var database = new Database(ConnectionSettings); MigrationOptions options = SetupMigrationOptions(setupAction); return new MigrationsRunner(database, options); })); @@ -152,7 +131,7 @@ private static MigrationOptions SetupMigrationOptions(Action s /// public void Migrate() { - if (AutoMigrate) + if (ConnectionSettings.AutoMigrate) { Services.BuildServiceProvider() .GetService() diff --git a/src/Kros.KORM.Extensions.Asp.csproj b/src/Kros.KORM.Extensions.Asp.csproj index d8626ba..46f3d2c 100644 --- a/src/Kros.KORM.Extensions.Asp.csproj +++ b/src/Kros.KORM.Extensions.Asp.csproj @@ -30,7 +30,7 @@ - + diff --git a/src/Properties/Resources.Designer.cs b/src/Properties/Resources.Designer.cs index 0e3b792..384757b 100644 --- a/src/Properties/Resources.Designer.cs +++ b/src/Properties/Resources.Designer.cs @@ -78,6 +78,15 @@ internal static string DuplicateDatabaseName { } } + /// + /// Looks up a localized string similar to Connection string in connection settings is empty.. + /// + internal static string EmptyConnectionStringInSettings { + get { + return ResourceManager.GetString("EmptyConnectionStringInSettings", resourceCulture); + } + } + /// /// Looks up a localized string similar to Connection strings section does not contain a connection string with name '{0}'.. /// diff --git a/src/Properties/Resources.resx b/src/Properties/Resources.resx index eb57381..5cb848d 100644 --- a/src/Properties/Resources.resx +++ b/src/Properties/Resources.resx @@ -124,6 +124,9 @@ Database with name "{0}" was already added. 0 - name of the database/connection string + + Connection string in connection settings is empty. + Connection strings section does not contain a connection string with name '{0}'. 0 - connection string name in appsettings diff --git a/src/ServiceCollectionExtensions.cs b/src/ServiceCollectionExtensions.cs index a02b36f..7afe5ff 100644 --- a/src/ServiceCollectionExtensions.cs +++ b/src/ServiceCollectionExtensions.cs @@ -2,9 +2,8 @@ using Kros.Utils; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using System; -using System.Collections.Concurrent; -using System.Data.Common; namespace Kros.KORM.Extensions.Asp { @@ -13,23 +12,6 @@ namespace Kros.KORM.Extensions.Asp /// public static class ServiceCollectionExtensions { - /// - /// Key for connection string for setting KORM database provider. If the provider is not set in connection string, - /// Microsoft SQL Server provider is used. - /// - public const string KormProviderKey = "KormProvider"; - - /// - /// Key for connection string for setting if automatic migrations are enabled (). - /// If the value is not set in connection string, automatic migrations are disabled. - /// - public const string KormAutoMigrateKey = "KormAutoMigrate"; - - // Only one IDatabaseFactory for service collection must be registered. - // This dictionary holds flags for already used service collections. - private static readonly ConcurrentDictionary _databaseFactoryAdded = - new ConcurrentDictionary(); - /// /// Register KORM into DI container. The connection string with default name /// () from is used for database. @@ -162,74 +144,29 @@ public static KormBuilder AddKorm(this IServiceCollection services, string conne Check.NotNullOrWhiteSpace(connectionString, nameof(connectionString)); Check.NotNullOrWhiteSpace(name, nameof(name)); - var cnstrBuilder = new DbConnectionStringBuilder - { - ConnectionString = connectionString - }; - string providerName = GetKormProvider(cnstrBuilder); - bool autoMigrate = GetKormAutoMigrate(cnstrBuilder); - connectionString = cnstrBuilder.ConnectionString; // Previous methods remove keys, so we want clean connection string. - - if (string.IsNullOrWhiteSpace(connectionString)) + var connectionSettings = new KormConnectionSettings(connectionString); + if (string.IsNullOrWhiteSpace(connectionSettings.ConnectionString)) { throw new ArgumentException(Resources.ConnectionStringContainsOnlyKormKeys, nameof(connectionString)); } - return AddKormBuilder(services, name, connectionString, autoMigrate, providerName); + return AddKormBuilder(services, name, connectionSettings); } private static KormBuilder AddKormBuilder( IServiceCollection services, string name, - string connectionString, - bool autoMigrate, - string providerName) + KormConnectionSettings connectionSettings) { AddDatabaseFactory(services); - var builder = new KormBuilder(services, connectionString, autoMigrate, providerName); - if (DatabaseFactory.AddBuilder(services, name, builder)) - { - // First database is added also as IDatabase mainly for backward compatibility. - services.AddScoped( - serviceProvider => serviceProvider.GetRequiredService().GetDatabase(name)); - } + var builder = new KormBuilder(services, connectionSettings); + DatabaseFactory.AddBuilder(services, name, builder); + services.TryAdd(ServiceDescriptor.Scoped( + serviceProvider => serviceProvider.GetRequiredService().GetDatabase(name))); return builder; } private static void AddDatabaseFactory(IServiceCollection services) - { - _ = _databaseFactoryAdded.GetOrAdd(services, svcs => - { - svcs.AddScoped(provider => new DatabaseFactory(services)); - return true; - }); - } - - private static string GetKormProvider(DbConnectionStringBuilder cnstrBuilder) - { - if (cnstrBuilder.TryGetValue(KormProviderKey, out object cnstrProviderName)) - { - cnstrBuilder.Remove(KormProviderKey); - string providerName = (string)cnstrProviderName; - if (!string.IsNullOrWhiteSpace(providerName)) - { - return providerName; - } - } - return KormBuilder.DefaultProviderName; - } - - private static bool GetKormAutoMigrate(DbConnectionStringBuilder cnstrBuilder) - { - if (cnstrBuilder.TryGetValue(KormAutoMigrateKey, out object cnstrAutoMigrate)) - { - cnstrBuilder.Remove(KormAutoMigrateKey); - if (bool.TryParse((string)cnstrAutoMigrate, out bool autoMigrate)) - { - return autoMigrate; - } - } - return KormBuilder.DefaultAutoMigrate; - } + => services.TryAdd(ServiceDescriptor.Scoped(provider => new DatabaseFactory(services))); } } diff --git a/tests/KormBuilderShould.cs b/tests/KormBuilderShould.cs index 951c721..ace5edd 100644 --- a/tests/KormBuilderShould.cs +++ b/tests/KormBuilderShould.cs @@ -20,23 +20,17 @@ public void ThrowArgumentExceptionWhenArgumentsAreInvalid() Action action = () => new KormBuilder(null, connectionString); action.Should().Throw().And.ParamName.Should().Be("services"); - action = () => new KormBuilder(services, null); + action = () => new KormBuilder(services, (string)null); action.Should().Throw().And.ParamName.Should().Be("connectionString"); + action = () => new KormBuilder(services, (KormConnectionSettings)null); + action.Should().Throw().And.ParamName.Should().Be("connectionSettings"); + action = () => new KormBuilder(services, string.Empty); action.Should().Throw().And.ParamName.Should().Be("connectionString"); action = () => new KormBuilder(services, " \t "); action.Should().Throw().And.ParamName.Should().Be("connectionString"); - - action = () => new KormBuilder(services, connectionString, false, null); - action.Should().Throw().And.ParamName.Should().Be("kormProvider"); - - action = () => new KormBuilder(services, connectionString, false, string.Empty); - action.Should().Throw().And.ParamName.Should().Be("kormProvider"); - - action = () => new KormBuilder(services, connectionString, false, " \t "); - action.Should().Throw().And.ParamName.Should().Be("kormProvider"); } [Fact] @@ -80,6 +74,6 @@ public void UseDatabaseConfiguration() } private KormBuilder CreateKormBuilder(bool autoMigrate) - => new KormBuilder(new ServiceCollection(), "server=localhost", autoMigrate); + => new KormBuilder(new ServiceCollection(), $"server=localhost;KormAutoMigrate={autoMigrate}"); } } diff --git a/tests/KormBuilderWithDatabaseShould.cs b/tests/KormBuilderWithDatabaseShould.cs index b042873..e217660 100644 --- a/tests/KormBuilderWithDatabaseShould.cs +++ b/tests/KormBuilderWithDatabaseShould.cs @@ -29,15 +29,12 @@ protected override string BaseConnectionString [Fact] public void InitDatabaseForIdGenerator() { - KormBuilder kormBuilder = CreateKormBuilder(false); + var kormBuilder = new KormBuilder(new ServiceCollection(), ServerHelper.Connection.ConnectionString); kormBuilder.InitDatabaseForIdGenerator(); CheckTableAndProcedure(); } - private KormBuilder CreateKormBuilder(bool autoMigrate) - => new KormBuilder(new ServiceCollection(), ServerHelper.Connection.ConnectionString, autoMigrate); - private void CheckTableAndProcedure() { using (ConnectionHelper.OpenConnection(ServerHelper.Connection)) diff --git a/tests/ServiceCollectionExtensionsShould.cs b/tests/ServiceCollectionExtensionsShould.cs index d9f1e2f..b0b8b7a 100644 --- a/tests/ServiceCollectionExtensionsShould.cs +++ b/tests/ServiceCollectionExtensionsShould.cs @@ -11,6 +11,8 @@ namespace Kros.KORM.Extensions.Api.UnitTests { public class ServiceCollectionExtensionsShould { + private const string DefaultProviderName = Kros.Data.SqlServer.SqlServerDataHelper.ClientId; + [Fact] public void AddKormToContainer() { @@ -31,7 +33,7 @@ public void UseDefaultConnectionStringIfConfigurationIsProvided() KormBuilder builder = services.AddKorm(configuration); - builder.ConnectionString.Should().Be(configuration.GetConnectionString("DefaultConnection")); + builder.ConnectionSettings.ConnectionString.Should().Be(configuration.GetConnectionString("DefaultConnection")); } [Fact] @@ -75,24 +77,24 @@ public void ThrowArgumentExceptionIfConnectionStringContainsOnlyKormValues() } [Theory] - [InlineData("server=localhost;", KormBuilder.DefaultProviderName, false)] - [InlineData("server=localhost;KormProvider=", KormBuilder.DefaultProviderName, false)] - [InlineData("server=localhost;KormProvider=' \t '", KormBuilder.DefaultProviderName, false)] + [InlineData("server=localhost;", DefaultProviderName, false)] + [InlineData("server=localhost;KormProvider=", DefaultProviderName, false)] + [InlineData("server=localhost;KormProvider=' \t '", DefaultProviderName, false)] [InlineData("server=localhost;KormProvider=LoremIpsum", "LoremIpsum", false)] - [InlineData("server=localhost;KormAutoMigrate=true", KormBuilder.DefaultProviderName, true)] - [InlineData("server=localhost;KormAutoMigrate=false", KormBuilder.DefaultProviderName, false)] - [InlineData("server=localhost;KormAutoMigrate=InvalidValue", KormBuilder.DefaultProviderName, false)] - [InlineData("server=localhost;KormAutoMigrate=", KormBuilder.DefaultProviderName, false)] - [InlineData("server=localhost;KormAutoMigrate=' \t '", KormBuilder.DefaultProviderName, false)] + [InlineData("server=localhost;KormAutoMigrate=true", DefaultProviderName, true)] + [InlineData("server=localhost;KormAutoMigrate=false", DefaultProviderName, false)] + [InlineData("server=localhost;KormAutoMigrate=InvalidValue", DefaultProviderName, false)] + [InlineData("server=localhost;KormAutoMigrate=", DefaultProviderName, false)] + [InlineData("server=localhost;KormAutoMigrate=' \t '", DefaultProviderName, false)] public void ParseKormConnectionStringKeys(string connectionString, string provider, bool autoMigrate) { var services = new ServiceCollection(); KormBuilder builder = services.AddKorm(connectionString); - builder.KormProvider.Should().Be(provider); - builder.AutoMigrate.Should().Be(autoMigrate); - builder.ConnectionString.Should().Be("server=localhost"); + builder.ConnectionSettings.KormProvider.Should().Be(provider); + builder.ConnectionSettings.AutoMigrate.Should().Be(autoMigrate); + builder.ConnectionSettings.ConnectionString.Should().Be("server=localhost"); } [Fact] From b129fe37cea91b04cd7627ec6e8eb0d53c0df117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stano=20Pe=C5=A5ko?= Date: Sun, 28 Jul 2019 18:37:13 +0200 Subject: [PATCH 2/2] Migrations middleware supports multiple databases --- README.md | 40 ++++++++++++------- src/DatabaseFactory.cs | 15 ++++++- src/IDatabaseFactory.cs | 23 ++++++++++- src/KormBuilder.cs | 26 ++++++------ src/Kros.KORM.Extensions.Asp.csproj | 2 +- .../Middleware/MigrationMiddlewareOptions.cs | 4 +- .../Middleware/MigrationsMiddleware.cs | 39 ++++++++++-------- tests/KormBuilderShould.cs | 28 ------------- 8 files changed, 99 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index b88223c..5d946eb 100644 --- a/README.md +++ b/README.md @@ -89,33 +89,43 @@ CONSTRAINT [PK_People] PRIMARY KEY CLUSTERED ([Id] ASC) GO ``` -Migration can also be executed through an HTTP request. By calling the `/kormmigration` endpoint, the necessary migrations will be executed. -However, you need to add middleware: +Migration can also be executed through an HTTP request. By calling the `/kormmigration` endpoint, the necessary migrations will be executed. However, you need to add middleware: ``` csharp public void Configure(IApplicationBuilder app, IHostingEnvironment env) { - app.UseKormMigrations(o => - { - o.EndpointUrl = "/kormmigration"; - }); + app.UseKormMigrations(); } ``` +You can change the endpoint URL by configuration: + +``` csharp +public void Configure(IApplicationBuilder app, IHostingEnvironment env) +{ + app.UseKormMigrations(options => + { + options.EndpointUrl = "/loremipsum"; + }); +} +``` + +If multiple KORM databases are registered, all of them have unique name. Migrations are performed per database and the name of the database is specified in URL as another path segment: `/kormmigration/dbname` If the name is not specified, default connection string will be used (`DefaultConnection`). + If you have scripts stored in a different way (for example, somewhere on a disk or in another assembly), you can configure your own providers to get these scripts. ``` csharp public void ConfigureServices(IServiceCollection services) { - services.AddKorm(Configuration) - .AddKormMigrations(o => - { - var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName.StartWith("Demo.DatabaseLayer")); - o.AddAssemblyScriptsProvider(assembly, "Demo.DatabaseLayer.Resources"); - o.AddFileScriptsProvider(@"C:\scripts\"); - o.AddScriptsProvider(new MyCustomScriptsProvider()); - }) - .Migrate(); + services.AddKorm(Configuration) + .AddKormMigrations(o => + { + var assembly = AppDomain.CurrentDomain.GetAssemblies() .FirstOrDefault(x => x.FullName.StartWith ("Demo.DatabaseLayer") ); + o.AddAssemblyScriptsProvider(assembly, "Demo.DatabaseLayer.Resources"); + o.AddFileScriptsProvider(@"C:\scripts\"); + o.AddScriptsProvider(new MyCustomScriptsProvider()); + }) + .Migrate(); } ``` diff --git a/src/DatabaseFactory.cs b/src/DatabaseFactory.cs index a5080ad..589e7b8 100644 --- a/src/DatabaseFactory.cs +++ b/src/DatabaseFactory.cs @@ -1,4 +1,5 @@ using Kros.KORM.Extensions.Asp.Properties; +using Kros.KORM.Migrations; using Kros.Utils; using Microsoft.Extensions.DependencyInjection; using System; @@ -43,6 +44,18 @@ internal DatabaseFactory(IServiceCollection services) } IDatabase IDatabaseFactory.GetDatabase(string name) + { + KormBuilder builder = GetBuilder(name); + return _databases.GetOrAdd(name, _ => builder.Build()); + } + + IMigrationsRunner IDatabaseFactory.GetMigrationsRunner(string name) + { + KormBuilder builder = GetBuilder(name); + return builder.MigrationsRunner; + } + + private KormBuilder GetBuilder(string name) { if (_disposed) { @@ -56,7 +69,7 @@ IDatabase IDatabaseFactory.GetDatabase(string name) string.Format(Resources.InvalidDatabaseName, name, nameof(ServiceCollectionExtensions.AddKorm)), nameof(name)); } - return _databases.GetOrAdd(name, _ => builder.Build()); + return builder; } public void Dispose() diff --git a/src/IDatabaseFactory.cs b/src/IDatabaseFactory.cs index 45dde2d..ae38083 100644 --- a/src/IDatabaseFactory.cs +++ b/src/IDatabaseFactory.cs @@ -1,4 +1,5 @@ -using System; +using Kros.KORM.Migrations; +using System; namespace Kros.KORM.Extensions.Asp { @@ -22,5 +23,25 @@ public interface IDatabaseFactory : IDisposable /// /// IDatabase GetDatabase(string name); + + /// + /// Returns the migrations runner for database with specified . + /// + /// Name of the database. + /// Implementation of or . + /// + /// Migrations must be added by . + /// If migrations was not added for specified database, this method returns . + /// + /// The value of is . + /// + /// The value of is: + /// + /// Empty string. + /// String containing whitespace characters. + /// Ivalid name. The database with that name is not registered. + /// + /// + IMigrationsRunner GetMigrationsRunner(string name); } } diff --git a/src/KormBuilder.cs b/src/KormBuilder.cs index f1dc9ab..2d9c9e3 100644 --- a/src/KormBuilder.cs +++ b/src/KormBuilder.cs @@ -20,6 +20,7 @@ public class KormBuilder public const string DefaultConnectionStringName = "DefaultConnection"; private readonly IDatabaseBuilder _builder; + private IMigrationsRunner _migrationsRunner; /// /// Initializes a new instance of the class. @@ -52,6 +53,12 @@ public KormBuilder(IServiceCollection services, KormConnectionSettings connectio /// public IServiceCollection Services { get; } + /// + /// for this database, if it was set + /// using method. + /// + public IMigrationsRunner MigrationsRunner => _migrationsRunner; + internal KormConnectionSettings ConnectionSettings { get; } /// @@ -98,15 +105,9 @@ public KormBuilder InitDatabaseForIdGenerator() /// This instance of . public KormBuilder AddKormMigrations(Action setupAction = null) { - Services - .AddMemoryCache() - .AddTransient((Func)(s => - { - var database = new Database(ConnectionSettings); - MigrationOptions options = SetupMigrationOptions(setupAction); - return new MigrationsRunner(database, options); - })); - + Services.AddMemoryCache(); + MigrationOptions options = SetupMigrationOptions(setupAction); + _migrationsRunner = new MigrationsRunner(ConnectionSettings.GetFullConnectionString(), options); return this; } @@ -131,12 +132,9 @@ private static MigrationOptions SetupMigrationOptions(Action s /// public void Migrate() { - if (ConnectionSettings.AutoMigrate) + if (ConnectionSettings.AutoMigrate && (MigrationsRunner != null)) { - Services.BuildServiceProvider() - .GetService() - .MigrateAsync() - .Wait(); + MigrationsRunner.MigrateAsync().Wait(); } } diff --git a/src/Kros.KORM.Extensions.Asp.csproj b/src/Kros.KORM.Extensions.Asp.csproj index 46f3d2c..291b116 100644 --- a/src/Kros.KORM.Extensions.Asp.csproj +++ b/src/Kros.KORM.Extensions.Asp.csproj @@ -30,7 +30,7 @@ - + diff --git a/src/Migrations/Middleware/MigrationMiddlewareOptions.cs b/src/Migrations/Middleware/MigrationMiddlewareOptions.cs index 7a3adc0..f3786fd 100644 --- a/src/Migrations/Middleware/MigrationMiddlewareOptions.cs +++ b/src/Migrations/Middleware/MigrationMiddlewareOptions.cs @@ -8,9 +8,9 @@ namespace Kros.KORM.Migrations.Middleware public class MigrationMiddlewareOptions { /// - /// Migrations endpoint URL. + /// Migrations endpoint URL. Default value is /kormmigration. /// - public string EndpointUrl { get; set; } = "/kormmigrate"; + public string EndpointUrl { get; set; } = "/kormmigration"; /// /// Minimum time between two migrations. diff --git a/src/Migrations/Middleware/MigrationsMiddleware.cs b/src/Migrations/Middleware/MigrationsMiddleware.cs index 809c158..6fe720c 100644 --- a/src/Migrations/Middleware/MigrationsMiddleware.cs +++ b/src/Migrations/Middleware/MigrationsMiddleware.cs @@ -1,4 +1,5 @@ -using Kros.Utils; +using Kros.KORM.Extensions.Asp; +using Kros.Utils; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Caching.Memory; using System.Threading.Tasks; @@ -10,7 +11,7 @@ namespace Kros.KORM.Migrations.Middleware /// public class MigrationsMiddleware { - private const string WasMigrationExecutedKey = "WasMigrationExecuted"; + private const string MigrationExecutedKey = "KormMigrationsExecuted"; #pragma warning disable IDE0052 // Remove unread private members private readonly RequestDelegate _next; @@ -24,40 +25,46 @@ public class MigrationsMiddleware /// The next delegate. /// Memory cache. /// Migration options. - public MigrationsMiddleware( - RequestDelegate next, - IMemoryCache cache, - MigrationMiddlewareOptions options) + public MigrationsMiddleware(RequestDelegate next, IMemoryCache cache, MigrationMiddlewareOptions options) { _next = next; _options = Check.NotNull(options, nameof(options)); _cache = Check.NotNull(cache, nameof(cache)); } -#pragma warning disable IDE0060 // Remove unused parameter /// /// Invokes the specified context. /// /// The context. - /// Migrations runner. - public async Task Invoke(HttpContext context, IMigrationsRunner migrationsRunner) + /// Database factory for getting . + public async Task Invoke(HttpContext context, IDatabaseFactory databaseFactory) { - if (CanMigrate()) + string databaseName = null; + if (context.Request.Path.HasValue) { - SetupCache(); + databaseName = context.Request.Path.Value.Trim('/'); + } + if (string.IsNullOrEmpty(databaseName)) + { + databaseName = KormBuilder.DefaultConnectionStringName; + } + IMigrationsRunner migrationsRunner = databaseFactory.GetMigrationsRunner(databaseName); + if ((migrationsRunner != null) && CanMigrate(databaseName)) + { + SetupCache(databaseName); await migrationsRunner.MigrateAsync(); } } -#pragma warning restore IDE0060 // Remove unused parameter - private bool CanMigrate() - => !_cache.TryGetValue(WasMigrationExecutedKey, out bool migrated) || !migrated; + private bool CanMigrate(string name) => !_cache.TryGetValue(GetCacheKey(name), out bool migrated) || !migrated; - private void SetupCache() + private void SetupCache(string name) { var options = new MemoryCacheEntryOptions(); options.SetSlidingExpiration(_options.SlidingExpirationBetweenMigrations); - _cache.Set(WasMigrationExecutedKey, true, options); + _cache.Set(GetCacheKey(name), true, options); } + + private string GetCacheKey(string name) => MigrationExecutedKey + "-" + name; } } diff --git a/tests/KormBuilderShould.cs b/tests/KormBuilderShould.cs index ace5edd..c40c83a 100644 --- a/tests/KormBuilderShould.cs +++ b/tests/KormBuilderShould.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Kros.KORM.Extensions.Asp; using Kros.KORM.Metadata; -using Kros.KORM.Migrations; using Microsoft.Extensions.DependencyInjection; using NSubstitute; using System; @@ -33,33 +32,6 @@ public void ThrowArgumentExceptionWhenArgumentsAreInvalid() action.Should().Throw().And.ParamName.Should().Be("connectionString"); } - [Fact] - public void AddMigrationsToContainer() - { - KormBuilder kormBuilder = CreateKormBuilder(false); - kormBuilder.AddKormMigrations(); - - kormBuilder.Services.BuildServiceProvider() - .GetService() - .Should().NotBeNull(); - } - - [Theory] - [InlineData(true, 1)] - [InlineData(false, 0)] - public void ExecuteMigrationsBasedOnAutoMigrateValue(bool autoMigrate, int migrateCallCount) - { - KormBuilder kormBuilder = CreateKormBuilder(autoMigrate); - kormBuilder.AddKormMigrations(); - - IMigrationsRunner migrationRunner = Substitute.For(); - kormBuilder.Services.AddSingleton(migrationRunner); - - kormBuilder.Migrate(); - - migrationRunner.Received(migrateCallCount).MigrateAsync(); - } - [Fact] public void UseDatabaseConfiguration() {