Merge pull request #183 from hishamco/oqtane-service-collection-extensions
Oqtane service collection extensions
This commit is contained in:
commit
af9f4aba73
35
Oqtane.Server/Extensions/AssemblyExtensions.cs
Normal file
35
Oqtane.Server/Extensions/AssemblyExtensions.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace System.Reflection
|
||||||
|
{
|
||||||
|
public static class AssemblyExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<Type> GetInterfaces<TInterfaceType>(this Assembly assembly)
|
||||||
|
{
|
||||||
|
if (assembly is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(assembly));
|
||||||
|
}
|
||||||
|
|
||||||
|
return assembly.GetTypes(typeof(TInterfaceType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Type> GetTypes(this Assembly assembly, Type interfaceType)
|
||||||
|
{
|
||||||
|
if (assembly is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(assembly));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interfaceType is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(interfaceType));
|
||||||
|
}
|
||||||
|
|
||||||
|
return assembly.GetTypes()
|
||||||
|
.Where(t => t.GetInterfaces().Contains(interfaceType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +1,24 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
{
|
{
|
||||||
public static class MvcModuleExtensions
|
public static class OqtaneMvcBuilderExtensions
|
||||||
{
|
{
|
||||||
public static IMvcBuilder AddModuleAssemblies(this IMvcBuilder mvcBuilder, List<Assembly> assemblies)
|
public static IMvcBuilder AddOqtaneApplicationParts(this IMvcBuilder mvcBuilder)
|
||||||
{
|
{
|
||||||
|
if (mvcBuilder is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(mvcBuilder));
|
||||||
|
}
|
||||||
|
|
||||||
// load MVC application parts from module assemblies
|
// load MVC application parts from module assemblies
|
||||||
foreach (Assembly assembly in assemblies)
|
foreach (var assembly in OqtaneServiceCollectionExtensions.GetOqtaneModuleAssemblies())
|
||||||
{
|
{
|
||||||
// check if assembly contains MVC Controllers
|
// check if assembly contains MVC Controllers
|
||||||
if (assembly.GetTypes().Where(item => item.IsSubclassOf(typeof(Controller))).ToArray().Length > 0)
|
if (assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Controller))).ToArray().Length > 0)
|
||||||
{
|
{
|
||||||
var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
|
var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
|
||||||
foreach (var part in partFactory.GetApplicationParts(assembly))
|
foreach (var part in partFactory.GetApplicationParts(assembly))
|
111
Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
Normal file
111
Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Oqtane.Infrastructure;
|
||||||
|
using Oqtane.Modules;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
{
|
||||||
|
public static class OqtaneServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
private static readonly IList<Assembly> _oqtaneModuleAssemblies = new List<Assembly>();
|
||||||
|
|
||||||
|
private static Assembly[] Assemblies => AppDomain.CurrentDomain.GetAssemblies();
|
||||||
|
|
||||||
|
internal static IEnumerable<Assembly> GetOqtaneModuleAssemblies() => _oqtaneModuleAssemblies;
|
||||||
|
|
||||||
|
public static IServiceCollection AddOqtaneModules(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
if (services is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(services));
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadAssemblies("Module");
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddOqtaneThemes(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
if (services is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(services));
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadAssemblies("Theme");
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddOqtaneServices(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
if (services is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(services));
|
||||||
|
}
|
||||||
|
|
||||||
|
// dynamically register module services, contexts, and repository classes
|
||||||
|
var assemblies = Assemblies.
|
||||||
|
Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray();
|
||||||
|
foreach (var assembly in assemblies)
|
||||||
|
{
|
||||||
|
var implementationTypes = assembly.GetInterfaces<IService>();
|
||||||
|
foreach (var implementationType in implementationTypes)
|
||||||
|
{
|
||||||
|
var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}"));
|
||||||
|
services.AddScoped(serviceType ?? implementationType, implementationType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddOqtaneHostedServices(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
if (services is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(services));
|
||||||
|
}
|
||||||
|
|
||||||
|
// dynamically register hosted services
|
||||||
|
var hostedServiceType = typeof(IHostedService);
|
||||||
|
foreach (var assembly in Assemblies)
|
||||||
|
{
|
||||||
|
var serviceTypes = assembly.GetTypes(hostedServiceType);
|
||||||
|
foreach (var serviceType in serviceTypes)
|
||||||
|
{
|
||||||
|
if (serviceType.Name != nameof(HostedServiceBase))
|
||||||
|
{
|
||||||
|
services.AddSingleton(hostedServiceType, serviceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadAssemblies(string pattern)
|
||||||
|
{
|
||||||
|
var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||||
|
var assembliesFolder = new DirectoryInfo(assemblyPath);
|
||||||
|
|
||||||
|
// iterate through Oqtane theme assemblies in /bin ( filter is narrow to optimize loading process )
|
||||||
|
foreach (var file in assembliesFolder.EnumerateFiles($"*.{pattern}.*.dll"))
|
||||||
|
{
|
||||||
|
// check if assembly is already loaded
|
||||||
|
var assembly = Assemblies.Where(a => a.Location == file.FullName).FirstOrDefault();
|
||||||
|
if (assembly == null)
|
||||||
|
{
|
||||||
|
// load assembly from stream to prevent locking file ( as long as dependencies are in /bin they will load as well )
|
||||||
|
assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(file.FullName)));
|
||||||
|
_oqtaneModuleAssemblies.Add(assembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -180,75 +180,15 @@ namespace Oqtane.Server
|
|||||||
services.AddTransient<IJobRepository, JobRepository>();
|
services.AddTransient<IJobRepository, JobRepository>();
|
||||||
services.AddTransient<IJobLogRepository, JobLogRepository>();
|
services.AddTransient<IJobLogRepository, JobLogRepository>();
|
||||||
|
|
||||||
// get list of loaded assemblies
|
services.AddOqtaneModules();
|
||||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
services.AddOqtaneThemes();
|
||||||
string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
|
||||||
DirectoryInfo folder = new DirectoryInfo(path);
|
|
||||||
List<Assembly> moduleassemblies = new List<Assembly>();
|
|
||||||
|
|
||||||
// iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process )
|
services.AddMvc()
|
||||||
foreach (FileInfo file in folder.EnumerateFiles("*.Module.*.dll"))
|
.AddOqtaneApplicationParts()
|
||||||
{
|
.AddNewtonsoftJson();
|
||||||
// check if assembly is already loaded
|
|
||||||
Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault();
|
|
||||||
if (assembly == null)
|
|
||||||
{
|
|
||||||
// load assembly from stream to prevent locking file ( as long as dependencies are in /bin they will load as well )
|
|
||||||
assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(file.FullName)));
|
|
||||||
moduleassemblies.Add(assembly);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate through Oqtane theme assemblies in /bin ( filter is narrow to optimize loading process )
|
services.AddOqtaneServices();
|
||||||
foreach (FileInfo file in folder.EnumerateFiles("*.Theme.*.dll"))
|
services.AddOqtaneHostedServices();
|
||||||
{
|
|
||||||
// check if assembly is already loaded
|
|
||||||
Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault();
|
|
||||||
if (assembly == null)
|
|
||||||
{
|
|
||||||
// load assembly from stream to prevent locking file ( as long as dependencies are in /bin they will load as well )
|
|
||||||
assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(file.FullName)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
services.AddMvc().AddModuleAssemblies(moduleassemblies).AddNewtonsoftJson();
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
.Where(item => item.GetInterfaces().Contains(typeof(IService)))
|
|
||||||
.ToArray();
|
|
||||||
foreach (Type implementationtype in implementationtypes)
|
|
||||||
{
|
|
||||||
Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dynamically register hosted services
|
|
||||||
foreach (Assembly assembly in assemblies)
|
|
||||||
{
|
|
||||||
Type[] servicetypes = assembly.GetTypes()
|
|
||||||
.Where(item => item.GetInterfaces().Contains(typeof(IHostedService)))
|
|
||||||
.ToArray();
|
|
||||||
foreach (Type servicetype in servicetypes)
|
|
||||||
{
|
|
||||||
if (servicetype.Name != "HostedServiceBase")
|
|
||||||
{
|
|
||||||
services.AddSingleton(typeof(IHostedService), servicetype);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
services.AddSwaggerGen(c =>
|
services.AddSwaggerGen(c =>
|
||||||
{
|
{
|
||||||
@ -387,74 +327,15 @@ namespace Oqtane.Server
|
|||||||
services.AddTransient<IJobRepository, JobRepository>();
|
services.AddTransient<IJobRepository, JobRepository>();
|
||||||
services.AddTransient<IJobLogRepository, JobLogRepository>();
|
services.AddTransient<IJobLogRepository, JobLogRepository>();
|
||||||
|
|
||||||
// get list of loaded assemblies
|
services.AddOqtaneModules();
|
||||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
services.AddOqtaneThemes();
|
||||||
string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
|
||||||
DirectoryInfo folder = new DirectoryInfo(path);
|
|
||||||
List<Assembly> moduleassemblies = new List<Assembly>();
|
|
||||||
|
|
||||||
// iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process )
|
services.AddMvc()
|
||||||
foreach (FileInfo file in folder.EnumerateFiles("*.Module.*.dll"))
|
.AddOqtaneApplicationParts()
|
||||||
{
|
.AddNewtonsoftJson();
|
||||||
// check if assembly is already loaded
|
|
||||||
Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault();
|
|
||||||
if (assembly == null)
|
|
||||||
{
|
|
||||||
// load assembly from stream to prevent locking file ( as long as dependencies are in /bin they will load as well )
|
|
||||||
assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(file.FullName)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate through Oqtane theme assemblies in /bin ( filter is narrow to optimize loading process )
|
services.AddOqtaneServices();
|
||||||
foreach (FileInfo file in folder.EnumerateFiles("*.Theme.*.dll"))
|
services.AddOqtaneHostedServices();
|
||||||
{
|
|
||||||
// check if assembly is already loaded
|
|
||||||
Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault();
|
|
||||||
if (assembly == null)
|
|
||||||
{
|
|
||||||
// load assembly from stream to prevent locking file ( as long as dependencies are in /bin they will load as well )
|
|
||||||
assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(file.FullName)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
services.AddMvc().AddModuleAssemblies(moduleassemblies).AddNewtonsoftJson();
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
.Where(item => item.GetInterfaces().Contains(typeof(IService)))
|
|
||||||
.ToArray();
|
|
||||||
foreach (Type implementationtype in implementationtypes)
|
|
||||||
{
|
|
||||||
Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dynamically register hosted services
|
|
||||||
foreach (Assembly assembly in assemblies)
|
|
||||||
{
|
|
||||||
Type[] servicetypes = assembly.GetTypes()
|
|
||||||
.Where(item => item.GetInterfaces().Contains(typeof(IHostedService)))
|
|
||||||
.ToArray();
|
|
||||||
foreach (Type servicetype in servicetypes)
|
|
||||||
{
|
|
||||||
if (servicetype.Name != "HostedServiceBase")
|
|
||||||
{
|
|
||||||
services.AddSingleton(typeof(IHostedService), servicetype);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
services.AddSwaggerGen(c =>
|
services.AddSwaggerGen(c =>
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user