IClientStartup implementation

This commit is contained in:
Pavel Vesely 2020-05-11 13:10:22 +02:00
parent 7c814a67b3
commit da73d519d7
7 changed files with 80 additions and 12 deletions

View File

@ -4,8 +4,10 @@ using System.Threading.Tasks;
using Oqtane.Services; using Oqtane.Services;
using System.Reflection; using System.Reflection;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Json;
using Oqtane.Modules; using Oqtane.Modules;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.Providers; using Oqtane.Providers;
@ -19,10 +21,9 @@ namespace Oqtane.Client
{ {
var builder = WebAssemblyHostBuilder.CreateDefault(args); var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app"); builder.RootComponents.Add<App>("app");
HttpClient httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)};
builder.Services.AddSingleton( builder.Services.AddSingleton(httpClient);
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }
);
builder.Services.AddOptions(); builder.Services.AddOptions();
// register auth services // register auth services
@ -57,14 +58,16 @@ namespace Oqtane.Client
builder.Services.AddScoped<ISqlService, SqlService>(); builder.Services.AddScoped<ISqlService, SqlService>();
builder.Services.AddScoped<ISystemService, SystemService>(); builder.Services.AddScoped<ISystemService, SystemService>();
await LoadClientAssemblies(httpClient);
// dynamically register module contexts and repository services // dynamically register module contexts and repository services
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies) foreach (Assembly assembly in assemblies)
{ {
Type[] implementationtypes = assembly.GetTypes() var implementationTypes = assembly.GetTypes()
.Where(item => item.GetInterfaces().Contains(typeof(IService))) .Where(item => item.GetInterfaces().Contains(typeof(IService)));
.ToArray();
foreach (Type implementationtype in implementationtypes) foreach (Type implementationtype in implementationTypes)
{ {
Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name)); Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name));
if (servicetype != null) if (servicetype != null)
@ -76,9 +79,27 @@ namespace Oqtane.Client
builder.Services.AddScoped(implementationtype, implementationtype); // no interface defined for service builder.Services.AddScoped(implementationtype, implementationtype); // no interface defined for service
} }
} }
assembly.GetInstances<IClientStartup>()
.ToList()
.ForEach(x => x.ConfigureServices(builder.Services));
} }
await builder.Build().RunAsync(); await builder.Build().RunAsync();
} }
private static async Task LoadClientAssemblies(HttpClient http)
{
var list = await http.GetFromJsonAsync<List<string>>($"/~/api/ModuleDefinition/load");
// get list of loaded assemblies on the client ( in the client-side hosting module the browser client has its own app domain )
var assemblyList = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToList();
foreach (var name in list)
{
if (assemblyList.Contains(name)) continue;
// download assembly from server and load
var bytes = await http.GetByteArrayAsync($"/~/api/ModuleDefinition/load/{name}.dll");
Assembly.Load(bytes);
}
}
} }
} }

View File

@ -170,6 +170,16 @@ namespace Oqtane.Controllers
return null; return null;
} }
} }
// GET api/<controller>/load/assembyname
[HttpGet("load")]
public List<string> Load()
{
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
var list = AppDomain.CurrentDomain.GetOqtaneClientAssemblies().Select(a => a.GetName().Name).ToList();
var deps = assemblies.SelectMany(a => a.GetReferencedAssemblies()).Distinct();
list.AddRange(deps.Where(a=>a.Name.EndsWith(".oqtane",StringComparison.OrdinalIgnoreCase)).Select(a=>a.Name));
return list;
}
// POST api/<controller>?moduleid=x // POST api/<controller>?moduleid=x
[HttpPost] [HttpPost]

View File

@ -8,21 +8,23 @@ using Microsoft.Extensions.Hosting;
using Oqtane.Extensions; using Oqtane.Extensions;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
using Oqtane.Modules; using Oqtane.Modules;
using Oqtane.Services;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.UI;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
{ {
public static class OqtaneServiceCollectionExtensions public static class OqtaneServiceCollectionExtensions
{ {
public static IServiceCollection AddOqtaneParts(this IServiceCollection services) public static IServiceCollection AddOqtaneParts(this IServiceCollection services, Runtime runtime)
{ {
LoadAssemblies(); LoadAssemblies();
services.AddOqtaneServices(); services.AddOqtaneServices(runtime);
return services; return services;
} }
private static IServiceCollection AddOqtaneServices(this IServiceCollection services) private static IServiceCollection AddOqtaneServices(this IServiceCollection services, Runtime runtime)
{ {
if (services is null) if (services is null)
{ {
@ -59,6 +61,13 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
startup.ConfigureServices(services); startup.ConfigureServices(services);
} }
if (runtime == Runtime.Server)
{
assembly.GetInstances<IClientStartup>()
.ToList()
.ForEach(x => x.ConfigureServices(services));
}
} }
return services; return services;
} }

View File

@ -20,6 +20,7 @@ using Oqtane.Repository;
using Oqtane.Security; using Oqtane.Security;
using Oqtane.Services; using Oqtane.Services;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.UI;
namespace Oqtane namespace Oqtane
{ {
@ -27,6 +28,7 @@ namespace Oqtane
{ {
public IConfigurationRoot Configuration { get; } public IConfigurationRoot Configuration { get; }
private string _webRoot; private string _webRoot;
private Runtime _runtime;
public Startup(IWebHostEnvironment env) public Startup(IWebHostEnvironment env)
{ {
@ -34,6 +36,9 @@ namespace Oqtane
.SetBasePath(env.ContentRootPath) .SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
Configuration = builder.Build(); Configuration = builder.Build();
_runtime = (Configuration.GetSection("Runtime").Value == "WebAssembly") ? Runtime.WebAssembly : Runtime.Server;
_webRoot = env.WebRootPath; _webRoot = env.WebRootPath;
AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(env.ContentRootPath, "Data")); AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(env.ContentRootPath, "Data"));
} }
@ -189,7 +194,7 @@ namespace Oqtane
services.AddTransient<IUpgradeManager, UpgradeManager>(); services.AddTransient<IUpgradeManager, UpgradeManager>();
// load the external assemblies into the app domain, install services // load the external assemblies into the app domain, install services
services.AddOqtaneParts(); services.AddOqtaneParts(_runtime);
services.AddMvc() services.AddMvc()
.AddNewtonsoftJson() .AddNewtonsoftJson()

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Oqtane.Services;
using Oqtane.Shared; using Oqtane.Shared;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
@ -35,6 +36,11 @@ namespace System.Reflection
.Where(x => interfaceType.IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract); .Where(x => interfaceType.IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract);
} }
public static IEnumerable<Type> GetTypes<T>(this Assembly assembly)
{
return assembly.GetTypes(typeof(T));
}
public static IEnumerable<T> GetInstances<T>(this Assembly assembly) where T : class public static IEnumerable<T> GetInstances<T>(this Assembly assembly) where T : class
{ {
if (assembly is null) if (assembly is null)
@ -65,5 +71,10 @@ namespace System.Reflection
{ {
return appDomain.GetAssemblies().Where(a => a.IsOqtaneAssembly()); return appDomain.GetAssemblies().Where(a => a.IsOqtaneAssembly());
} }
public static IEnumerable<Assembly> GetOqtaneClientAssemblies(this AppDomain appDomain)
{
return appDomain.GetOqtaneAssemblies()
.Where(a => a.GetTypes<IClientStartup>().Any());
}
} }
} }

View File

@ -0,0 +1,11 @@

using Microsoft.Extensions.DependencyInjection;
namespace Oqtane.Services
{
public interface IClientStartup
{
// This method gets called by the runtime. Use this method to add services to the container.
void ConfigureServices(IServiceCollection services);
}
}

View File

@ -18,6 +18,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.2" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
<PackageReference Include="System.Text.Json" Version="4.7.1" /> <PackageReference Include="System.Text.Json" Version="4.7.1" />
</ItemGroup> </ItemGroup>