From 005bf4b10a7f2747cf34d5d2971425485e4cbe54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 5 Feb 2021 21:42:10 +0300 Subject: [PATCH] Define and handle AbpDbConnectionOptions.Databases option. --- .../Volo/Abp/Data/AbpDataModule.cs | 9 +++ .../Volo/Abp/Data/AbpDatabaseInfo.cs | 28 +++++++++ .../Abp/Data/AbpDatabaseInfoDictionary.cs | 58 +++++++++++++++++++ .../Volo/Abp/Data/AbpDbConnectionOptions.cs | 44 +++++++++++++- .../Data/DefaultConnectionStringResolver.cs | 25 ++++---- .../MultiTenantConnectionStringResolver.cs | 30 +++++----- 6 files changed, 165 insertions(+), 29 deletions(-) create mode 100644 framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDatabaseInfo.cs create mode 100644 framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDatabaseInfoDictionary.cs diff --git a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs index 8c9643012e..bc3bc8e5dd 100644 --- a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs +++ b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Volo.Abp.Modularity; using Volo.Abp.ObjectExtending; using Volo.Abp.Uow; @@ -27,6 +28,14 @@ namespace Volo.Abp.Data context.Services.AddSingleton(typeof(IDataFilter<>), typeof(DataFilter<>)); } + public override void PostConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Databases.RefreshIndexes(); + }); + } + private static void AutoAddDataSeedContributors(IServiceCollection services) { var contributors = new List(); diff --git a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDatabaseInfo.cs b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDatabaseInfo.cs new file mode 100644 index 0000000000..4f719a862a --- /dev/null +++ b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDatabaseInfo.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Data +{ + public class AbpDatabaseInfo + { + public string DatabaseName { get; set; } + + /// + /// List of connection names mapped to this database. + /// + public HashSet MappedConnections { get; } + + /// + /// Is this database used by tenants. Set this to true if this database + /// can't owned by tenants. + /// + /// Default: true. + /// + public bool IsUsedByTenants { get; set; } = true; + + internal AbpDatabaseInfo(string databaseName) + { + DatabaseName = databaseName; + MappedConnections = new HashSet(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDatabaseInfoDictionary.cs b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDatabaseInfoDictionary.cs new file mode 100644 index 0000000000..93098d303d --- /dev/null +++ b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDatabaseInfoDictionary.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.Data +{ + public class AbpDatabaseInfoDictionary : Dictionary + { + private Dictionary ConnectionIndex { get; set; } + + public AbpDatabaseInfoDictionary() + { + ConnectionIndex = new Dictionary(); + } + + [CanBeNull] + public AbpDatabaseInfo GetMappedDatabaseOrNull(string connectionStringName) + { + return ConnectionIndex.GetOrDefault(connectionStringName); + } + + public AbpDatabaseInfoDictionary Configure(string databaseName, Action configureAction) + { + var databaseInfo = this.GetOrAdd( + databaseName, + () => new AbpDatabaseInfo(databaseName) + ); + + configureAction(databaseInfo); + + return this; + } + + /// + /// This method should be called if this dictionary changes. + /// It refreshes indexes for quick access to the connection informations. + /// + public void RefreshIndexes() + { + ConnectionIndex = new Dictionary(); + + foreach (var databaseInfo in Values) + { + foreach (var mappedConnection in databaseInfo.MappedConnections) + { + if (ConnectionIndex.ContainsKey(mappedConnection)) + { + throw new AbpException( + $"A connection name can not map to multiple databases: {mappedConnection}." + ); + } + + ConnectionIndex[mappedConnection] = databaseInfo; + } + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDbConnectionOptions.cs b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDbConnectionOptions.cs index 863bffb743..9f6ad78a69 100644 --- a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDbConnectionOptions.cs +++ b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDbConnectionOptions.cs @@ -1,12 +1,54 @@ -namespace Volo.Abp.Data +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Data { public class AbpDbConnectionOptions { public ConnectionStrings ConnectionStrings { get; set; } + + public AbpDatabaseInfoDictionary Databases { get; set; } public AbpDbConnectionOptions() { ConnectionStrings = new ConnectionStrings(); + Databases = new AbpDatabaseInfoDictionary(); + } + + public string GetConnectionStringOrNull( + string connectionStringName, + bool fallbackToDatabaseMappings = true, + bool fallbackToDefault = true) + { + var connectionString = ConnectionStrings.GetOrDefault(connectionStringName); + if (!connectionString.IsNullOrEmpty()) + { + return connectionString; + } + + if (fallbackToDatabaseMappings) + { + var database = Databases.GetMappedDatabaseOrNull(connectionStringName); + if (database != null) + { + connectionString = ConnectionStrings.GetOrDefault(database.DatabaseName); + if (!connectionString.IsNullOrEmpty()) + { + return connectionString; + } + } + } + + if (fallbackToDefault) + { + connectionString = ConnectionStrings.Default; + if (!connectionString.IsNullOrWhiteSpace()) + { + return connectionString; + } + } + + return null; } } } diff --git a/framework/src/Volo.Abp.Data/Volo/Abp/Data/DefaultConnectionStringResolver.cs b/framework/src/Volo.Abp.Data/Volo/Abp/Data/DefaultConnectionStringResolver.cs index 4d5ce2fb27..f907cb0cb6 100644 --- a/framework/src/Volo.Abp.Data/Volo/Abp/Data/DefaultConnectionStringResolver.cs +++ b/framework/src/Volo.Abp.Data/Volo/Abp/Data/DefaultConnectionStringResolver.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; @@ -10,7 +9,8 @@ namespace Volo.Abp.Data { protected AbpDbConnectionOptions Options { get; } - public DefaultConnectionStringResolver(IOptionsSnapshot options) + public DefaultConnectionStringResolver( + IOptionsSnapshot options) { Options = options.Value; } @@ -28,18 +28,19 @@ namespace Volo.Abp.Data private string ResolveInternal(string connectionStringName) { - //Get module specific value if provided - if (!connectionStringName.IsNullOrEmpty()) + if (connectionStringName == null) { - var moduleConnString = Options.ConnectionStrings.GetOrDefault(connectionStringName); - if (!moduleConnString.IsNullOrEmpty()) - { - return moduleConnString; - } + return Options.ConnectionStrings.Default; + } + + var connectionString = Options.GetConnectionStringOrNull(connectionStringName); + + if (!connectionString.IsNullOrEmpty()) + { + return connectionString; } - //Get default value - return Options.ConnectionStrings.Default; + return null; } } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenantConnectionStringResolver.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenantConnectionStringResolver.cs index 959c910901..41ba7873ac 100644 --- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenantConnectionStringResolver.cs +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenantConnectionStringResolver.cs @@ -41,7 +41,7 @@ namespace Volo.Abp.MultiTenancy } var tenantDefaultConnectionString = tenant.ConnectionStrings.Default; - + //Requesting default connection string... if (connectionStringName == null || connectionStringName == ConnectionStrings.DefaultConnectionStringName) @@ -59,6 +59,18 @@ namespace Volo.Abp.MultiTenancy //Found for the tenant return connString; } + + //Fallback to the mapped database for the specific connection string + var database = Options.Databases.GetMappedDatabaseOrNull(connectionStringName); + if (database != null) + { + connString = tenant.ConnectionStrings.GetOrDefault(database.DatabaseName); + if (!connString.IsNullOrWhiteSpace()) + { + //Found for the tenant + return connString; + } + } //Fallback to tenant's default connection string if available if (!tenantDefaultConnectionString.IsNullOrWhiteSpace()) @@ -66,21 +78,7 @@ namespace Volo.Abp.MultiTenancy return tenantDefaultConnectionString; } - //Try to find the specific connection string for given name - var connStringInOptions = Options.ConnectionStrings.GetOrDefault(connectionStringName); - if (!connStringInOptions.IsNullOrWhiteSpace()) - { - return connStringInOptions; - } - - //Fallback to the global default connection string - var defaultConnectionString = Options.ConnectionStrings.Default; - if (!defaultConnectionString.IsNullOrWhiteSpace()) - { - return defaultConnectionString; - } - - throw new AbpException("No connection string defined!"); + return await base.ResolveAsync(connectionStringName); } [Obsolete("Use ResolveAsync method.")]