Support for third party modules, improved error handling, standardardized enum naming, reorganized interface definitions, support for DB script upgrades, added Settings entity

This commit is contained in:
Shaun Walker
2019-08-14 09:34:35 -04:00
parent 916109015f
commit b71f007981
78 changed files with 809 additions and 261 deletions

View File

@ -1,11 +1,15 @@
using DbUp;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Oqtane.Models;
using Oqtane.Repository;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
@ -182,11 +186,71 @@ namespace Oqtane.Controllers
response.Success = !dbUpgrade.IsUpgradeRequired();
if (!response.Success)
{
response.Message = "Scripts Have Not Been Run";
response.Message = "Master Installation Scripts Have Not Been Executed";
}
else
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(item => item.FullName.Contains(".Module.")).ToArray();
// get tenants
using (var db = new InstallationContext(connectionString))
{
foreach (Tenant tenant in db.Tenant.ToList())
{
// upgrade framework
dbUpgradeConfig = DeployChanges.To.SqlDatabase(tenant.DBConnectionString)
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly());
dbUpgrade = dbUpgradeConfig.Build();
if (dbUpgrade.IsUpgradeRequired())
{
var result = dbUpgrade.PerformUpgrade();
if (!result.Successful)
{
// TODO: log result.Error.Message;
}
}
// iterate through Oqtane module assemblies and execute any database scripts
foreach (Assembly assembly in assemblies)
{
InstallModule(assembly, tenant.DBConnectionString);
}
}
}
}
}
return response;
}
private void InstallModule(Assembly assembly, string connectionstring)
{
var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionstring)
.WithScriptsEmbeddedInAssembly(assembly); // scripts must be included as Embedded Resources
var dbUpgrade = dbUpgradeConfig.Build();
if (dbUpgrade.IsUpgradeRequired())
{
var result = dbUpgrade.PerformUpgrade();
if (!result.Successful)
{
// TODO: log result.Error.Message;
}
}
}
}
public class InstallationContext : DbContext
{
private readonly string _connectionString;
public InstallationContext(string connectionString)
{
_connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(_connectionString);
public virtual DbSet<Tenant> Tenant { get; set; }
}
}

View File

@ -0,0 +1,61 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Repository;
using Oqtane.Models;
namespace Oqtane.Controllers
{
[Route("{site}/api/[controller]")]
public class SettingController : Controller
{
private readonly ISettingRepository Settings;
public SettingController(ISettingRepository Settings)
{
this.Settings = Settings;
}
// GET: api/<controller>
[HttpGet]
public IEnumerable<Setting> Get(string entityname, int entityid)
{
return Settings.GetSettings(entityname, entityid);
}
// GET api/<controller>/5
[HttpGet("{id}")]
public Setting Get(int id)
{
return Settings.GetSetting(id);
}
// POST api/<controller>
[HttpPost]
public Setting Post([FromBody] Setting Setting)
{
if (ModelState.IsValid)
{
Setting = Settings.AddSetting(Setting);
}
return Setting;
}
// PUT api/<controller>/5
[HttpPut("{id}")]
public Setting Put(int id, [FromBody] Setting Setting)
{
if (ModelState.IsValid)
{
Setting = Settings.UpdateSetting(Setting);
}
return Setting;
}
// DELETE api/<controller>/5
[HttpDelete("{id}")]
public void Delete(int id)
{
Settings.DeleteSetting(id);
}
}
}

View File

@ -4,6 +4,7 @@ using Oqtane.Repository;
using Oqtane.Models;
using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
using System.Linq;
namespace Oqtane.Controllers
{
@ -11,12 +12,14 @@ namespace Oqtane.Controllers
public class UserController : Controller
{
private readonly IUserRepository users;
private readonly ISiteUserRepository siteusers;
private readonly UserManager<IdentityUser> identityUserManager;
private readonly SignInManager<IdentityUser> identitySignInManager;
public UserController(IUserRepository Users, UserManager<IdentityUser> IdentityUserManager, SignInManager<IdentityUser> IdentitySignInManager)
public UserController(IUserRepository Users, ISiteUserRepository SiteUsers, UserManager<IdentityUser> IdentityUserManager, SignInManager<IdentityUser> IdentitySignInManager)
{
users = Users;
siteusers = SiteUsers;
identityUserManager = IdentityUserManager;
identitySignInManager = IdentitySignInManager;
}
@ -39,6 +42,8 @@ namespace Oqtane.Controllers
[HttpPost]
public async Task<User> Post([FromBody] User User)
{
User user = null;
if (ModelState.IsValid)
{
IdentityUser identityuser = await identityUserManager.FindByNameAsync(User.Username);
@ -50,11 +55,17 @@ namespace Oqtane.Controllers
var result = await identityUserManager.CreateAsync(identityuser, User.Password);
if (result.Succeeded)
{
User = users.AddUser(User);
user = users.AddUser(User);
SiteUser SiteUser = new SiteUser();
SiteUser.SiteId = User.SiteId;
SiteUser.UserId = user.UserId;
SiteUser.IsAuthorized = true;
siteusers.AddSiteUser(SiteUser);
}
}
}
return User;
return user;
}
// PUT api/<controller>/5
@ -84,36 +95,38 @@ namespace Oqtane.Controllers
// POST api/<controller>/login
[HttpPost("login")]
public async Task<User> Login([FromBody] User user)
public async Task<User> Login([FromBody] User User)
{
User user = new Models.User { Username = User.Username, IsAuthenticated = false };
if (ModelState.IsValid)
{
IdentityUser identityuser = await identityUserManager.FindByNameAsync(user.Username);
IdentityUser identityuser = await identityUserManager.FindByNameAsync(User.Username);
if (identityuser != null)
{
var result = await identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false);
var result = await identitySignInManager.CheckPasswordSignInAsync(identityuser, User.Password, false);
if (result.Succeeded)
{
await identitySignInManager.SignInAsync(identityuser, user.IsPersistent);
user = users.GetUser(identityuser.UserName);
user.IsAuthenticated = true;
if (user != null)
{
SiteUser siteuser = siteusers.GetSiteUsers(User.SiteId, user.UserId).FirstOrDefault();
if (siteuser.IsAuthorized)
{
await identitySignInManager.SignInAsync(identityuser, User.IsPersistent);
user.IsAuthenticated = true;
}
}
}
else
{
user = new Models.User { Username = user.Username, IsAuthenticated = false };
}
}
else
{
user = new Models.User { Username = user.Username, IsAuthenticated = false };
}
}
return user;
}
// POST api/<controller>/logout
[HttpPost("logout")]
public async Task Logout([FromBody] User user)
public async Task Logout([FromBody] User User)
{
await identitySignInManager.SignOutAsync();
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
namespace Microsoft.Extensions.DependencyInjection
{
public static class MvcModuleExtensions
{
public static IMvcBuilder AddModuleAssemblies(this IMvcBuilder mvcBuilder, List<Assembly> assemblies)
{
// load MVC application parts from module assemblies
foreach (Assembly assembly in assemblies)
{
// check if assembly contains MVC Controllers
if (assembly.GetTypes().Where(item => item.IsSubclassOf(typeof(Controller))).ToArray().Length > 0)
{
var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
foreach (var part in partFactory.GetApplicationParts(assembly))
{
mvcBuilder.PartManager.ApplicationParts.Add(part);
}
}
}
return mvcBuilder;
}
}
}

View File

@ -26,14 +26,14 @@
</PropertyGroup>
<ItemGroup>
<None Remove="Scripts\Identity.sql" />
<None Remove="Scripts\00.00.01.sql" />
<None Remove="Scripts\Master.sql" />
</ItemGroup>
<ItemGroup>
<Content Include="Scripts\Master.sql" />
<EmbeddedResource Include="Scripts\Identity.sql" />
<EmbeddedResource Include="Scripts\Tenant.sql" />
<EmbeddedResource Include="Scripts\00.00.01.sql" />
<EmbeddedResource Include="Scripts\00.00.00.sql" />
</ItemGroup>
<ItemGroup>

View File

@ -12,6 +12,7 @@ namespace Oqtane.Repository
public virtual DbSet<Module> Module { get; set; }
public virtual DbSet<User> User { get; set; }
public virtual DbSet<SiteUser> SiteUser { get; set; }
public virtual DbSet<Setting> Setting { get; set; }
public TenantDBContext(ITenantResolver TenantResolver, IHttpContextAccessor accessor) : base(TenantResolver, accessor)
{

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Repository
{
public interface ISettingRepository
{
IEnumerable<Setting> GetSettings(string EntityName, int EntityId);
Setting AddSetting(Setting Setting);
Setting UpdateSetting(Setting Setting);
Setting GetSetting(int SettingId);
void DeleteSetting(int SettingId);
}
}

View File

@ -22,12 +22,11 @@ namespace Oqtane.Repository
List<ModuleDefinition> moduledefinitions = new List<ModuleDefinition>();
// iterate through Oqtane module assemblies
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray();
foreach (Assembly assembly in assemblies)
{
if (assembly.FullName.StartsWith("Oqtane.Client") || assembly.FullName.StartsWith("Oqtane.Module."))
{
moduledefinitions = LoadModuleDefinitionsFromAssembly(moduledefinitions, assembly);
}
moduledefinitions = LoadModuleDefinitionsFromAssembly(moduledefinitions, assembly);
}
return moduledefinitions;
@ -119,6 +118,5 @@ namespace Oqtane.Repository
return moduledefinitions;
}
}
}

View File

@ -0,0 +1,85 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using Oqtane.Models;
namespace Oqtane.Repository
{
public class SettingRepository : ISettingRepository
{
private TenantDBContext db;
public SettingRepository(TenantDBContext context)
{
db = context;
}
public IEnumerable<Setting> GetSettings(string EntityName, int EntityId)
{
try
{
return db.Setting.Where(item => item.EntityName == EntityName)
.Where(item => item.EntityId == EntityId).ToList();
}
catch
{
throw;
}
}
public Setting AddSetting(Setting Setting)
{
try
{
db.Setting.Add(Setting);
db.SaveChanges();
return Setting;
}
catch
{
throw;
}
}
public Setting UpdateSetting(Setting Setting)
{
try
{
db.Entry(Setting).State = EntityState.Modified;
db.SaveChanges();
return Setting;
}
catch
{
throw;
}
}
public Setting GetSetting(int SettingId)
{
try
{
Setting Setting = db.Setting.Find(SettingId);
return Setting;
}
catch
{
throw;
}
}
public void DeleteSetting(int SettingId)
{
try
{
Setting Setting = db.Setting.Find(SettingId);
db.Setting.Remove(Setting);
db.SaveChanges();
}
catch
{
throw;
}
}
}
}

View File

@ -22,13 +22,11 @@ namespace Oqtane.Repository
List<Theme> themes = new List<Theme>();
// iterate through Oqtane theme assemblies
// TODO: Remove restriction on assembly
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Theme.")).ToArray();
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.FullName.StartsWith("Oqtane.Client") || assembly.FullName.StartsWith("Oqtane.Theme."))
{
themes = LoadThemesFromAssembly(themes, assembly);
}
themes = LoadThemesFromAssembly(themes, assembly);
}
return themes;

View File

@ -127,6 +127,23 @@ CREATE TABLE [dbo].[SiteUser](
)
)
GO
CREATE TABLE [dbo].[Setting](
[SettingId] [int] IDENTITY(1,1) NOT NULL,
[EntityName] [nvarchar](50) NOT NULL,
[EntityId] [int] NOT NULL,
[SettingName] [nvarchar](50) NOT NULL,
[SettingValue] [nvarchar](max) NOT NULL,
[CreatedBy] [nvarchar](256) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[ModifiedBy] [nvarchar](256) NOT NULL,
[ModifiedOn] [datetime] NOT NULL,
CONSTRAINT [PK_Setting] PRIMARY KEY CLUSTERED
(
[SettingId] ASC
)
)
GO
/*
Create foreign key relationships
@ -167,6 +184,20 @@ GO
/*
Create indexes
*/
CREATE UNIQUE NONCLUSTERED INDEX IX_Setting ON dbo.Setting
(
EntityName,
EntityId,
SettingName
) ON [PRIMARY]
GO
/*
Create seed data
*/
@ -303,19 +334,19 @@ INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane]
VALUES (2, 1, 2, N'Counter', N'Left', 1, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (3, 1, 3, N'Text', N'Left', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
VALUES (3, 1, 3, N'Lorem ipsum', N'Left', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (4, 2, 4, N'Weather', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())
GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (5, 2, 5, N'Text', N'Top', 1, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
VALUES (5, 2, 5, N'Enim sed', N'Top', 1, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (6, 3, 6, N'Text', N'Left', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
VALUES (6, 3, 6, N'Id consectetur', N'Left', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (7, 3, 7, N'Text', N'Right', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
VALUES (7, 3, 7, N'Ornare arcu', N'Right', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (8, 5, 8, N'Page Management', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())
@ -342,10 +373,10 @@ INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane]
VALUES (15, 11, 15, N'Theme Management', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())
GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (16, 12, 16, N'Text', N'Top', 1, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())
VALUES (16, 12, 16, N'Id consectetur', N'Top', 1, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())
GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (17, 13, 17, N'Text', N'Top', 1, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())
VALUES (17, 13, 17, N'Lorem ipsum', N'Top', 1, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())
GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (18, 14, 18, N'Login', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())

View File

@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.AspNetCore.ResponseCompression; // needed for WASM
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using Microsoft.Extensions.Configuration;
@ -19,6 +19,7 @@ using Microsoft.AspNetCore.Components;
using Oqtane.Shared;
using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Oqtane.Server
{
@ -77,27 +78,7 @@ namespace Oqtane.Server
services.AddScoped<IModuleService, ModuleService>();
services.AddScoped<IPageModuleService, PageModuleService>();
services.AddScoped<IUserService, UserService>();
// dynamically register module contexts and repository services
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
Type[] implementationtypes = assembly.GetTypes()
.Where(item => item.GetInterfaces().Contains(typeof(IService)))
.ToArray();
foreach (Type implementationtype in implementationtypes)
{
Type servicetype = Type.GetType(implementationtype.FullName.Replace(implementationtype.Name, "I" + implementationtype.Name));
if (servicetype != null)
{
services.AddScoped(servicetype, implementationtype); // traditional service interface
}
else
{
services.AddScoped(implementationtype, implementationtype); // no interface defined for service
}
}
}
services.AddScoped<ISettingService, SettingService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
@ -139,9 +120,26 @@ namespace Oqtane.Server
};
});
services.AddMemoryCache();
// get list of loaded assemblies
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
services.AddMvc().AddNewtonsoftJson();
// iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process )
string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
DirectoryInfo folder = new DirectoryInfo(path);
List<Assembly> moduleassemblies = new List<Assembly>();
foreach (FileInfo file in folder.EnumerateFiles("*.Module.*.dll"))
{
// check if assembly is already loaded
Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault();
if (assembly == null)
{
// load assembly ( as long as dependencies are in /bin they will load as well )
assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName);
moduleassemblies.Add(assembly);
}
}
services.AddMvc().AddModuleAssemblies(moduleassemblies).AddNewtonsoftJson();
// register singleton scoped core services
services.AddSingleton<IConfigurationRoot>(Configuration);
@ -157,26 +155,12 @@ namespace Oqtane.Server
services.AddTransient<IModuleRepository, ModuleRepository>();
services.AddTransient<IPageModuleRepository, PageModuleRepository>();
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<ISiteUserRepository, SiteUserRepository>();
services.AddTransient<ISettingRepository, SettingRepository>();
// get list of loaded assemblies
assemblies = AppDomain.CurrentDomain.GetAssemblies();
// get path to /bin
string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
DirectoryInfo folder = new DirectoryInfo(path);
// iterate through Oqtane assemblies in /bin ( filter is narrow to optimize loading process )
foreach (FileInfo file in folder.EnumerateFiles("Oqtane.*.dll"))
{
// check if assembly is already loaded
Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault();
if (assembly == null)
{
// load assembly ( as long as dependencies are in /bin they will load as well )
assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName);
}
}
// dynamically register module contexts and repository services
assemblies = AppDomain.CurrentDomain.GetAssemblies();
// dynamically register module services, contexts, and repository classes
assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray();
foreach (Assembly assembly in assemblies)
{
Type[] implementationtypes = assembly.GetTypes()
@ -184,7 +168,7 @@ namespace Oqtane.Server
.ToArray();
foreach (Type implementationtype in implementationtypes)
{
Type servicetype = Type.GetType(implementationtype.FullName.Replace(implementationtype.Name, "I" + implementationtype.Name));
Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name));
if (servicetype != null)
{
services.AddScoped(servicetype, implementationtype); // traditional service interface
@ -273,9 +257,26 @@ namespace Oqtane.Server
};
});
services.AddMemoryCache();
// get list of loaded assemblies
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
services.AddMvc().AddNewtonsoftJson();
// iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process )
string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
DirectoryInfo folder = new DirectoryInfo(path);
List<Assembly> moduleassemblies = new List<Assembly>();
foreach (FileInfo file in folder.EnumerateFiles("*.Module.*.dll"))
{
// check if assembly is already loaded
Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault();
if (assembly == null)
{
// load assembly ( as long as dependencies are in /bin they will load as well )
assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName);
moduleassemblies.Add(assembly);
}
}
services.AddMvc().AddModuleAssemblies(moduleassemblies).AddNewtonsoftJson();
// register singleton scoped core services
services.AddSingleton<IConfigurationRoot>(Configuration);
@ -291,26 +292,12 @@ namespace Oqtane.Server
services.AddTransient<IModuleRepository, ModuleRepository>();
services.AddTransient<IPageModuleRepository, PageModuleRepository>();
services.AddTransient<IUserRepository, UserRepository>();
// get list of loaded assemblies
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
// get path to /bin
string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
DirectoryInfo folder = new DirectoryInfo(path);
// iterate through Oqtane assemblies in /bin ( filter is narrow to optimize loading process )
foreach (FileInfo file in folder.EnumerateFiles("Oqtane.*.dll"))
{
// check if assembly is already loaded
Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault();
if (assembly == null)
{
// load assembly ( as long as dependencies are in /bin they will load as well )
assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName);
}
}
// dynamically register module contexts and repository services
assemblies = AppDomain.CurrentDomain.GetAssemblies();
services.AddTransient<ISiteUserRepository, SiteUserRepository>();
services.AddTransient<ISettingRepository, SettingRepository>();
// dynamically register module services, contexts, and repository classes
assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray();
foreach (Assembly assembly in assemblies)
{
Type[] implementationtypes = assembly.GetTypes()
@ -318,7 +305,7 @@ namespace Oqtane.Server
.ToArray();
foreach (Type implementationtype in implementationtypes)
{
Type servicetype = Type.GetType(implementationtype.FullName.Replace(implementationtype.Name, "I" + implementationtype.Name));
Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name));
if (servicetype != null)
{
services.AddScoped(servicetype, implementationtype); // traditional service interface