From b4b73b7e5a5b2a13f72d95a2146e51831e06bf90 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 3 Nov 2020 14:41:49 -0500 Subject: [PATCH] fixed compatibility issue in .NET5/WebAssembly where assemblies were not being loaded into the default AppDomain, optimized service registration on WebAssembly, fixed spelling mistake for satellite assemblies constant and fixed issue in LocalizableComponent --- .../Modules/Controls/LocalizableComponent.cs | 2 +- Oqtane.Client/Program.cs | 49 ++++++++----------- Oqtane.Client/UI/SiteRouter.razor | 4 +- .../Controllers/InstallationController.cs | 14 +++--- .../OqtaneServiceCollectionExtensions.cs | 7 ++- .../Extensions/AssemblyExtensions.cs | 3 +- Oqtane.Shared/Shared/Constants.cs | 4 +- 7 files changed, 38 insertions(+), 45 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/LocalizableComponent.cs b/Oqtane.Client/Modules/Controls/LocalizableComponent.cs index 19e1aa46..d19997bd 100644 --- a/Oqtane.Client/Modules/Controls/LocalizableComponent.cs +++ b/Oqtane.Client/Modules/Controls/LocalizableComponent.cs @@ -18,7 +18,7 @@ namespace Oqtane.Modules.Controls { if (!IsLocalizable) { - return null; + return name; } var key = $"{ResourceKey}.{name}"; diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index d442141d..af283cec 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; @@ -6,6 +6,7 @@ using System.Linq; using System.Net.Http; using System.Reflection; using System.Threading.Tasks; +using System.Runtime.Loader; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.DependencyInjection; @@ -64,30 +65,28 @@ namespace Oqtane.Client await LoadClientAssemblies(httpClient); - // dynamically register module contexts and repository services - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - foreach (Assembly assembly in assemblies) + var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies(); + foreach (var assembly in assemblies) { - var implementationTypes = assembly.GetTypes() - .Where(item => item.GetInterfaces().Contains(typeof(IService))); - - foreach (Type implementationtype in implementationTypes) + // dynamically register module services + var implementationTypes = assembly.GetInterfaces(); + foreach (var implementationType in implementationTypes) { - Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name)); - if (servicetype != null) + if (implementationType.AssemblyQualifiedName != null) { - builder.Services.AddScoped(servicetype, implementationtype); // traditional service interface - } - else - { - builder.Services.AddScoped(implementationtype, implementationtype); // no interface defined for service + var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}")); + builder.Services.AddScoped(serviceType ?? implementationType, implementationType); } } - assembly.GetInstances() - .ToList() - .ForEach(x => x.ConfigureServices(builder.Services)); + // register client startup services + var startUps = assembly.GetInstances(); + foreach (var startup in startUps) + { + startup.ConfigureServices(builder.Services); + } } + var host = builder.Build(); ServiceActivator.Configure(host.Services); @@ -120,15 +119,7 @@ namespace Oqtane.Client switch (Path.GetExtension(entry.Name)) { case ".dll": - // Loads the stallite assemblies early - if (entry.Name.EndsWith(Constants.StalliteAssemblyExtension)) - { - Assembly.Load(file); - } - else - { - dlls.Add(entry.Name, file); - } + dlls.Add(entry.Name, file); break; case ".pdb": pdbs.Add(entry.Name, file); @@ -142,11 +133,11 @@ namespace Oqtane.Client { if (pdbs.ContainsKey(item.Key)) { - Assembly.Load(item.Value, pdbs[item.Key]); + AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value), new MemoryStream(pdbs[item.Key])); } else { - Assembly.Load(item.Value); + AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value)); } } } diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index ae34d6f8..cfbed1fe 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -1,4 +1,4 @@ -@using System.Diagnostics.CodeAnalysis +@using System.Diagnostics.CodeAnalysis @using System.Runtime.InteropServices @namespace Oqtane.UI @inject AuthenticationStateProvider AuthenticationStateProvider @@ -442,7 +442,7 @@ var paneindex = new Dictionary(); foreach (Module module in modules) { - if (module.PageId == page.PageId || module.ModuleId == moduleid) + if ((module.PageId == page.PageId || module.ModuleId == moduleid) && module.ModuleDefinition != null) { var typename = string.Empty; if (module.ModuleDefinition != null && (module.ModuleDefinition.Runtimes == "" || module.ModuleDefinition.Runtimes.Contains(GetRuntime().ToString()))) diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index 73998b13..39b00b2f 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Reflection; using System.Linq; @@ -71,12 +71,12 @@ namespace Oqtane.Controllers { if (_config.GetSection("Runtime").Value == "WebAssembly") { - // get list of assemblies which should be downloaded to browser + // get list of assemblies which should be downloaded to client var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies(); var list = assemblies.Select(a => a.GetName().Name).ToList(); var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - // Get the satellite assemblies + // insert satellite assemblies at beginning of list foreach (var culture in _localizationManager.GetSupportedCultures()) { var assembliesFolderPath = Path.Combine(binFolder, culture); @@ -89,7 +89,7 @@ namespace Oqtane.Controllers { foreach (var resourceFile in Directory.EnumerateFiles(assembliesFolderPath)) { - list.Add(Path.Combine(culture, Path.GetFileNameWithoutExtension(resourceFile))); + list.Insert(0, Path.Combine(culture, Path.GetFileNameWithoutExtension(resourceFile))); } } else @@ -98,7 +98,7 @@ namespace Oqtane.Controllers } } - // get module and theme dependencies + // insert module and theme dependencies at beginning of list foreach (var assembly in assemblies) { foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModule)))) @@ -106,7 +106,7 @@ namespace Oqtane.Controllers var instance = Activator.CreateInstance(type) as IModule; foreach (string name in instance.ModuleDefinition.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { - if (!list.Contains(name)) list.Add(name); + if (!list.Contains(name)) list.Insert(0, name); } } foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ITheme)))) @@ -114,7 +114,7 @@ namespace Oqtane.Controllers var instance = Activator.CreateInstance(type) as ITheme; foreach (string name in instance.Theme.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { - if (!list.Contains(name)) list.Add(name); + if (!list.Contains(name)) list.Insert(0, name); } } } diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index 540f399c..3afb023b 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using System.Reflection; @@ -32,6 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection } var hostedServiceType = typeof(IHostedService); + var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies(); foreach (var assembly in assemblies) { @@ -56,6 +57,7 @@ namespace Microsoft.Extensions.DependencyInjection } } + // register server startup services var startUps = assembly.GetInstances(); foreach (var startup in startUps) { @@ -64,6 +66,7 @@ namespace Microsoft.Extensions.DependencyInjection if (runtime == Runtime.Server) { + // register client startup services if running on server assembly.GetInstances() .ToList() .ForEach(x => x.ConfigureServices(services)); @@ -143,7 +146,7 @@ namespace Microsoft.Extensions.DependencyInjection var assembliesFolder = new DirectoryInfo(Path.Combine(assemblyPath, culture)); if (assembliesFolder.Exists) { - foreach (var assemblyFile in assembliesFolder.EnumerateFiles(Constants.StalliteAssemblyExtension)) + foreach (var assemblyFile in assembliesFolder.EnumerateFiles(Constants.SatelliteAssemblyExtension)) { AssemblyName assemblyName; try diff --git a/Oqtane.Shared/Extensions/AssemblyExtensions.cs b/Oqtane.Shared/Extensions/AssemblyExtensions.cs index 74ae7070..c6b39224 100644 --- a/Oqtane.Shared/Extensions/AssemblyExtensions.cs +++ b/Oqtane.Shared/Extensions/AssemblyExtensions.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using Oqtane.Modules; @@ -34,7 +34,6 @@ namespace System.Reflection } return assembly.GetTypes() - //.Where(t => t.GetInterfaces().Contains(interfaceType)); .Where(x => !x.IsInterface && !x.IsAbstract && interfaceType.IsAssignableFrom(x)); } diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index c065f347..5680df02 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; namespace Oqtane.Shared { @@ -68,7 +68,7 @@ namespace Oqtane.Shared { }; public static readonly string[] InvalidFileNameEndingChars = { ".", " " }; - public static readonly string StalliteAssemblyExtension = ".resources.dll"; + public static readonly string SatelliteAssemblyExtension = ".resources.dll"; public static readonly string DefaultCulture = CultureInfo.InstalledUICulture.Name; }