From f2df8e96db1cacc17d9f5d22b3ab25a062d01213 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 23 Feb 2023 16:29:15 -0500 Subject: [PATCH] fix #2567 - migrate tenant connection string details from database to appsettings.json --- Oqtane.Client/Installer/Installer.razor | 2 +- Oqtane.Client/Modules/Admin/Site/Index.razor | 28 +- Oqtane.Client/Modules/Admin/Sites/Add.razor | 10 +- Oqtane.Client/Modules/Admin/Sql/Index.razor | 383 +++++++++++++----- .../Modules/Admin/SystemInfo/Index.razor | 6 +- .../Resources/Installer/Installer.resx | 2 +- .../Resources/Modules/Admin/Site/Index.resx | 10 +- .../Resources/Modules/Admin/Sites/Add.resx | 51 +-- .../Resources/Modules/Admin/Sql/Index.resx | 79 +++- .../Services/Interfaces/ISystemService.cs | 6 - Oqtane.Client/Services/SystemService.cs | 5 +- .../Controllers/InstallationController.cs | 2 +- Oqtane.Server/Controllers/SqlController.cs | 18 +- Oqtane.Server/Controllers/SystemController.cs | 18 +- .../OqtaneServiceCollectionExtensions.cs | 1 + Oqtane.Server/Infrastructure/ConfigManager.cs | 12 + .../Infrastructure/DatabaseManager.cs | 72 +++- .../Interfaces/IConfigManager.cs | 2 + .../HtmlText/Manager/HtmlTextManager.cs | 13 +- .../HtmlText/Repository/HtmlTextContext.cs | 2 +- .../Repository/Context/DBContextBase.cs | 46 +-- .../Context/DBContextDependencies.cs | 20 + .../Repository/Context/InstallationContext.cs | 5 +- .../Repository/Context/MasterDBContext.cs | 4 +- .../Repository/Context/TenantDBContext.cs | 4 +- .../Interfaces/IDBContextDependencies.cs | 13 + .../Repository/Interfaces/ISqlRepository.cs | 4 +- Oqtane.Server/Repository/SqlRepository.cs | 31 +- Oqtane.Server/Startup.cs | 2 +- .../Server/Manager/[Module]Manager.cs | 14 +- .../Server/Repository/[Module]Context.cs | 2 +- Oqtane.Shared/Models/SqlQuery.cs | 3 +- Oqtane.Shared/Shared/Constants.cs | 1 + 33 files changed, 562 insertions(+), 309 deletions(-) create mode 100644 Oqtane.Server/Repository/Context/DBContextDependencies.cs create mode 100644 Oqtane.Server/Repository/Interfaces/IDBContextDependencies.cs diff --git a/Oqtane.Client/Installer/Installer.razor b/Oqtane.Client/Installer/Installer.razor index 18146904..5f10a74b 100644 --- a/Oqtane.Client/Installer/Installer.razor +++ b/Oqtane.Client/Installer/Installer.razor @@ -55,7 +55,7 @@ else {
- +
diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index bb9a7660..30a586a1 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -267,16 +267,16 @@
-
+
- +
- +
@@ -284,10 +284,7 @@
-
- - -
+
@@ -342,8 +339,6 @@ private string _tenant = string.Empty; private string _database = string.Empty; private string _connectionstring = string.Empty; - private string _connectionstringtype = "password"; - private string _connectionstringtoggle = string.Empty; private string _createdby; private DateTime _createdon; private string _modifiedby; @@ -358,7 +353,6 @@ { try { - _connectionstringtoggle = SharedLocalizer["ShowPassword"]; _themeList = await ThemeService.GetThemesAsync(); Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId); if (site != null) @@ -466,20 +460,6 @@ } } - private void ToggleConnectionString() - { - if (_connectionstringtype == "password") - { - _connectionstringtype = "text"; - _connectionstringtoggle = SharedLocalizer["HidePassword"]; - } - else - { - _connectionstringtype = "password"; - _connectionstringtoggle = SharedLocalizer["ShowPassword"]; - } - } - private async Task SaveSite() { validated = true; diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 509928f8..4b61f801 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -103,7 +103,7 @@ else
- +
- +
@if (_databases != null) { @@ -160,7 +160,7 @@ else else {
- +
@@ -329,7 +329,7 @@ else if (_tenantid == "+") { - if (!string.IsNullOrEmpty(_tenantName) && _tenants.FirstOrDefault(item => item.Name == _tenantName) == null) + if (!string.IsNullOrEmpty(_tenantName) && !_tenants.Exists(item => item.Name == _tenantName)) { // validate host credentials var user = new User(); diff --git a/Oqtane.Client/Modules/Admin/Sql/Index.razor b/Oqtane.Client/Modules/Admin/Sql/Index.razor index c6bc3408..6d0dee92 100644 --- a/Oqtane.Client/Modules/Admin/Sql/Index.razor +++ b/Oqtane.Client/Modules/Admin/Sql/Index.razor @@ -1,6 +1,7 @@ @namespace Oqtane.Modules.Admin.Sql @inherits ModuleBase @inject NavigationManager NavigationManager +@inject ISystemService SystemService @inject ITenantService TenantService @inject IDatabaseService DatabaseService @inject ISqlService SqlService @@ -14,123 +15,284 @@ else {
- -
- -
- -
-
- @if (_tenantid != "-1") - { -
- -
- -
-
-
- -
-
- - -
+
+ +
+ +
+
+ @if (_connection == "+") + { +
+ +
+
-
- -
- -
-
- } -
-
- -
-
- @if (_results != null) - { - @if (_results.Count > 0) - { - -
- @foreach (KeyValuePair kvp in _results.First()) - { - @kvp.Key - } -
- - @foreach (KeyValuePair kvp in context) - { - @kvp.Value - } - -
- } - else - { - @Localizer["Return.NoResult"] - } -
-
- } +
+ +
+ @if (_databases != null) + { +
+ + @if (!_showConnectionString) + { + + } + else + { + + } +
+ } +
+
+ @if (!_showConnectionString) + { + if (_databaseConfigType != null) + { + @DatabaseConfigComponent + } + } + else + { +
+ +
+ +
+
+ } +
+ + } + else + { + @if (_connection != "-") + { +
+ +
+ @if (_databases != null) + { + + } +
+
+ @if (!string.IsNullOrEmpty(_tenant)) + { +
+ +
+ +
+
+ } +
+ +
+
+ + +
+
+
+
+ +
+ +
+
+
+ +
+
+ @if (_results != null) + { + @if (_results.Count > 0) + { + +
+ @foreach (KeyValuePair kvp in _results.First()) + { + @kvp.Key + } +
+ + @foreach (KeyValuePair kvp in context) + { + @kvp.Value + } + +
+ } + else + { + @Localizer["Return.NoResult"] + } +
+ +
+ } + } + } +
} @code { - private List _tenants; - private string _tenantid = "-1"; - private string _database = string.Empty; + private string _connection = "-"; + private Dictionary _connections; + private List _tenants; + private List _databases; + + private string _name = string.Empty; + private string _databasetype = string.Empty; + private Type _databaseConfigType; + private object _databaseConfig; + private RenderFragment DatabaseConfigComponent { get; set; } + private bool _showConnectionString = false; + private string _tenant = string.Empty; private string _connectionstring = string.Empty; private string _connectionstringtype = "password"; private string _connectionstringtoggle = string.Empty; private string _sql = string.Empty; - private List> _results; + private List> _results; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; - protected override async Task OnInitializedAsync() - { - try - { - _tenants = await TenantService.GetTenantsAsync(); + protected override async Task OnInitializedAsync() + { + try + { + _connections = await SystemService.GetSystemInfoAsync("connectionstrings"); + _tenants = await TenantService.GetTenantsAsync(); + _databases = await DatabaseService.GetDatabasesAsync(); _connectionstringtoggle = SharedLocalizer["ShowPassword"]; } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Tenants {Error}", ex.Message); - AddModuleMessage(ex.Message, MessageType.Error); - } - } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Tenants {Error}", ex.Message); + AddModuleMessage(ex.Message, MessageType.Error); + } + } - private async void TenantChanged(ChangeEventArgs e) - { - try - { - _tenantid = (string)e.Value; - var tenants = await TenantService.GetTenantsAsync(); - var _databases = await DatabaseService.GetDatabasesAsync(); - var tenant = tenants.Find(item => item.TenantId == int.Parse(_tenantid)); - if (tenant != null) - { - _database = _databases.Find(item => item.DBType == tenant.DBType)?.Name; - _connectionstring = tenant.DBConnectionString; - } - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Tenant {TenantId} {Error}", _tenantid, ex.Message); - AddModuleMessage(ex.Message, MessageType.Error); - } - } + private async void ConnectionChanged(ChangeEventArgs e) + { + try + { + _connection = (string)e.Value; + if (_connection != "-" && _connection != "+") + { + _connectionstring = _connections[_connection].ToString(); + _tenant = ""; + _databasetype = "-"; + var tenant = _tenants.FirstOrDefault(item => item.DBConnectionString == _connection); + if (tenant != null) + { + _tenant = tenant.Name; + _databasetype = _databases.FirstOrDefault(item => item.DBType == tenant.DBType).Name; + } + } + else + { + if (_databases.Exists(item => item.IsDefault)) + { + _databasetype = _databases.Find(item => item.IsDefault).Name; + } + else + { + _databasetype = "LocalDB"; + } + _showConnectionString = false; + LoadDatabaseConfigComponent(); + } + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Connection {Connection} {Error}", _connection, ex.Message); + AddModuleMessage(ex.Message, MessageType.Error); + } + } + + private void DatabaseTypeChanged(ChangeEventArgs eventArgs) + { + try + { + _databasetype = (string)eventArgs.Value; + _showConnectionString = false; + LoadDatabaseConfigComponent(); + } + catch + { + AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error); + } + } + + private void LoadDatabaseConfigComponent() + { + var database = _databases.SingleOrDefault(d => d.Name == _databasetype); + if (database != null) + { + _databaseConfigType = Type.GetType(database.ControlType); + DatabaseConfigComponent = builder => + { + builder.OpenComponent(0, _databaseConfigType); + builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); }); + builder.CloseComponent(); + }; + } + } + + private void ShowConnectionString() + { + if (_databaseConfig is IDatabaseConfigControl databaseConfigControl) + { + _connectionstring = databaseConfigControl.GetConnectionString(); + } + _showConnectionString = !_showConnectionString; + } + + private async Task Add() + { + var connectionstring = _connectionstring; + if (!_showConnectionString && _databaseConfig is IDatabaseConfigControl databaseConfigControl) + { + connectionstring = databaseConfigControl.GetConnectionString(); + } + if (!string.IsNullOrEmpty(_name) && !string.IsNullOrEmpty(connectionstring)) + { + var settings = new Dictionary(); + settings.Add($"{SettingKeys.ConnectionStringsSection}:{_name}", connectionstring); + await SystemService.UpdateSystemInfoAsync(settings); + _connections = await SystemService.GetSystemInfoAsync("connectionstrings"); + _connection = "-"; + AddModuleMessage(Localizer["Message.Connection.Added"], MessageType.Success); + } + else + { + AddModuleMessage(Localizer["Message.Required.Connection"], MessageType.Warning); + } + } private void ToggleConnectionString() { @@ -145,14 +307,15 @@ else _connectionstringtoggle = SharedLocalizer["ShowPassword"]; } } - + private async Task Execute() - { - try - { - if (_tenantid != "-1" && !string.IsNullOrEmpty(_sql)) - { - var sqlquery = new SqlQuery { TenantId = int.Parse(_tenantid), Query = _sql }; + { + try + { + if (_databasetype != "-" && !string.IsNullOrEmpty(_sql)) + { + var dbtype = _databases.FirstOrDefault(item => item.Name == _databasetype).DBType; + var sqlquery = new SqlQuery { DBConnectionString = _connection, DBType = dbtype, Query = _sql }; sqlquery = await SqlService.ExecuteQueryAsync(sqlquery); _results = sqlquery.Results; AddModuleMessage(Localizer["Success.QueryExecuted"], MessageType.Success); diff --git a/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor b/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor index 239d3e9b..c27f9282 100644 --- a/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor +++ b/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor @@ -190,7 +190,7 @@ { _version = Constants.Version; - Dictionary systeminfo = await SystemService.GetSystemInfoAsync("environment"); + var systeminfo = await SystemService.GetSystemInfoAsync("environment"); if (systeminfo != null) { _clrversion = systeminfo["CLRVersion"].ToString(); @@ -247,7 +247,9 @@ { try { - await SystemService.UpdateSystemInfoAsync("Log", "Clear"); + var settings = new Dictionary(); + settings.Add("clearlog", "true"); + await SystemService.UpdateSystemInfoAsync(settings); _log = string.Empty; AddModuleMessage(Localizer["Success.ClearLog"], MessageType.Success); } diff --git a/Oqtane.Client/Resources/Installer/Installer.resx b/Oqtane.Client/Resources/Installer/Installer.resx index 4d9afff5..ec83b9bb 100644 --- a/Oqtane.Client/Resources/Installer/Installer.resx +++ b/Oqtane.Client/Resources/Installer/Installer.resx @@ -172,7 +172,7 @@ Enter a complete connection string including all parameters and delimiters - String: + Settings: Enter Connection Parameters diff --git a/Oqtane.Client/Resources/Modules/Admin/Site/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Site/Index.resx index e74d0343..00708582 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Site/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Site/Index.resx @@ -163,7 +163,7 @@ Enter the site name - Enter the tenant for the site + The name of the database used for the site The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). @@ -214,7 +214,7 @@ Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension. - Tenant: + Database: Aliases: @@ -292,7 +292,7 @@ Browse - Tenant Information + Database PWA Settings @@ -304,13 +304,13 @@ Connection: - Database: + Type: The connection information for the database - The database for the tenant + The type of database Delete Site diff --git a/Oqtane.Client/Resources/Modules/Admin/Sites/Add.resx b/Oqtane.Client/Resources/Modules/Admin/Sites/Add.resx index 79980b5d..f42efc99 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Sites/Add.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Sites/Add.resx @@ -123,9 +123,6 @@ SQL Server - - Server: - Select Container @@ -145,7 +142,7 @@ Select the default container for the site - Tenant: + Database: Aliases: @@ -157,10 +154,10 @@ Select Site Template - Select Tenant + Select Database - Create New Tenant + Create Database Error Loading Containers For Theme @@ -172,19 +169,19 @@ Invalid Host Password - Tenant Name Is Missing Or Already Exists + Database Name Is Missing Or Already Exists {0} Already Used For Another Site - You Must Provide A Tenant, Site Name, Alias, Default Theme/Container, And Site Template + You Must Provide A Database, Site Name, Alias, Default Theme/Container, And Site Template Enter the name of the site - Select the default theme for the website + Select the default theme for the site Select the admin container for the site @@ -193,28 +190,13 @@ Select the site template - Select the tenant for the site + Select the database for the site - Enter the name for the tenant + Enter the name for the database - Select the database type for the tenant - - - Enter the server for the tenant - - - Enter the database for the tenant - - - Select if you want integrated security or not - - - Enter the username for the integrated security - - - Enter the password for the integrated security + Select the database type Enter the username of an existing host user @@ -232,23 +214,14 @@ Site Template: - Tenant Name: + Name: - Database Type: + Type: Database: - - Integrated Security: - - - Database Username: - - - Database Password: - Host Username: @@ -274,7 +247,7 @@ Enter a complete connection string including all parameters and delimiters - String: + Settings: Enter Connection Parameters diff --git a/Oqtane.Client/Resources/Modules/Admin/Sql/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Sql/Index.resx index ab2f651f..4cc9e688 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Sql/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Sql/Index.resx @@ -117,30 +117,75 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Connection: + + + Select a database connection (from appsettings.json) + + + Select Connection + + + Add Connection + + + Name: + + + Enter the name of the connection + + + Type: + + + Select the database type + + + Select Type + + + Enter Connection Parameters + + + Enter Connection String + + + Settings: + + + A complete connection string including all parameters and delimiters + + + Add + - Tenant: - - - Select Tenant - - - Execute - - - You Must Select A Tenant And Provide A Valid SQL Query - - - No Results Returned + Database: - Select the tenant associated with the database server - - - Enter the SQL query for the database server + The database using this connection SQL Query: + + Enter a valid SQL query for the database + + + Execute + + + You Must Select A Database Type And Provide A Valid SQL Query + + + You Must Provide A Connection Name And Settings + + + Connection Added Successfully + + + No Results Returned + SQL Query Executed diff --git a/Oqtane.Client/Services/Interfaces/ISystemService.cs b/Oqtane.Client/Services/Interfaces/ISystemService.cs index 8648cc4a..4ecc757c 100644 --- a/Oqtane.Client/Services/Interfaces/ISystemService.cs +++ b/Oqtane.Client/Services/Interfaces/ISystemService.cs @@ -32,11 +32,5 @@ namespace Oqtane.Services /// /// Task UpdateSystemInfoAsync(Dictionary settings); - - /// - /// updates a config value - /// - /// - Task UpdateSystemInfoAsync(string settingKey, object settingValue); } } diff --git a/Oqtane.Client/Services/SystemService.cs b/Oqtane.Client/Services/SystemService.cs index 41605da2..1d632dc5 100644 --- a/Oqtane.Client/Services/SystemService.cs +++ b/Oqtane.Client/Services/SystemService.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using System.Collections.Generic; using Oqtane.Documentation; using Oqtane.Shared; +using System.Net; namespace Oqtane.Services { @@ -32,9 +33,5 @@ namespace Oqtane.Services { await PostJsonAsync(Apiurl, settings); } - public async Task UpdateSystemInfoAsync(string settingKey, object settingValue) - { - await PutJsonAsync($"{Apiurl}/{settingKey}/{settingValue}", ""); - } } } diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index 17398958..25976fc9 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -52,7 +52,7 @@ namespace Oqtane.Controllers { var installation = new Installation { Success = false, Message = "" }; - if (ModelState.IsValid && (User.IsInRole(RoleNames.Host) || string.IsNullOrEmpty(_configManager.GetSetting("ConnectionStrings:" + SettingKeys.ConnectionStringKey, "")))) + if (ModelState.IsValid && (User.IsInRole(RoleNames.Host) || string.IsNullOrEmpty(_configManager.GetSetting($"{SettingKeys.ConnectionStringsSection}:{SettingKeys.ConnectionStringKey}", "")))) { installation = _databaseManager.Install(config); diff --git a/Oqtane.Server/Controllers/SqlController.cs b/Oqtane.Server/Controllers/SqlController.cs index d755ed84..97ee4798 100644 --- a/Oqtane.Server/Controllers/SqlController.cs +++ b/Oqtane.Server/Controllers/SqlController.cs @@ -32,13 +32,23 @@ namespace Oqtane.Controllers { var results = new List>(); Dictionary row; - Tenant tenant = _tenants.GetTenant(sqlquery.TenantId); + + if (string.IsNullOrEmpty(sqlquery.DBType) || string.IsNullOrEmpty(sqlquery.DBConnectionString)) + { + Tenant tenant = _tenants.GetTenant(sqlquery.TenantId); + if (tenant != null) + { + sqlquery.DBType = tenant.DBType; + sqlquery.DBConnectionString = tenant.DBConnectionString; + } + } + try { foreach (string query in sqlquery.Query.Split("GO", StringSplitOptions.RemoveEmptyEntries)) { - IDataReader dr = _sql.ExecuteReader(tenant, query); - _logger.Log(LogLevel.Information, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId}", query, sqlquery.TenantId); + IDataReader dr = _sql.ExecuteReader(sqlquery.DBType, sqlquery.DBConnectionString, query); + _logger.Log(LogLevel.Information, this, LogFunction.Other, "Sql Query {Query} Executed on Database {DBType} and Connection {DBConnectionString}", query, sqlquery.DBType, sqlquery.DBConnectionString); while (dr.Read()) { row = new Dictionary(); @@ -53,7 +63,7 @@ namespace Oqtane.Controllers catch (Exception ex) { results.Add(new Dictionary() { { "Error", ex.Message } }); - _logger.Log(LogLevel.Warning, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId} Resulted In An Error {Error}", sqlquery.Query, sqlquery.TenantId, ex.Message); + _logger.Log(LogLevel.Warning, this, LogFunction.Other, "Sql Query {Query} Executed on Database {DBType} and Connection {DBConnectionString} Resulted In An Error {Error}", sqlquery.Query, sqlquery.DBType, sqlquery.DBConnectionString, ex.Message); } sqlquery.Results = results; return sqlquery; diff --git a/Oqtane.Server/Controllers/SystemController.cs b/Oqtane.Server/Controllers/SystemController.cs index e6fb098c..ca95428a 100644 --- a/Oqtane.Server/Controllers/SystemController.cs +++ b/Oqtane.Server/Controllers/SystemController.cs @@ -63,6 +63,12 @@ namespace Oqtane.Controllers } systeminfo.Add("Log", log); break; + case "connectionstrings": + foreach (var kvp in _configManager.GetSettings(SettingKeys.ConnectionStringsSection)) + { + systeminfo.Add(kvp.Key, kvp.Value); + } + break; } return systeminfo; @@ -88,19 +94,11 @@ namespace Oqtane.Controllers } } - // PUT: api/ - [HttpPut("{key}/{value}")] - [Authorize(Roles = RoleNames.Host)] - public void Put(string key, object value) - { - UpdateSetting(key, value); - } - private void UpdateSetting(string key, object value) { - switch (key) + switch (key.ToLower()) { - case "Log": + case "clearlog": string path = Path.Combine(_environment.ContentRootPath, "Content", "Log", "error.log"); if (System.IO.File.Exists(path)) { diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index 719463ae..2bb65c38 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -72,6 +72,7 @@ namespace Microsoft.Extensions.DependencyInjection internal static IServiceCollection AddOqtaneTransientServices(this IServiceCollection services) { + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/Oqtane.Server/Infrastructure/ConfigManager.cs b/Oqtane.Server/Infrastructure/ConfigManager.cs index 85278929..8b96569d 100644 --- a/Oqtane.Server/Infrastructure/ConfigManager.cs +++ b/Oqtane.Server/Infrastructure/ConfigManager.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; using Microsoft.Extensions.Configuration; @@ -42,6 +44,16 @@ namespace Oqtane.Infrastructure return value; } + public Dictionary GetSettings(string sectionKey) + { + var settings = new Dictionary(); + foreach (var kvp in _config.GetSection(sectionKey).GetChildren().AsEnumerable()) + { + settings.Add(kvp.Key, kvp.Value); + } + return settings; + } + public void AddOrUpdateSetting(string key, T value, bool reload) { AddOrUpdateSetting("appsettings.json", key, value, reload); diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index ed4a730c..69776d0c 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -162,6 +162,16 @@ namespace Oqtane.Infrastructure { install.DefaultContainer = GetInstallationConfig(SettingKeys.DefaultContainerKey, Constants.DefaultContainer); } + + // add new site + if (install.TenantName != TenantNames.Master && install.ConnectionString.Contains("=")) + { + _configManager.AddOrUpdateSetting($"{SettingKeys.ConnectionStringsSection}:{install.TenantName}", install.ConnectionString, false); + } + if (install.TenantName == TenantNames.Master && !install.ConnectionString.Contains("=")) + { + install.ConnectionString = _config.GetConnectionString(install.ConnectionString); + } } else { @@ -273,7 +283,7 @@ namespace Oqtane.Infrastructure var database = Activator.CreateInstance(type) as IDatabase; // create data directory if does not exist - var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); + var dataDirectory = AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString(); if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory ?? String.Empty); var dbOptions = new DbContextOptionsBuilder().UseOqtaneDatabase(database, NormalizeConnectionString(install.ConnectionString)).Options; @@ -316,10 +326,7 @@ namespace Oqtane.Infrastructure using (var masterDbContext = new MasterDBContext(new DbContextOptions(), null, _config)) { - if (installation.Success && (install.DatabaseType == Constants.DefaultDBType)) - { - UpgradeSqlServer(sql, install.ConnectionString, install.DatabaseType, true); - } + AddEFMigrationsHistory(sql, install.ConnectionString, install.DatabaseType, "", true); // push latest model into database masterDbContext.Database.Migrate(); result.Success = true; @@ -354,7 +361,7 @@ namespace Oqtane.Infrastructure tenant = new Tenant { Name = install.TenantName, - DBConnectionString = DenormalizeConnectionString(install.ConnectionString), + DBConnectionString = (install.TenantName == TenantNames.Master) ? SettingKeys.ConnectionStringKey : install.TenantName, DBType = install.DatabaseType, CreatedBy = "", CreatedOn = DateTime.UtcNow, @@ -413,21 +420,19 @@ namespace Oqtane.Infrastructure var upgrades = scope.ServiceProvider.GetRequiredService(); var sql = scope.ServiceProvider.GetRequiredService(); var tenantManager = scope.ServiceProvider.GetRequiredService(); + var DBContextDependencies = scope.ServiceProvider.GetRequiredService(); using (var db = GetInstallationContext()) { foreach (var tenant in db.Tenant.ToList()) { tenantManager.SetTenant(tenant.TenantId); + tenant.DBConnectionString = MigrateConnectionString(db, tenant); try { - using (var tenantDbContext = new TenantDBContext(tenantManager, null)) + using (var tenantDbContext = new TenantDBContext(DBContextDependencies)) { - if (install.DatabaseType == Constants.DefaultDBType) - { - UpgradeSqlServer(sql, tenant.DBConnectionString, tenant.DBType, false); - } - + AddEFMigrationsHistory(sql, _configManager.GetSetting($"{SettingKeys.ConnectionStringsSection}:{tenant.DBConnectionString}", ""), tenant.DBType, tenant.Version, false); // push latest model into database tenantDbContext.Database.Migrate(); result.Success = true; @@ -753,8 +758,8 @@ namespace Oqtane.Infrastructure private string DenormalizeConnectionString(string connectionString) { - var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); - connectionString = connectionString.Replace(dataDirectory ?? String.Empty, "|DataDirectory|"); + var dataDirectory = AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString(); + connectionString = connectionString.Replace(dataDirectory ?? String.Empty, $"|{Constants.DataDirectory}|"); return connectionString; } @@ -780,8 +785,8 @@ namespace Oqtane.Infrastructure private string NormalizeConnectionString(string connectionString) { - var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); - connectionString = connectionString.Replace("|DataDirectory|", dataDirectory); + var dataDirectory = AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString(); + connectionString = connectionString.Replace($"|{Constants.DataDirectory}|", dataDirectory); return connectionString; } @@ -799,14 +804,39 @@ namespace Oqtane.Infrastructure _configManager.AddOrUpdateSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", databaseType, true); } - public void UpgradeSqlServer(ISqlRepository sql, string connectionString, string databaseType, bool isMaster) + public void AddEFMigrationsHistory(ISqlRepository sql, string connectionString, string databaseType, string version, bool isMaster) { - var script = (isMaster) ? "MigrateMaster.sql" : "MigrateTenant.sql"; + // in version 2.1.0 the __EFMigrationsHistory tables were introduced and must be added to existing SQL Server installations + if ((isMaster || (version != null && Version.Parse(version).CompareTo(Version.Parse("2.1.0")) < 0)) && databaseType == Constants.DefaultDBType) + { + var script = (isMaster) ? "MigrateMaster.sql" : "MigrateTenant.sql"; - var query = sql.GetScriptFromAssembly(Assembly.GetExecutingAssembly(), script); - query = query.Replace("{{Version}}", Constants.Version); + var query = sql.GetScriptFromAssembly(Assembly.GetExecutingAssembly(), script); + query = query.Replace("{{Version}}", Constants.Version); - sql.ExecuteNonQuery(connectionString, databaseType, query); + sql.ExecuteNonQuery(connectionString, databaseType, query); + } + } + + public string MigrateConnectionString(InstallationContext db, Tenant tenant) + { + // migrate connection strings from the Tenant table to appsettings + if (tenant.DBConnectionString.Contains("=")) + { + var defaultConnection = _configManager.GetConnectionString(SettingKeys.ConnectionStringKey); + if (tenant.DBConnectionString == defaultConnection) + { + tenant.DBConnectionString = SettingKeys.ConnectionStringKey; + } + else + { + _configManager.AddOrUpdateSetting($"{SettingKeys.ConnectionStringsSection}:{tenant.Name}", tenant.DBConnectionString, false); + tenant.DBConnectionString = tenant.Name; + } + db.Entry(tenant).State = EntityState.Modified; + db.SaveChanges(); + } + return tenant.DBConnectionString; } private void ValidateConfiguration() diff --git a/Oqtane.Server/Infrastructure/Interfaces/IConfigManager.cs b/Oqtane.Server/Infrastructure/Interfaces/IConfigManager.cs index 5bff86b9..2f5ba875 100644 --- a/Oqtane.Server/Infrastructure/Interfaces/IConfigManager.cs +++ b/Oqtane.Server/Infrastructure/Interfaces/IConfigManager.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Microsoft.Extensions.Configuration; namespace Oqtane.Infrastructure @@ -7,6 +8,7 @@ namespace Oqtane.Infrastructure public IConfigurationSection GetSection(string sectionKey); public T GetSetting(string settingKey, T defaultValue); public T GetSetting(string sectionKey, string settingKey, T defaultValue); + public Dictionary GetSettings(string sectionKey); void AddOrUpdateSetting(string key, T value, bool reload); void AddOrUpdateSetting(string file, string key, T value, bool reload); void RemoveSetting(string key, bool reload); diff --git a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs index 6179c40c..959f4d07 100644 --- a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs +++ b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs @@ -2,7 +2,6 @@ using Oqtane.Infrastructure; using Oqtane.Models; using Oqtane.Modules.HtmlText.Repository; using System.Net; -using Microsoft.AspNetCore.Http; using Oqtane.Enums; using Oqtane.Repository; using Oqtane.Shared; @@ -17,15 +16,13 @@ namespace Oqtane.Modules.HtmlText.Manager public class HtmlTextManager : MigratableModuleBase, IInstallable, IPortable { private readonly IHtmlTextRepository _htmlText; - private readonly ITenantManager _tenantManager; - private readonly IHttpContextAccessor _accessor; + private readonly IDBContextDependencies _DBContextDependencies; private readonly ISqlRepository _sqlRepository; - public HtmlTextManager(IHtmlTextRepository htmlText, ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor, ISqlRepository sqlRepository) + public HtmlTextManager(IHtmlTextRepository htmlText, IDBContextDependencies DBContextDependencies, ISqlRepository sqlRepository) { _htmlText = htmlText; - _tenantManager = tenantManager; - _accessor = httpContextAccessor; + _DBContextDependencies = DBContextDependencies; _sqlRepository = sqlRepository; } @@ -56,12 +53,12 @@ namespace Oqtane.Modules.HtmlText.Manager // version 1.0.0 used SQL scripts rather than migrations, so we need to seed the migration history table _sqlRepository.ExecuteNonQuery(tenant, MigrationUtils.BuildInsertScript("HtmlText.01.00.00.00")); } - return Migrate(new HtmlTextContext(_tenantManager, _accessor), tenant, MigrationType.Up); + return Migrate(new HtmlTextContext(_DBContextDependencies), tenant, MigrationType.Up); } public bool Uninstall(Tenant tenant) { - return Migrate(new HtmlTextContext(_tenantManager, _accessor), tenant, MigrationType.Down); + return Migrate(new HtmlTextContext(_DBContextDependencies), tenant, MigrationType.Down); } } } diff --git a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs index 97f95772..9f7e93de 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs @@ -13,7 +13,7 @@ namespace Oqtane.Modules.HtmlText.Repository [PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")] public class HtmlTextContext : DBContextBase, ITransientService, IMultiDatabase { - public HtmlTextContext(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor) : base(tenantManager, httpContextAccessor) { } + public HtmlTextContext(IDBContextDependencies DBContextDependencies) : base(DBContextDependencies) { } public virtual DbSet HtmlText { get; set; } } diff --git a/Oqtane.Server/Repository/Context/DBContextBase.cs b/Oqtane.Server/Repository/Context/DBContextBase.cs index 6d876d47..d1ded765 100644 --- a/Oqtane.Server/Repository/Context/DBContextBase.cs +++ b/Oqtane.Server/Repository/Context/DBContextBase.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -6,11 +7,13 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.Extensions.Configuration; using Oqtane.Databases.Interfaces; using Oqtane.Extensions; using Oqtane.Infrastructure; using Oqtane.Migrations.Framework; using Oqtane.Models; +using Oqtane.Shared; // ReSharper disable BuiltInTypeReferenceStyleForMemberAccess @@ -18,17 +21,17 @@ namespace Oqtane.Repository { public class DBContextBase : IdentityUserContext { - private readonly ITenantResolver _tenantResolver; private readonly ITenantManager _tenantManager; private readonly IHttpContextAccessor _accessor; - private string _connectionString; - private string _databaseType; + private readonly IConfigurationRoot _config; + private string _connectionString = ""; + private string _databaseType = ""; - public DBContextBase(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor) + public DBContextBase(IDBContextDependencies DBContextDependencies) { - _connectionString = String.Empty; - _tenantManager = tenantManager; - _accessor = httpContextAccessor; + _tenantManager = DBContextDependencies.TenantManager; + _accessor = DBContextDependencies.Accessor; + _config = DBContextDependencies.Config; } public IDatabase ActiveDatabase { get; set; } @@ -39,21 +42,11 @@ namespace Oqtane.Repository if (string.IsNullOrEmpty(_connectionString)) { - - Tenant tenant; - if (_tenantResolver != null) - { - tenant = _tenantResolver.GetTenant(); - } - else - { - tenant = _tenantManager.GetTenant(); - } - + Tenant tenant = _tenantManager.GetTenant(); if (tenant != null) { - _connectionString = tenant.DBConnectionString - .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); + _connectionString = _config.GetConnectionString(tenant.DBConnectionString) + .Replace($"|{Constants.DataDirectory}|", AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString()); _databaseType = tenant.DBType; } } @@ -93,12 +86,17 @@ namespace Oqtane.Repository return base.SaveChangesAsync(cancellationToken); } - [Obsolete("This constructor is obsolete. Use DBContextBase(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor) instead.", false)] - public DBContextBase(ITenantResolver tenantResolver, IHttpContextAccessor httpContextAccessor) + [Obsolete("This constructor is obsolete. Use DBContextBase(IDBContextDependencies DBContextDependencies) instead.", false)] + public DBContextBase(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor) { - _connectionString = String.Empty; - _tenantResolver = tenantResolver; + _tenantManager = tenantManager; _accessor = httpContextAccessor; + + // anti-pattern used to reference config service in base class without causing breaking change + _config = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .Build(); } } } diff --git a/Oqtane.Server/Repository/Context/DBContextDependencies.cs b/Oqtane.Server/Repository/Context/DBContextDependencies.cs new file mode 100644 index 00000000..f48d4fcd --- /dev/null +++ b/Oqtane.Server/Repository/Context/DBContextDependencies.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Oqtane.Infrastructure; + +namespace Oqtane.Repository +{ + public class DBContextDependencies : IDBContextDependencies + { + public DBContextDependencies(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor, IConfigurationRoot config) + { + TenantManager = tenantManager; + Accessor = httpContextAccessor; + Config = config; + } + + public ITenantManager TenantManager { get; } + public IHttpContextAccessor Accessor { get; } + public IConfigurationRoot Config { get; } + } +} diff --git a/Oqtane.Server/Repository/Context/InstallationContext.cs b/Oqtane.Server/Repository/Context/InstallationContext.cs index 20644def..ad6912c3 100644 --- a/Oqtane.Server/Repository/Context/InstallationContext.cs +++ b/Oqtane.Server/Repository/Context/InstallationContext.cs @@ -1,8 +1,5 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; -using Oqtane.Databases.Interfaces; +using Microsoft.EntityFrameworkCore; using Oqtane.Extensions; -using Oqtane.Interfaces; using Oqtane.Models; using IDatabase = Oqtane.Databases.Interfaces.IDatabase; diff --git a/Oqtane.Server/Repository/Context/MasterDBContext.cs b/Oqtane.Server/Repository/Context/MasterDBContext.cs index d607749d..a50c8fe2 100644 --- a/Oqtane.Server/Repository/Context/MasterDBContext.cs +++ b/Oqtane.Server/Repository/Context/MasterDBContext.cs @@ -41,8 +41,8 @@ namespace Oqtane.Repository { if (_config.IsInstalled()) { - _connectionString = _config.GetConnectionString("DefaultConnection") - .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); + _connectionString = _config.GetConnectionString(SettingKeys.ConnectionStringKey) + .Replace($"|{Constants.DataDirectory}|", AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString()); } _databaseType = _config.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey]; diff --git a/Oqtane.Server/Repository/Context/TenantDBContext.cs b/Oqtane.Server/Repository/Context/TenantDBContext.cs index dc2c219b..2efad3f7 100644 --- a/Oqtane.Server/Repository/Context/TenantDBContext.cs +++ b/Oqtane.Server/Repository/Context/TenantDBContext.cs @@ -1,6 +1,4 @@ -using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; -using Oqtane.Infrastructure; using Oqtane.Models; using Oqtane.Repository.Databases.Interfaces; @@ -12,7 +10,7 @@ namespace Oqtane.Repository { public class TenantDBContext : DBContextBase, IMultiDatabase { - public TenantDBContext(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor) : base(tenantManager, httpContextAccessor) { } + public TenantDBContext(IDBContextDependencies DBContextDependencies) : base(DBContextDependencies) { } public virtual DbSet Site { get; set; } public virtual DbSet Page { get; set; } diff --git a/Oqtane.Server/Repository/Interfaces/IDBContextDependencies.cs b/Oqtane.Server/Repository/Interfaces/IDBContextDependencies.cs new file mode 100644 index 00000000..5e93f0e1 --- /dev/null +++ b/Oqtane.Server/Repository/Interfaces/IDBContextDependencies.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Oqtane.Infrastructure; + +namespace Oqtane.Repository +{ + public interface IDBContextDependencies + { + ITenantManager TenantManager { get; } + IHttpContextAccessor Accessor { get; } + IConfigurationRoot Config { get; } + } +} diff --git a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs index ed1b96b0..d4d87201 100644 --- a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs @@ -1,4 +1,4 @@ -using System.Data; +using System.Data; using System.Reflection; using Oqtane.Models; @@ -18,6 +18,8 @@ namespace Oqtane.Repository IDataReader ExecuteReader(Tenant tenant, string query); + IDataReader ExecuteReader(string DBType, string DBConnectionString, string query); + string GetScriptFromAssembly(Assembly assembly, string fileName); } } diff --git a/Oqtane.Server/Repository/SqlRepository.cs b/Oqtane.Server/Repository/SqlRepository.cs index 59cb2a46..31e0aa10 100644 --- a/Oqtane.Server/Repository/SqlRepository.cs +++ b/Oqtane.Server/Repository/SqlRepository.cs @@ -1,11 +1,10 @@ -using System; -using System.Collections.Generic; +using System; using System.Data; using System.IO; using System.Linq; using System.Reflection; +using Microsoft.Extensions.Configuration; using Oqtane.Databases.Interfaces; -using Oqtane.Interfaces; using Oqtane.Models; // ReSharper disable ConvertToUsingDeclaration // ReSharper disable InvertIf @@ -15,6 +14,13 @@ namespace Oqtane.Repository { public class SqlRepository : ISqlRepository { + private IConfigurationRoot _config; + + public SqlRepository(IConfigurationRoot config) + { + _config = config; + } + public void ExecuteScript(Tenant tenant, string script) { // execute script in current tenant @@ -75,13 +81,19 @@ namespace Oqtane.Repository public IDataReader ExecuteReader(Tenant tenant, string query) { var db = GetActiveDatabase(tenant.DBType); - return db.ExecuteReader(tenant.DBConnectionString, query); + return db.ExecuteReader(GetConnectionString(tenant.DBConnectionString), query); + } + + public IDataReader ExecuteReader(string DBType, string DBConnectionString, string query) + { + var db = GetActiveDatabase(DBType); + return db.ExecuteReader(GetConnectionString(DBConnectionString), query); } public int ExecuteNonQuery(string connectionString, string databaseType, string query) { var db = GetActiveDatabase(databaseType); - return db.ExecuteNonQuery(connectionString, query); + return db.ExecuteNonQuery(GetConnectionString(connectionString), query); } public string GetScriptFromAssembly(Assembly assembly, string fileName) @@ -119,5 +131,14 @@ namespace Oqtane.Repository return activeDatabase; } + + private string GetConnectionString(string connectionString) + { + if (!connectionString.Contains("=")) + { + connectionString = _config.GetConnectionString(connectionString); + } + return connectionString; + } } } diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index a8eb7e25..188f95e3 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -40,7 +40,7 @@ namespace Oqtane //add possibility to switch off swagger on production. _useSwagger = Configuration.GetSection("UseSwagger").Value != "false"; - AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(env.ContentRootPath, "Data")); + AppDomain.CurrentDomain.SetData(Constants.DataDirectory, Path.Combine(env.ContentRootPath, "Data")); _env = env; } diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/Manager/[Module]Manager.cs b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/Manager/[Module]Manager.cs index e9092589..a04db0cd 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/Manager/[Module]Manager.cs +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/Manager/[Module]Manager.cs @@ -12,25 +12,23 @@ namespace [Owner].[Module].Manager { public class [Module]Manager : MigratableModuleBase, IInstallable, IPortable { - private I[Module]Repository _[Module]Repository; - private readonly ITenantManager _tenantManager; - private readonly IHttpContextAccessor _accessor; + private readonly I[Module]Repository _[Module]Repository; + private readonly IDBContextDependencies _DBContextDependencies; - public [Module]Manager(I[Module]Repository [Module]Repository, ITenantManager tenantManager, IHttpContextAccessor accessor) + public [Module]Manager(I[Module]Repository [Module]Repository, IDBContextDependencies DBContextDependencies) { _[Module]Repository = [Module]Repository; - _tenantManager = tenantManager; - _accessor = accessor; + _DBContextDependencies = DBContextDependencies; } public bool Install(Tenant tenant, string version) { - return Migrate(new [Module]Context(_tenantManager, _accessor), tenant, MigrationType.Up); + return Migrate(new [Module]Context(_DBContextDependencies), tenant, MigrationType.Up); } public bool Uninstall(Tenant tenant) { - return Migrate(new [Module]Context(_tenantManager, _accessor), tenant, MigrationType.Down); + return Migrate(new [Module]Context(_DBContextDependencies), tenant, MigrationType.Down); } public string ExportModule(Module module) diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/Repository/[Module]Context.cs b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/Repository/[Module]Context.cs index 44e1336b..90103d5c 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/Repository/[Module]Context.cs +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/Repository/[Module]Context.cs @@ -11,7 +11,7 @@ namespace [Owner].[Module].Repository { public virtual DbSet [Module] { get; set; } - public [Module]Context(ITenantManager tenantManager, IHttpContextAccessor accessor) : base(tenantManager, accessor) + public [Module]Context(IDBContextDependencies DBContextDependencies) : base(DBContextDependencies) { } { // ContextBase handles multi-tenant database connections } diff --git a/Oqtane.Shared/Models/SqlQuery.cs b/Oqtane.Shared/Models/SqlQuery.cs index 9949d2ef..5b8f2b4c 100644 --- a/Oqtane.Shared/Models/SqlQuery.cs +++ b/Oqtane.Shared/Models/SqlQuery.cs @@ -8,7 +8,8 @@ namespace Oqtane.Models /// Reference to the this belongs to /// public int TenantId { get; set; } - + public string DBType { get; set; } + public string DBConnectionString { get; set; } public string Query { get; set; } public List> Results { get; set; } } diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 15e9af81..56bd1dbf 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -11,6 +11,7 @@ namespace Oqtane.Shared public const string UpdaterPackageId = "Oqtane.Updater"; public const string PackageRegistryUrl = "https://www.oqtane.net"; + public const string DataDirectory = "DataDirectory"; public const string DefaultDBType = "Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Database.SqlServer"; public const string PageComponent = "Oqtane.UI.ThemeBuilder, Oqtane.Client";