Added IDatabase interface and refactored to use it to handle database type - updated Installer to dynamically add databases to selector

This commit is contained in:
Charles Nurse 2021-03-24 11:45:44 -07:00
parent cbcfc88492
commit 3a032f401a
14 changed files with 145 additions and 36 deletions

View File

@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;
using Oqtane.Interfaces;
using Oqtane.Modules;
using Oqtane.Providers;
using Oqtane.Services;
@ -74,8 +75,8 @@ namespace Oqtane.Client
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
foreach (var assembly in assemblies)
{
// dynamically register module services
var implementationTypes = assembly.GetInterfaces<IService>();
// dynamically register module services
var implementationTypes = assembly.GetInterfaces<IService>();
foreach (var implementationType in implementationTypes)
{
if (implementationType.AssemblyQualifiedName != null)
@ -85,6 +86,17 @@ namespace Oqtane.Client
}
}
// dynamically register database providers
var databaseTypes = assembly.GetInterfaces<IDatabase>();
foreach (var databaseType in databaseTypes)
{
if (databaseType.AssemblyQualifiedName != null)
{
var serviceType = Type.GetType("Oqtane.Interfaces.IDatabase, Oqtane.Shared");
builder.Services.AddScoped(serviceType ?? databaseType, databaseType);
}
}
// register client startup services
var startUps = assembly.GetInstances<IClientStartup>();
foreach (var startup in startUps)
@ -115,7 +127,7 @@ namespace Oqtane.Client
private static async Task LoadClientAssemblies(HttpClient http)
{
// get list of loaded assemblies on the client
// get list of loaded assemblies on the client
var assemblies = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToList();
// get assemblies from server and load into client app domain

View File

@ -1,3 +1,4 @@
@using Oqtane.Interfaces
@namespace Oqtane.UI
@inject NavigationManager NavigationManager
@inject IInstallationService InstallationService
@ -5,6 +6,7 @@
@inject IUserService UserService
@inject IJSRuntime JSRuntime
@inject IStringLocalizer<Installer> Localizer
@inject IEnumerable<IDatabase> Databases
<div class="container">
<div class="row">
@ -25,9 +27,12 @@
</td>
<td>
<select class="custom-select" @bind="@_databaseType">
<option value="LocalDB">@Localizer["Local Database"]</option>
<option value="SQLServer">@Localizer["SQL Server"]</option>
<option value="Sqlite">@Localizer["Sqlite"]</option>
@{
foreach (var database in Databases)
{
<option value="@database.Name">@Localizer[@database.FriendlyName]</option>
}
}
</select>
</td>
</tr>
@ -148,8 +153,6 @@
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)
@ -176,10 +179,12 @@
StateHasChanged();
var connectionstring = "";
var fullyQualifiedType = "";
switch (_databaseType)
{
case "LocalDB":
connectionstring = "Data Source=" + _serverName + ";AttachDbFilename=|DataDirectory|\\" + _databaseName + ".mdf;Initial Catalog=" + _databaseName + ";Integrated Security=SSPI;";
fullyQualifiedType = "Oqtane.Repository.Databases.LocalDbDatabase, Oqtane.Server";
break;
case "SQLServer":
connectionstring = "Data Source=" + _serverName + ";Initial Catalog=" + _databaseName + ";";
@ -191,9 +196,11 @@
{
connectionstring += "User ID=" + _username + ";Password=" + _password;
}
fullyQualifiedType = "Oqtane.Repository.Databases.SqlServerDatabase, Oqtane.Server";
break;
case "Sqlite":
connectionstring = "Data Source=" + _fileName;
fullyQualifiedType = "Oqtane.Repository.Databases.SqliteDatabase, Oqtane.Server";
break;
}
@ -201,7 +208,7 @@
var config = new InstallConfig
{
DatabaseType = _databaseType,
DatabaseType = fullyQualifiedType,
ConnectionString = connectionstring,
Aliases = uri.Authority,
HostEmail = _hostEmail,
@ -211,6 +218,8 @@
IsNewTenant = true,
SiteName = Constants.DefaultSite
};
var installation = await InstallationService.Install(config);
if (installation.Success)

View File

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
using Oqtane.Interfaces;
namespace Oqtane.Repository.Databases
{
public class LocalDbDatabase : IDatabase
{
public string FriendlyName => "Local Database";
public string Name => "LocalDB";
public DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString)
{
return optionsBuilder.UseSqlServer(connectionString);
}
}
}

View File

@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Oqtane.Interfaces;
namespace Oqtane.Repository.Databases
{
public class SqlServerDatabase : IDatabase
{
public string FriendlyName => "SQL Server";
public string Name => "SqlServer";
public DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString)
{
return optionsBuilder.UseSqlServer(connectionString);
}
}
}

View File

@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Oqtane.Interfaces;
namespace Oqtane.Repository.Databases
{
public class SqliteDatabase : IDatabase
{
public string FriendlyName => Name;
public string Name => "Sqlite";
public DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString)
{
return optionsBuilder.UseSqlite(connectionString);
}
}
}

View File

@ -1,5 +1,10 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Interfaces;
// ReSharper disable ConvertToUsingDeclaration
namespace Oqtane.Extensions
{
@ -7,16 +12,10 @@ namespace Oqtane.Extensions
{
public static DbContextOptionsBuilder UseOqtaneDatabase([NotNull] this DbContextOptionsBuilder optionsBuilder, string databaseType, string connectionString)
{
switch (databaseType)
{
case "SqlServer":
optionsBuilder.UseSqlServer(connectionString);
var type = Type.GetType(databaseType);
var database = Activator.CreateInstance(type) as IDatabase;
break;
case "Sqlite":
optionsBuilder.UseSqlite(connectionString);
break;
}
database.UseDatabase(optionsBuilder, connectionString);
return optionsBuilder;
}

View File

@ -5,6 +5,7 @@ using System.Reflection;
using System.Runtime.Loader;
using Microsoft.Extensions.Hosting;
using Oqtane.Infrastructure;
using Oqtane.Interfaces;
using Oqtane.Modules;
using Oqtane.Services;
using Oqtane.Shared;
@ -46,6 +47,17 @@ namespace Microsoft.Extensions.DependencyInjection
}
}
// dynamically register database providers
var databaseTypes = assembly.GetInterfaces<IDatabase>();
foreach (var databaseType in databaseTypes)
{
if (databaseType.AssemblyQualifiedName != null)
{
var serviceType = Type.GetType("Oqtane.Interfaces.IDatabase, Oqtane.Shared");
services.AddScoped(serviceType ?? databaseType, databaseType);
}
}
// dynamically register hosted services
var serviceTypes = assembly.GetTypes(hostedServiceType);
foreach (var serviceType in serviceTypes)

View File

@ -234,18 +234,18 @@ namespace Oqtane.Infrastructure
if (!string.IsNullOrEmpty(install.TenantName) && !string.IsNullOrEmpty(install.Aliases))
{
using (var db = new InstallationContext(install.DatabaseType, NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
using (var db = GetInstallationContext())
{
Tenant tenant;
if (install.IsNewTenant)
{
tenant = new Tenant { Name = install.TenantName,
DBConnectionString = DenormalizeConnectionString(install.ConnectionString),
DBType = install.DatabaseType,
CreatedBy = "",
CreatedOn = DateTime.UtcNow,
ModifiedBy = "",
ModifiedOn = DateTime.UtcNow };
DBConnectionString = DenormalizeConnectionString(install.ConnectionString),
DBType = install.DatabaseType,
CreatedBy = "",
CreatedOn = DateTime.UtcNow,
ModifiedBy = "",
ModifiedOn = DateTime.UtcNow };
db.Tenant.Add(tenant);
db.SaveChanges();
_cache.Remove("tenants");
@ -274,6 +274,14 @@ namespace Oqtane.Infrastructure
return result;
}
private InstallationContext GetInstallationContext()
{
var databaseType = _config.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey];
var connectionString = NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey));
return new InstallationContext(databaseType, connectionString);
}
private Installation MigrateTenants(InstallConfig install)
{
var result = new Installation { Success = false, Message = string.Empty };
@ -284,9 +292,7 @@ namespace Oqtane.Infrastructure
{
var upgrades = scope.ServiceProvider.GetRequiredService<IUpgradeManager>();
var databaseType = _config.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey];
var connectionString = NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey));
using (var db = new InstallationContext(databaseType, connectionString))
using (var db = GetInstallationContext())
{
foreach (var tenant in db.Tenant.ToList())
{
@ -347,9 +353,8 @@ namespace Oqtane.Infrastructure
if (moduleType != null)
{
var versions = moduleDefinition.ReleaseVersions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var databaseType = _config.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey];
var connectionString = NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey));
using (var db = new InstallationContext(databaseType, connectionString)) {
using (var db = GetInstallationContext())
{
foreach (var tenant in db.Tenant.ToList())
{
var index = Array.FindIndex(versions, item => item == moduleDefinition.Version);

View File

@ -19,7 +19,7 @@ namespace Oqtane.Migrations
migrationBuilder.Sql(
@"
UPDATE Tenant
SET DBType = 'SqlServer'
SET DBType = 'Oqtane.Repository.Databases.SqlServerDatabase, Oqtane.Server'
");
}

View File

@ -9,8 +9,9 @@ namespace Oqtane.Migrations.Extensions
public static OperationBuilder<AddColumnOperation> AddAutoIncrementColumn(this ColumnsBuilder table, string name)
{
return table.Column<int>(name: name, nullable: false)
.Annotation("SqlServer:Identity", "1, 1")
.Annotation("Sqlite:Autoincrement", true);
.Annotation("SqlServer:Identity", "1, 1")
.Annotation("Sqlite:Autoincrement", true)
.Annotation("MySql:ValueGeneratedOnAdd", true);
}
public static OperationBuilder<AddColumnOperation> AddBooleanColumn(this ColumnsBuilder table, string name, bool nullable = false)

View File

@ -1,12 +1,10 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Oqtane.Extensions;
using Oqtane.Models;
using Oqtane.Shared;
// ReSharper disable BuiltInTypeReferenceStyleForMemberAccess

View File

@ -0,0 +1,13 @@
using Microsoft.EntityFrameworkCore;
namespace Oqtane.Interfaces
{
public interface IDatabase
{
public string FriendlyName { get; }
public string Name { get; }
public DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString);
}
}

View File

@ -0,0 +1,9 @@
namespace Oqtane.Models
{
public class Database
{
public string Name { get; set; }
public string Type { get; set; }
}
}

View File

@ -18,6 +18,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.4" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Text.Json" Version="5.0.1" />