From cbcfc8849256c986e0e3017ff96702d9df7e8a8e Mon Sep 17 00:00:00 2001 From: Charles Nurse Date: Tue, 23 Mar 2021 11:06:18 -0700 Subject: [PATCH] Add support for Sqlite - Installation path tested but AddSite not supported yet --- .gitignore | 1 + Oqtane.Client/UI/Installer.razor | 53 ++++++++----- Oqtane.Server/Data/Oqtane.db | Bin 200704 -> 0 bytes .../DbContextOptionsBuilderExtensions.cs | 14 +++- .../Infrastructure/DatabaseManager.cs | 75 ++++++++++-------- .../Infrastructure/InstallationManager.cs | 1 + .../Infrastructure/Interfaces/IMigratable.cs | 10 --- .../Migrations/01000000_InitializeTenant.cs | 6 ++ .../Migrations/01000201_DropColumnFromPage.cs | 2 +- .../02010001_AddDatabaseTypeColumnToTenant.cs | 27 +++++++ .../EntityBuilders/PageEntityBuilder.cs | 3 - .../HtmlText/Manager/HtmlTextManager.cs | 43 +++------- Oqtane.Server/Modules/MigratableModuleBase.cs | 42 ++++++++++ .../Repository/Context/DBContextBase.cs | 38 ++++++--- Oqtane.Server/Repository/Context/DbConfig.cs | 2 + .../Repository/Context/InstallationContext.cs | 6 +- .../Repository/Context/MasterDBContext.cs | 5 +- .../Repository/Interfaces/IDbConfig.cs | 1 + Oqtane.Server/appsettings.json | 4 +- Oqtane.Shared/Models/Tenant.cs | 1 + Oqtane.Shared/Shared/InstallConfig.cs | 1 + Oqtane.Shared/Shared/SettingKeys.cs | 8 +- 22 files changed, 227 insertions(+), 116 deletions(-) delete mode 100644 Oqtane.Server/Data/Oqtane.db delete mode 100644 Oqtane.Server/Infrastructure/Interfaces/IMigratable.cs create mode 100644 Oqtane.Server/Migrations/02010001_AddDatabaseTypeColumnToTenant.cs create mode 100644 Oqtane.Server/Modules/MigratableModuleBase.cs diff --git a/.gitignore b/.gitignore index 98408e86..29b72c2a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ msbuild.binlog Oqtane.Server/appsettings.json Oqtane.Server/Data/*.mdf Oqtane.Server/Data/*.ldf +Oqtane.Server/Data/*.db /Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml Oqtane.Server/Content diff --git a/Oqtane.Client/UI/Installer.razor b/Oqtane.Client/UI/Installer.razor index 9231e092..5983c253 100644 --- a/Oqtane.Client/UI/Installer.razor +++ b/Oqtane.Client/UI/Installer.razor @@ -27,10 +27,19 @@ - + + + + + + + + + @@ -38,7 +47,7 @@ - + @@ -46,7 +55,7 @@ - + @@ -129,6 +138,7 @@ @code { private string _databaseType = "LocalDB"; private string _serverName = "(LocalDb)\\MSSQLLocalDB"; + private string _fileName = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm") + ".db"; private string _databaseName = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm"); private string _username = string.Empty; private string _password = string.Empty; @@ -138,6 +148,8 @@ private string _hostEmail = string.Empty; private string _message = string.Empty; private string _integratedSecurityDisplay = "display: none;"; + private string _fileFieldsDisplay = "display: none;"; + private string _serverFieldsDisplay = "display: none;"; private string _loadingDisplay = "display: none;"; protected override async Task OnAfterRenderAsync(bool firstRender) @@ -158,33 +170,38 @@ private async Task Install() { - if (_serverName != "" && _databaseName != "" && _hostUsername != "" && _hostPassword.Length >= 6 && _hostPassword == _confirmPassword && _hostEmail != "") + if (((_serverName != "" && _databaseName != "") || _fileName !="") && _hostUsername != "" && _hostPassword.Length >= 6 && _hostPassword == _confirmPassword && _hostEmail != "") { _loadingDisplay = ""; StateHasChanged(); var connectionstring = ""; - if (_databaseType == "LocalDB") + switch (_databaseType) { - connectionstring = "Data Source=" + _serverName + ";AttachDbFilename=|DataDirectory|\\" + _databaseName + ".mdf;Initial Catalog=" + _databaseName + ";Integrated Security=SSPI;"; - } - else - { - connectionstring = "Data Source=" + _serverName + ";Initial Catalog=" + _databaseName + ";"; - if (_integratedSecurityDisplay == "display: none;") - { - connectionstring += "Integrated Security=SSPI;"; - } - else - { - connectionstring += "User ID=" + _username + ";Password=" + _password; - } + case "LocalDB": + connectionstring = "Data Source=" + _serverName + ";AttachDbFilename=|DataDirectory|\\" + _databaseName + ".mdf;Initial Catalog=" + _databaseName + ";Integrated Security=SSPI;"; + break; + case "SQLServer": + connectionstring = "Data Source=" + _serverName + ";Initial Catalog=" + _databaseName + ";"; + if (_integratedSecurityDisplay == "display: none;") + { + connectionstring += "Integrated Security=SSPI;"; + } + else + { + connectionstring += "User ID=" + _username + ";Password=" + _password; + } + break; + case "Sqlite": + connectionstring = "Data Source=" + _fileName; + break; } Uri uri = new Uri(NavigationManager.Uri); var config = new InstallConfig { + DatabaseType = _databaseType, ConnectionString = connectionstring, Aliases = uri.Authority, HostEmail = _hostEmail, diff --git a/Oqtane.Server/Data/Oqtane.db b/Oqtane.Server/Data/Oqtane.db deleted file mode 100644 index d02cc5a4e260a659fdcdeb39c8340186f72a2eab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 200704 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, dbConfig)) { @@ -216,6 +217,7 @@ namespace Oqtane.Infrastructure if (result.Success) { UpdateConnectionString(install.ConnectionString); + UpdateDatabaseType(install.DatabaseType); } } else @@ -232,12 +234,18 @@ namespace Oqtane.Infrastructure if (!string.IsNullOrEmpty(install.TenantName) && !string.IsNullOrEmpty(install.Aliases)) { - using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))) + using (var db = new InstallationContext(install.DatabaseType, NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))) { Tenant tenant; if (install.IsNewTenant) { - tenant = new Tenant { Name = install.TenantName, DBConnectionString = DenormalizeConnectionString(install.ConnectionString), CreatedBy = "", CreatedOn = DateTime.UtcNow, ModifiedBy = "", ModifiedOn = DateTime.UtcNow }; + tenant = new Tenant { Name = install.TenantName, + DBConnectionString = DenormalizeConnectionString(install.ConnectionString), + DBType = install.DatabaseType, + CreatedBy = "", + CreatedOn = DateTime.UtcNow, + ModifiedBy = "", + ModifiedOn = DateTime.UtcNow }; db.Tenant.Add(tenant); db.SaveChanges(); _cache.Remove("tenants"); @@ -276,13 +284,15 @@ namespace Oqtane.Infrastructure { var upgrades = scope.ServiceProvider.GetRequiredService(); - using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))) + var databaseType = _config.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey]; + var connectionString = NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)); + using (var db = new InstallationContext(databaseType, connectionString)) { foreach (var tenant in db.Tenant.ToList()) { try { - var dbConfig = new DbConfig(null, null) {ConnectionString = install.ConnectionString}; + var dbConfig = new DbConfig(null, null) {ConnectionString = install.ConnectionString, DatabaseType = install.DatabaseType}; using (var tenantDbContext = new TenantDBContext(dbConfig, null)) { // Push latest model into database @@ -337,44 +347,37 @@ namespace Oqtane.Infrastructure if (moduleType != null) { var versions = moduleDefinition.ReleaseVersions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))) - { + var databaseType = _config.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey]; + var connectionString = NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)); + using (var db = new InstallationContext(databaseType, connectionString)) { foreach (var tenant in db.Tenant.ToList()) { - if (moduleType.GetInterface("IMigratable") != null) + var index = Array.FindIndex(versions, item => item == moduleDefinition.Version); + if (tenant.Name == install.TenantName && install.TenantName != TenantNames.Master) { - var moduleObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduleType) as IMigratable; - moduleObject.Migrate(tenant, MigrationType.Up); + index = -1; } - else + if (index != (versions.Length - 1)) { - var index = Array.FindIndex(versions, item => item == moduleDefinition.Version); - if (tenant.Name == install.TenantName && install.TenantName != TenantNames.Master) + if (index == -1) index = 0; + for (var i = index; i < versions.Length; i++) { - index = -1; - } - if (index != (versions.Length - 1)) - { - if (index == -1) index = 0; - for (var i = index; i < versions.Length; i++) + try { - try + if (moduleType.GetInterface("IInstallable") != null) { - if (moduleType.GetInterface("IInstallable") != null) - { - var moduleObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduleType); - ((IInstallable)moduleObject).Install(tenant, versions[i]); - } - else - { - sql.ExecuteScript(tenant, moduleType.Assembly, Utilities.GetTypeName(moduleDefinition.ModuleDefinitionName) + "." + versions[i] + ".sql"); - } + var moduleObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduleType) as IInstallable; + moduleObject?.Install(tenant, versions[i]); } - catch (Exception ex) + else { - result.Message = "An Error Occurred Installing " + moduleDefinition.Name + " Version " + versions[i] + " - " + ex.Message; + sql.ExecuteScript(tenant, moduleType.Assembly, Utilities.GetTypeName(moduleDefinition.ModuleDefinitionName) + "." + versions[i] + ".sql"); } } + catch (Exception ex) + { + result.Message = "An Error Occurred Installing " + moduleDefinition.Name + " Version " + versions[i] + " - " + ex.Message; + } } } } @@ -530,11 +533,17 @@ namespace Oqtane.Infrastructure connectionString = DenormalizeConnectionString(connectionString); if (_config.GetConnectionString(SettingKeys.ConnectionStringKey) != connectionString) { - AddOrUpdateAppSetting($"ConnectionStrings:{SettingKeys.ConnectionStringKey}", connectionString); + AddOrUpdateAppSetting($"{SettingKeys.ConnectionStringsSection}:{SettingKeys.ConnectionStringKey}", connectionString); _config.Reload(); } } + public void UpdateDatabaseType(string databaseType) + { + AddOrUpdateAppSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", databaseType); + _config.Reload(); + } + public void AddOrUpdateAppSetting(string sectionPathKey, T value) { try diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index 3b627c70..3962baf5 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Hosting; using Oqtane.Shared; +// ReSharper disable AssignNullToNotNullAttribute namespace Oqtane.Infrastructure { diff --git a/Oqtane.Server/Infrastructure/Interfaces/IMigratable.cs b/Oqtane.Server/Infrastructure/Interfaces/IMigratable.cs deleted file mode 100644 index 009268fc..00000000 --- a/Oqtane.Server/Infrastructure/Interfaces/IMigratable.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Oqtane.Enums; -using Oqtane.Models; - -namespace Oqtane.Infrastructure -{ - public interface IMigratable - { - bool Migrate(Tenant tenant, MigrationType migrationType); - } -} diff --git a/Oqtane.Server/Migrations/01000000_InitializeTenant.cs b/Oqtane.Server/Migrations/01000000_InitializeTenant.cs index d977e508..fe1b8b1e 100644 --- a/Oqtane.Server/Migrations/01000000_InitializeTenant.cs +++ b/Oqtane.Server/Migrations/01000000_InitializeTenant.cs @@ -21,6 +21,12 @@ namespace Oqtane.Migrations pageEntityBuilder.Create(); pageEntityBuilder.AddIndex("IX_Page", new [] {"SiteId", "Path", "UserId"}, true); + //Add Column to Page table (for Sql Server only) we will drop it later for Sql Server only + if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.SqlServer") + { + pageEntityBuilder.AddBooleanColumn("EditMode"); + } + //Create Module table var moduleEntityBuilder = new ModuleEntityBuilder(migrationBuilder); moduleEntityBuilder.Create(); diff --git a/Oqtane.Server/Migrations/01000201_DropColumnFromPage.cs b/Oqtane.Server/Migrations/01000201_DropColumnFromPage.cs index 516aae4d..9ee79ff7 100644 --- a/Oqtane.Server/Migrations/01000201_DropColumnFromPage.cs +++ b/Oqtane.Server/Migrations/01000201_DropColumnFromPage.cs @@ -12,7 +12,7 @@ namespace Oqtane.Migrations protected override void Up(MigrationBuilder migrationBuilder) { //Drop Column from Page table - if (migrationBuilder.ActiveProvider != "Microsoft.EntityFrameworkCore.Sqlite") + if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.SqlServer") { var pageEntityBuilder = new PageEntityBuilder(migrationBuilder); pageEntityBuilder.DropColumn("EditMode"); diff --git a/Oqtane.Server/Migrations/02010001_AddDatabaseTypeColumnToTenant.cs b/Oqtane.Server/Migrations/02010001_AddDatabaseTypeColumnToTenant.cs new file mode 100644 index 00000000..bfea92bd --- /dev/null +++ b/Oqtane.Server/Migrations/02010001_AddDatabaseTypeColumnToTenant.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Migrations.EntityBuilders; +using Oqtane.Repository; + +namespace Oqtane.Migrations +{ + [DbContext(typeof(MasterDBContext))] + [Migration("Master.02.01.00.01")] + public class AddDatabaseTypeColumnToTenant : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + //Add Column to Site table + var tenantEntityBuilder = new TenantEntityBuilder(migrationBuilder); + tenantEntityBuilder.AddStringColumn("DBType", 200, true); + + //Update new column + migrationBuilder.Sql( + @" + UPDATE Tenant + SET DBType = 'SqlServer' + "); + + } + } +} diff --git a/Oqtane.Server/Migrations/EntityBuilders/PageEntityBuilder.cs b/Oqtane.Server/Migrations/EntityBuilders/PageEntityBuilder.cs index 49c75ba0..c259b945 100644 --- a/Oqtane.Server/Migrations/EntityBuilders/PageEntityBuilder.cs +++ b/Oqtane.Server/Migrations/EntityBuilders/PageEntityBuilder.cs @@ -35,7 +35,6 @@ namespace Oqtane.Migrations.EntityBuilders IsNavigation = table.AddBooleanColumn("IsNavigation"); Url = table.AddStringColumn("Url", 500, true); LayoutType = table.AddStringColumn("LayoutType", 200); - EditMode = table.AddBooleanColumn("EditMode"); UserId = table.AddIntegerColumn("UserId", true); IsPersonalizable = table.AddBooleanColumn("IsPersonalizable"); DefaultContainerType = table.AddStringColumn("DefaultContainerType", 200, true); @@ -69,8 +68,6 @@ namespace Oqtane.Migrations.EntityBuilders public OperationBuilder LayoutType { get; private set; } - public OperationBuilder EditMode { get; private set; } - public OperationBuilder UserId { get; private set; } public OperationBuilder IsPersonalizable { get; private set; } diff --git a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs index d8afa1b2..4329a4c7 100644 --- a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs +++ b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs @@ -12,7 +12,7 @@ using Oqtane.Enums; namespace Oqtane.Modules.HtmlText.Manager { - public class HtmlTextManager : IMigratable, IPortable + public class HtmlTextManager : MigratableModuleBase, IInstallable, IPortable { private readonly IHtmlTextRepository _htmlText; private readonly ISqlRepository _sql; @@ -23,35 +23,6 @@ namespace Oqtane.Modules.HtmlText.Manager _sql = sql; } - public bool Migrate(Tenant tenant, MigrationType migrationType) - { - var result = true; - - var dbConfig = new DbConfig(null, null) {ConnectionString = tenant.DBConnectionString}; - using (var db = new HtmlTextContext(dbConfig, null)) - { - try - { - var migrator = db.GetService(); - if (migrationType == MigrationType.Down) - { - migrator.Migrate(Migration.InitialDatabase); - } - else - { - migrator.Migrate(); - } - } - catch (Exception e) - { - Console.WriteLine(e); - result = false; - } - - } - return result; - } - public string ExportModule(Module module) { string content = ""; @@ -80,5 +51,17 @@ namespace Oqtane.Modules.HtmlText.Manager _htmlText.AddHtmlText(htmlText); } } + + public bool Install(Tenant tenant, string version) + { + var dbConfig = new DbConfig(null, null) {ConnectionString = tenant.DBConnectionString, DatabaseType = tenant.DBType}; + return Migrate(new HtmlTextContext(dbConfig, null), tenant, MigrationType.Up); + } + + public bool Uninstall(Tenant tenant) + { + var dbConfig = new DbConfig(null, null) {ConnectionString = tenant.DBConnectionString, DatabaseType = tenant.DBType}; + return Migrate(new HtmlTextContext(dbConfig, null), tenant, MigrationType.Down); + } } } diff --git a/Oqtane.Server/Modules/MigratableModuleBase.cs b/Oqtane.Server/Modules/MigratableModuleBase.cs new file mode 100644 index 00000000..c282d324 --- /dev/null +++ b/Oqtane.Server/Modules/MigratableModuleBase.cs @@ -0,0 +1,42 @@ +using System; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Enums; +using Oqtane.Models; +using Oqtane.Modules.HtmlText.Repository; +using Oqtane.Repository; + +namespace Oqtane.Modules +{ + public class MigratableModuleBase + { + public bool Migrate(DBContextBase dbContext, Tenant tenant, MigrationType migrationType) + { + var result = true; + + using (dbContext) + { + try + { + var migrator = dbContext.GetService(); + if (migrationType == MigrationType.Down) + { + migrator.Migrate(Migration.InitialDatabase); + } + else + { + migrator.Migrate(); + } + } + catch (Exception e) + { + Console.WriteLine(e); + result = false; + } + + } + return result; + + } + } +} diff --git a/Oqtane.Server/Repository/Context/DBContextBase.cs b/Oqtane.Server/Repository/Context/DBContextBase.cs index 013675d0..550665da 100644 --- a/Oqtane.Server/Repository/Context/DBContextBase.cs +++ b/Oqtane.Server/Repository/Context/DBContextBase.cs @@ -7,48 +7,62 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Oqtane.Extensions; using Oqtane.Models; +using Oqtane.Shared; + // ReSharper disable BuiltInTypeReferenceStyleForMemberAccess namespace Oqtane.Repository { public class DBContextBase : IdentityUserContext { - private readonly IDbConfig _dbConfig; private readonly ITenantResolver _tenantResolver; + private readonly IHttpContextAccessor _accessor; + private readonly IConfiguration _configuration; + private string _connectionString; + private string _databaseType; + + public DBContextBase(ITenantResolver tenantResolver, IHttpContextAccessor httpContextAccessor) + { + _connectionString = String.Empty; + _tenantResolver = tenantResolver; + _accessor = httpContextAccessor; + } public DBContextBase(IDbConfig dbConfig, ITenantResolver tenantResolver) { - _dbConfig = dbConfig; + _accessor = dbConfig.Accessor; + _configuration = dbConfig.Configuration; + _connectionString = dbConfig.ConnectionString; + _databaseType = dbConfig.DatabaseType; _tenantResolver = tenantResolver; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - var connectionString = _dbConfig.ConnectionString; - - if (string.IsNullOrEmpty(connectionString) && _tenantResolver != null) + if (string.IsNullOrEmpty(_connectionString) && _tenantResolver != null) { var tenant = _tenantResolver.GetTenant(); - var configuration = _dbConfig.Configuration; if (tenant != null) { - connectionString = tenant.DBConnectionString + _connectionString = tenant.DBConnectionString .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); + _databaseType = tenant.DBType; } else { - if (!String.IsNullOrEmpty(configuration.GetConnectionString("DefaultConnection"))) + if (!String.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection"))) { - connectionString = configuration.GetConnectionString("DefaultConnection") + _connectionString = _configuration.GetConnectionString("DefaultConnection") .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); } + _databaseType = _configuration.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey]; } } - if (!string.IsNullOrEmpty(connectionString)) + if (!string.IsNullOrEmpty(_connectionString) && !string.IsNullOrEmpty(_databaseType)) { - optionsBuilder.UseOqtaneDatabase(connectionString); + optionsBuilder.UseOqtaneDatabase(_databaseType, _connectionString); } base.OnConfiguring(optionsBuilder); @@ -56,7 +70,7 @@ namespace Oqtane.Repository public override int SaveChanges() { - DbContextUtils.SaveChanges(this, _dbConfig.Accessor); + DbContextUtils.SaveChanges(this, _accessor); return base.SaveChanges(); } diff --git a/Oqtane.Server/Repository/Context/DbConfig.cs b/Oqtane.Server/Repository/Context/DbConfig.cs index 7bc5cb8a..7cc636ef 100644 --- a/Oqtane.Server/Repository/Context/DbConfig.cs +++ b/Oqtane.Server/Repository/Context/DbConfig.cs @@ -16,5 +16,7 @@ namespace Oqtane.Repository public IConfiguration Configuration { get; } public string ConnectionString { get; set; } + + public string DatabaseType { get; set; } } } diff --git a/Oqtane.Server/Repository/Context/InstallationContext.cs b/Oqtane.Server/Repository/Context/InstallationContext.cs index 52036bbe..061aecbb 100644 --- a/Oqtane.Server/Repository/Context/InstallationContext.cs +++ b/Oqtane.Server/Repository/Context/InstallationContext.cs @@ -9,14 +9,16 @@ namespace Oqtane.Repository public class InstallationContext : DbContext { private readonly string _connectionString; + private readonly string _databaseType; - public InstallationContext(string connectionString) + public InstallationContext(string databaseType, string connectionString) { _connectionString = connectionString; + _databaseType = databaseType; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => optionsBuilder.UseOqtaneDatabase(_connectionString); + => optionsBuilder.UseOqtaneDatabase(_databaseType, _connectionString); public virtual DbSet Alias { get; set; } public virtual DbSet Tenant { get; set; } diff --git a/Oqtane.Server/Repository/Context/MasterDBContext.cs b/Oqtane.Server/Repository/Context/MasterDBContext.cs index 68ef6b07..04f61176 100644 --- a/Oqtane.Server/Repository/Context/MasterDBContext.cs +++ b/Oqtane.Server/Repository/Context/MasterDBContext.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore; using Oqtane.Models; using Microsoft.Extensions.Configuration; using Oqtane.Extensions; +using Oqtane.Shared; // ReSharper disable BuiltInTypeReferenceStyleForMemberAccess // ReSharper disable UnusedAutoPropertyAccessor.Global @@ -24,6 +25,7 @@ namespace Oqtane.Repository { var connectionString = _dbConfig.ConnectionString; var configuration = _dbConfig.Configuration; + var databaseType = _dbConfig.DatabaseType; if(string.IsNullOrEmpty(connectionString) && configuration != null) { @@ -33,11 +35,12 @@ namespace Oqtane.Repository .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); } + databaseType = configuration.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey]; } if (!string.IsNullOrEmpty(connectionString)) { - optionsBuilder.UseOqtaneDatabase(connectionString); + optionsBuilder.UseOqtaneDatabase(databaseType, connectionString); } base.OnConfiguring(optionsBuilder); } diff --git a/Oqtane.Server/Repository/Interfaces/IDbConfig.cs b/Oqtane.Server/Repository/Interfaces/IDbConfig.cs index 6ba4cfbe..cde50726 100644 --- a/Oqtane.Server/Repository/Interfaces/IDbConfig.cs +++ b/Oqtane.Server/Repository/Interfaces/IDbConfig.cs @@ -10,5 +10,6 @@ namespace Oqtane.Repository public IConfiguration Configuration { get; } public string ConnectionString { get; set; } + public string DatabaseType { get; set; } } } diff --git a/Oqtane.Server/appsettings.json b/Oqtane.Server/appsettings.json index 06f423f8..33858f9e 100644 --- a/Oqtane.Server/appsettings.json +++ b/Oqtane.Server/appsettings.json @@ -1,10 +1,10 @@ { "Database": { - "DatabaseType": "", + "DatabaseType": "Sqlite", "DatabaseEngineVersion": "" }, "ConnectionStrings": { - "DefaultConnection": "Data Source=.;Initial Catalog=Oqtane-Migrations;Integrated Security=SSPI;" + "DefaultConnection": "Data Source=Oqtane.db" }, "Runtime": "Server", "Installation": { diff --git a/Oqtane.Shared/Models/Tenant.cs b/Oqtane.Shared/Models/Tenant.cs index 645f1e97..d0891563 100644 --- a/Oqtane.Shared/Models/Tenant.cs +++ b/Oqtane.Shared/Models/Tenant.cs @@ -7,6 +7,7 @@ namespace Oqtane.Models public int TenantId { get; set; } public string Name { get; set; } public string DBConnectionString { get; set; } + public string DBType { get; set; } public string Version { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } diff --git a/Oqtane.Shared/Shared/InstallConfig.cs b/Oqtane.Shared/Shared/InstallConfig.cs index 7658e97d..5ca5284d 100644 --- a/Oqtane.Shared/Shared/InstallConfig.cs +++ b/Oqtane.Shared/Shared/InstallConfig.cs @@ -3,6 +3,7 @@ namespace Oqtane.Shared public class InstallConfig { public string ConnectionString { get; set; } + public string DatabaseType { get; set; } public string Aliases { get; set; } public string TenantName { get; set; } public bool IsNewTenant { get; set; } diff --git a/Oqtane.Shared/Shared/SettingKeys.cs b/Oqtane.Shared/Shared/SettingKeys.cs index 6524e333..0656bbc5 100644 --- a/Oqtane.Shared/Shared/SettingKeys.cs +++ b/Oqtane.Shared/Shared/SettingKeys.cs @@ -2,12 +2,18 @@ { public static class SettingKeys { + public const string ConnectionStringsSection = "ConnectionStrings"; + public const string DatabaseSection = "Database"; public const string InstallationSection = "Installation"; + + public const string ConnectionStringKey = "DefaultConnection"; + + public const string DatabaseTypeKey = "DatabaseType"; + public const string DefaultAliasKey = "DefaultAlias"; public const string HostPasswordKey = "HostPassword"; public const string HostEmailKey = "HostEmail"; public const string SiteTemplateKey = "SiteTemplate"; - public const string ConnectionStringKey = "DefaultConnection"; public const string DefaultThemeKey = "DefaultTheme"; public const string DefaultLayoutKey = "DefaultLayout"; public const string DefaultContainerKey = "DefaultContainer";