diff --git a/demo/WasmHost/Pages/Counter.razor b/demo/WasmHost/Pages/Counter.razor
index 7ad43fd..d5f2336 100644
--- a/demo/WasmHost/Pages/Counter.razor
+++ b/demo/WasmHost/Pages/Counter.razor
@@ -9,11 +9,18 @@
-
+
+
+
+
+ Component loading errored...
+
-
-
+
+
+
+
@code {
diff --git a/nuget/BlazorLazyLoading.Components/BlazorLazyLoading.Components.csproj b/nuget/BlazorLazyLoading.Components/BlazorLazyLoading.Components.csproj
index 6dc1cf6..ff65dbf 100644
--- a/nuget/BlazorLazyLoading.Components/BlazorLazyLoading.Components.csproj
+++ b/nuget/BlazorLazyLoading.Components/BlazorLazyLoading.Components.csproj
@@ -3,6 +3,10 @@
netstandard2.1
3.0
+ 8.0
+ enable
+ nullable
+ true
diff --git a/nuget/BlazorLazyLoading.Server/BlazorLazyLoading.Server.csproj b/nuget/BlazorLazyLoading.Server/BlazorLazyLoading.Server.csproj
index ac0c216..bb76df5 100644
--- a/nuget/BlazorLazyLoading.Server/BlazorLazyLoading.Server.csproj
+++ b/nuget/BlazorLazyLoading.Server/BlazorLazyLoading.Server.csproj
@@ -5,7 +5,8 @@
Library
8.0
enable
- true
+ nullable
+ true
diff --git a/nuget/BlazorLazyLoading.Server/StartupExtensions.cs b/nuget/BlazorLazyLoading.Server/StartupExtensions.cs
index 8eda079..6e42716 100644
--- a/nuget/BlazorLazyLoading.Server/StartupExtensions.cs
+++ b/nuget/BlazorLazyLoading.Server/StartupExtensions.cs
@@ -11,16 +11,30 @@
namespace BlazorLazyLoading.Server
{
+ ///
+ /// Server startup extensions for BlazorLazyLoading
+ ///
public static class BLLServerStartupExtensions
{
+ ///
+ /// Registers BlazorLazyLoading services
+ ///
public static IServiceCollection AddLazyLoading(
this IServiceCollection services,
LazyLoadingOptions options)
{
- services.AddScoped();
+ if (options.UseAssemblyIsolation)
+ {
+ services.AddScoped();
+ }
+ else
+ {
+ services.AddSingleton();
+ }
+
services.AddSingleton();
- services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton(typeof(IAssemblyDataLocator), options.AssemblyDataLocator ?? typeof(AssemblyDataLocator));
services.AddSingleton(
p =>
@@ -38,6 +52,9 @@ public static IServiceCollection AddLazyLoading(
return services;
}
+ ///
+ /// Configures the host to use BlazorLazyLoading
+ ///
public static void UseLazyLoading(
this IApplicationBuilder app)
{
@@ -53,13 +70,26 @@ public static void UseLazyLoading(
}
}
+ ///
+ /// BlazorLazyLoading options
+ ///
public sealed class LazyLoadingOptions
{
///
- /// Specifies a list of Module Names (hints) to:
- /// - Download DLLs from them
- /// - Use their manifest to locate lazy resources
+ /// Specifies a list of Module Names (hints) to download DLLs from them and use their manifest to locate lazy resources
///
public IEnumerable ModuleHints { get; set; } = Array.Empty();
+
+ ///
+ ///
Configures assembly isolation level. Do NOT set this to 'false' unless you want to share 'static' fields between users.
+ ///
Keeping this enabled ensures that the server can be scaled horizontally.
+ ///
default: true
+ ///
+ public bool UseAssemblyIsolation { get; set; } = true;
+
+ ///
+ /// Configures a custom AssemblyDataLocator. The type must implement IAssemblyDataLocator.
+ ///
+ public Type? AssemblyDataLocator { get; set; } = null;
}
}
diff --git a/nuget/BlazorLazyLoading.Wasm/BlazorLazyLoading.Wasm.csproj b/nuget/BlazorLazyLoading.Wasm/BlazorLazyLoading.Wasm.csproj
index 998f853..22aecb4 100644
--- a/nuget/BlazorLazyLoading.Wasm/BlazorLazyLoading.Wasm.csproj
+++ b/nuget/BlazorLazyLoading.Wasm/BlazorLazyLoading.Wasm.csproj
@@ -4,7 +4,8 @@
netstandard2.1
8.0
enable
- true
+ nullable
+ true
diff --git a/nuget/BlazorLazyLoading.Wasm/StartupExtensions.cs b/nuget/BlazorLazyLoading.Wasm/StartupExtensions.cs
index d5bd9ee..d88b75d 100644
--- a/nuget/BlazorLazyLoading.Wasm/StartupExtensions.cs
+++ b/nuget/BlazorLazyLoading.Wasm/StartupExtensions.cs
@@ -7,15 +7,21 @@
namespace BlazorLazyLoading.Wasm
{
+ ///
+ /// WebAssembly startup extensions for BlazorLazyLoading
+ ///
public static class BLLWasmStartupExtensions
{
+ ///
+ /// Registers BlazorLazyLoading services
+ ///
public static IServiceCollection AddLazyLoading(
this IServiceCollection services,
LazyLoadingOptions options)
{
services.AddSingleton();
services.AddSingleton();
- services.AddSingleton();
+ services.AddSingleton(typeof(IAssemblyDataLocator), options.AssemblyDataLocator ?? typeof(AssemblyDataLocator));
services.AddSingleton();
services.AddSingleton();
@@ -29,13 +35,19 @@ public static IServiceCollection AddLazyLoading(
}
}
+ ///
+ /// BlazorLazyLoading options
+ ///
public sealed class LazyLoadingOptions
{
///
- /// Specifies a list of Module Names (hints) to:
- /// - Download DLLs from them
- /// - Use their manifest to locate lazy resources
+ /// Specifies a list of Module Names (hints) to download DLLs from them and use their manifest to locate lazy resources
///
public IEnumerable ModuleHints { get; set; } = Array.Empty();
+
+ ///
+ /// Configures a custom AssemblyDataLocator. The type must implement IAssemblyDataLocator.
+ ///
+ public Type? AssemblyDataLocator { get; set; } = null;
}
}
diff --git a/src/AssemblyLoader.Server/AssemblyLoader.Server.csproj b/src/AssemblyLoader.Server/AssemblyLoader.Server.csproj
index 89f0568..a889bae 100644
--- a/src/AssemblyLoader.Server/AssemblyLoader.Server.csproj
+++ b/src/AssemblyLoader.Server/AssemblyLoader.Server.csproj
@@ -5,7 +5,8 @@
Library
8.0
enable
- true
+ nullable
+ true
diff --git a/src/AssemblyLoader.Wasm/AssemblyLoader.Wasm.csproj b/src/AssemblyLoader.Wasm/AssemblyLoader.Wasm.csproj
index 3a9f085..01771f8 100644
--- a/src/AssemblyLoader.Wasm/AssemblyLoader.Wasm.csproj
+++ b/src/AssemblyLoader.Wasm/AssemblyLoader.Wasm.csproj
@@ -4,7 +4,8 @@
netstandard2.1
8.0
enable
- true
+ nullable
+ true
diff --git a/src/AssemblyLoader.Wasm/Services/AppDomainAssemblyLoadContext.cs b/src/AssemblyLoader.Wasm/Services/AppDomainAssemblyLoadContext.cs
index e8f6fc5..bfd6ec7 100644
--- a/src/AssemblyLoader.Wasm/Services/AppDomainAssemblyLoadContext.cs
+++ b/src/AssemblyLoader.Wasm/Services/AppDomainAssemblyLoadContext.cs
@@ -9,7 +9,7 @@
namespace BlazorLazyLoading.Wasm.Services
{
- public sealed class AppDomainAssemblyLoadContext : IAssemblyLoadContext
+ internal sealed class AppDomainAssemblyLoadContext : IAssemblyLoadContext
{
private readonly object _domainLock = new object();
private readonly AppDomain _baseDomain;
diff --git a/src/AssemblyLoader/Abstractions/IAssemblyDataLocator.cs b/src/AssemblyLoader/Abstractions/IAssemblyDataLocator.cs
index 87c5154..8440d4d 100644
--- a/src/AssemblyLoader/Abstractions/IAssemblyDataLocator.cs
+++ b/src/AssemblyLoader/Abstractions/IAssemblyDataLocator.cs
@@ -4,10 +4,13 @@
namespace BlazorLazyLoading.Abstractions
{
+ ///
+ /// Locates assembly DLLs based on context and hints
+ ///
public interface IAssemblyDataLocator
{
///
- /// Returns a list of possible paths where the assembly data is
+ /// Returns a list of possible paths to look for the assembly data (dll)
///
public IEnumerable GetFindPaths(AssemblyName assemblyName, AssemblyLoaderContext context);
}
diff --git a/src/AssemblyLoader/AssemblyLoader.csproj b/src/AssemblyLoader/AssemblyLoader.csproj
index 762f34f..d03d89d 100644
--- a/src/AssemblyLoader/AssemblyLoader.csproj
+++ b/src/AssemblyLoader/AssemblyLoader.csproj
@@ -4,7 +4,8 @@
netstandard2.1
8.0
enable
- true
+ nullable
+ true
diff --git a/src/AssemblyLoader/Comparers/AssemblyByNameAndVersionComparer.cs b/src/AssemblyLoader/Comparers/AssemblyByNameAndVersionComparer.cs
index 9566e5e..023013a 100644
--- a/src/AssemblyLoader/Comparers/AssemblyByNameAndVersionComparer.cs
+++ b/src/AssemblyLoader/Comparers/AssemblyByNameAndVersionComparer.cs
@@ -1,6 +1,5 @@
using System;
using System.Reflection;
-using BlazorLazyLoading.Helpers;
namespace BlazorLazyLoading.Comparers
{
diff --git a/src/AssemblyLoader/Extensions/EnumerableExtensions.cs b/src/AssemblyLoader/Extensions/EnumerableExtensions.cs
new file mode 100644
index 0000000..f8db812
--- /dev/null
+++ b/src/AssemblyLoader/Extensions/EnumerableExtensions.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace BlazorLazyLoading.Extensions
+{
+ public static class EnumerableExtensions
+ {
+ public static IEnumerable NotNull(
+ this IEnumerable source)
+ where T : class
+ {
+ return source.Where(i => i != null).Cast();
+ }
+
+ public static IEnumerable DistinctBy(
+ this IEnumerable source,
+ Func selector)
+ {
+ return source
+ .GroupBy(m => selector(m))
+ .Select(g => g.First());
+ }
+ }
+}
diff --git a/src/AssemblyLoader/Helpers/HashCode.cs b/src/AssemblyLoader/Helpers/HashCode.cs
deleted file mode 100644
index 8a6fdb0..0000000
--- a/src/AssemblyLoader/Helpers/HashCode.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Collections.Generic;
-
-namespace BlazorLazyLoading.Helpers
-{
- //public static class HashCode
- //{
- // public static int Combine(params object[] instances)
- // {
- // int hash = 17;
-
- // foreach (var i in instances)
- // {
- // hash = unchecked((hash * 31) + (i?.GetHashCode() ?? 0));
- // }
-
- // return hash;
- // }
- //}
-}
diff --git a/src/AssemblyLoader/Models/AssemblyData.cs b/src/AssemblyLoader/Models/AssemblyData.cs
index 57620d9..5223bc9 100644
--- a/src/AssemblyLoader/Models/AssemblyData.cs
+++ b/src/AssemblyLoader/Models/AssemblyData.cs
@@ -1,10 +1,17 @@
namespace BlazorLazyLoading.Models
{
+ ///
+ /// Contains the data required to load an assembly
+ ///
public sealed class AssemblyData
{
+ /// Bytes from the DLL
public readonly byte[] DllBytes;
+
+ /// Bytes from the PDB
public readonly byte[]? PdbBytes;
+ /// Constructs AssemblyData
public AssemblyData(
byte[] dllBytes,
byte[]? pdbBytes)
diff --git a/src/AssemblyLoader/Models/AssemblyLoaderContext.cs b/src/AssemblyLoader/Models/AssemblyLoaderContext.cs
index fe11308..287ee59 100644
--- a/src/AssemblyLoader/Models/AssemblyLoaderContext.cs
+++ b/src/AssemblyLoader/Models/AssemblyLoaderContext.cs
@@ -3,13 +3,23 @@
namespace BlazorLazyLoading.Models
{
+ ///
+ /// Describes the tree of loaded assembly dependencies
+ ///
public sealed class AssemblyLoaderContext
{
+ ///
+ /// Assembly Name
+ ///
public readonly AssemblyName AssemblyName;
+ /// Parent Node
public readonly AssemblyLoaderContext? Parent = null;
+
+ /// Children Nodes
public readonly List Children = new List();
+ /// Constructs the AssemblyLoaderContext
public AssemblyLoaderContext(AssemblyName name)
{
AssemblyName = name;
@@ -21,6 +31,9 @@ private AssemblyLoaderContext(AssemblyName name, AssemblyLoaderContext parent)
AssemblyName = name;
}
+ ///
+ /// Creates a new scope for the current AssemblyLoaderContext
+ ///
public AssemblyLoaderContext NewScope(AssemblyName name)
{
var scope = new AssemblyLoaderContext(name, this);
diff --git a/src/AssemblyLoader/Services/AssemblyDataLocator.cs b/src/AssemblyLoader/Services/AssemblyDataLocator.cs
index 41812e6..a3a721f 100644
--- a/src/AssemblyLoader/Services/AssemblyDataLocator.cs
+++ b/src/AssemblyLoader/Services/AssemblyDataLocator.cs
@@ -5,16 +5,21 @@
namespace BlazorLazyLoading.Services
{
+ ///
+ /// Locates assembly DLLs based on context and hints
+ ///
public sealed class AssemblyDataLocator : IAssemblyDataLocator
{
private readonly ILazyModuleHintsProvider _lazyModuleNamesProvider;
+ ///
public AssemblyDataLocator(
ILazyModuleHintsProvider lazyModuleProvider)
{
_lazyModuleNamesProvider = lazyModuleProvider;
}
+ ///
public IEnumerable GetFindPaths(
AssemblyName assemblyName,
AssemblyLoaderContext context)
diff --git a/src/LazyComponents/LazyComponent/Lazy.cs b/src/LazyComponents/LazyComponent/Lazy.cs
index 42a2185..f757813 100644
--- a/src/LazyComponents/LazyComponent/Lazy.cs
+++ b/src/LazyComponents/LazyComponent/Lazy.cs
@@ -1,10 +1,10 @@
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using BlazorLazyLoading.Abstractions;
+using BlazorLazyLoading.Extensions;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Newtonsoft.Json.Linq;
@@ -17,7 +17,13 @@ public class Lazy : ComponentBase
public string Name { get; set; } = null!;
[Parameter]
- public RenderFragment? ChildContent { get; set; } = null;
+ public bool Required { get; set; } = false;
+
+ [Parameter]
+ public RenderFragment? Loading { get; set; } = null;
+
+ [Parameter]
+ public RenderFragment? Error { get; set; } = null;
[Parameter]
public Func? OnBeforeLoadAsync { get; set; } = null;
@@ -35,10 +41,23 @@ public class Lazy : ComponentBase
[Inject]
private IManifestRepository _manifestRepository { get; set; } = null!;
+ private RenderFragment? _currentFallbackBuilder = null;
+
+ protected override void OnInitialized()
+ {
+ _currentFallbackBuilder = Loading;
+ base.OnInitialized(); // trigger initial render
+ }
+
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync().ConfigureAwait(false);
+ if (OnBeforeLoadAsync != null)
+ {
+ await OnBeforeLoadAsync(this);
+ }
+
var allManifests = await _manifestRepository.GetAllAsync().ConfigureAwait(false);
var manifests = allManifests
@@ -71,6 +90,7 @@ protected override async Task OnInitializedAsync()
}));
var bestMatches = manifests
+ .Where(i => i.Score > 0)
.GroupBy(i => i.Score)
.OrderByDescending(i => i.Key)
.FirstOrDefault()
@@ -78,13 +98,16 @@ protected override async Task OnInitializedAsync()
if (bestMatches == null || !bestMatches.Any())
{
- Debug.WriteLine($"Unable to find lazy component '{Name}'");
+ DisplayErrorView();
+ ThrowIfRequired($"Unable to find lazy component '{Name}'. Required: {(Required ? "true" : "false")}");
return;
}
if (bestMatches.Count > 1)
{
- throw new NotSupportedException($"Multiple matches for Component with name '{Name}': '{string.Join(";", bestMatches.Select(m => m.Match.TypeFullName))}'");
+ DisplayErrorView();
+ ThrowIfRequired($"Multiple matches for Component with name '{Name}': '{string.Join(";", bestMatches.Select(m => m.Match.TypeFullName))}'");
+ return;
}
var bestMatch = bestMatches.First();
@@ -97,12 +120,13 @@ protected override async Task OnInitializedAsync()
})
.ConfigureAwait(false);
- if (OnBeforeLoadAsync != null)
+ Type = componentAssembly?.GetType(bestMatch.Match.TypeFullName);
+
+ if (Type == null)
{
- await OnBeforeLoadAsync(this);
+ DisplayErrorView(false);
}
- Type = componentAssembly?.GetType(bestMatch.Match.TypeFullName);
StateHasChanged();
}
@@ -125,34 +149,25 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
private void BuildFallbackComponent(RenderTreeBuilder builder)
{
- if (ChildContent != null)
- {
- ChildContent.Invoke(builder);
- return;
- }
-
- builder.OpenElement(0, "div");
- builder.AddAttribute(1, "class", "bll-loading");
- builder.AddContent(2, "Loading...");
- builder.CloseElement();
+ _currentFallbackBuilder?.Invoke(builder);
}
- }
- public static class X
- {
- public static IEnumerable NotNull(this IEnumerable source)
- where T : class
+ private void DisplayErrorView(bool render = true)
{
- return source.Where(i => i != null).Cast();
+ _currentFallbackBuilder = Error;
+ if (render) StateHasChanged();
}
- public static IEnumerable DistinctBy(
- this IEnumerable source,
- Func selector)
+ private void ThrowIfRequired(string errorMessage)
{
- return source
- .GroupBy(m => selector(m))
- .Select(g => g.First());
+ if (Required)
+ {
+ throw new NotSupportedException(errorMessage);
+ }
+ else
+ {
+ Debug.WriteLine(errorMessage);
+ }
}
}
}
diff --git a/src/LazyComponents/LazyComponents.csproj b/src/LazyComponents/LazyComponents.csproj
index c9447c9..641bc3e 100644
--- a/src/LazyComponents/LazyComponents.csproj
+++ b/src/LazyComponents/LazyComponents.csproj
@@ -4,7 +4,8 @@
netstandard2.1
8.0
enable
- true
+ nullable
+ true
diff --git a/src/LazyComponents/LazyRoute/LazyRouter.cs b/src/LazyComponents/LazyRoute/LazyRouter.cs
index c0b9261..abf5a65 100644
--- a/src/LazyComponents/LazyRoute/LazyRouter.cs
+++ b/src/LazyComponents/LazyRoute/LazyRouter.cs
@@ -5,6 +5,7 @@
using System.Reflection;
using System.Threading.Tasks;
using BlazorLazyLoading.Abstractions;
+using BlazorLazyLoading.Extensions;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.Extensions.Logging;
diff --git a/src/ManifestGenerator/ManifestGenerator.csproj b/src/ManifestGenerator/ManifestGenerator.csproj
index 17bfa71..d2ee05c 100644
--- a/src/ManifestGenerator/ManifestGenerator.csproj
+++ b/src/ManifestGenerator/ManifestGenerator.csproj
@@ -4,7 +4,8 @@
netstandard2.0
8.0
enable
- true
+ nullable
+ true
diff --git a/src/ManifestReader/Abstractions/IManifestLocator.cs b/src/ManifestReader/Abstractions/IManifestLocator.cs
index 5953775..b021736 100644
--- a/src/ManifestReader/Abstractions/IManifestLocator.cs
+++ b/src/ManifestReader/Abstractions/IManifestLocator.cs
@@ -2,6 +2,9 @@
namespace BlazorLazyLoading.Abstractions
{
+ ///
+ /// Locates _lazy.json manifests
+ ///
public interface IManifestLocator
{
///
diff --git a/src/ManifestReader/ManifestReader.csproj b/src/ManifestReader/ManifestReader.csproj
index a1afc28..275aead 100644
--- a/src/ManifestReader/ManifestReader.csproj
+++ b/src/ManifestReader/ManifestReader.csproj
@@ -4,7 +4,8 @@
netstandard2.1
8.0
enable
- true
+ nullable
+ true
diff --git a/src/ManifestReader/Services/ManifestLocator.cs b/src/ManifestReader/Services/ManifestLocator.cs
index 9787ef9..22f0274 100644
--- a/src/ManifestReader/Services/ManifestLocator.cs
+++ b/src/ManifestReader/Services/ManifestLocator.cs
@@ -4,16 +4,21 @@
namespace BlazorLazyLoading.Services
{
+ ///
+ /// Locates _lazy.json manifests based on hints
+ ///
public sealed class ManifestLocator : IManifestLocator
{
private readonly ILazyModuleHintsProvider _moduleHintsProvider;
+ ///
public ManifestLocator(
ILazyModuleHintsProvider moduleHintsProvider)
{
_moduleHintsProvider = moduleHintsProvider;
}
+ ///
public IEnumerable GetManifestPaths()
{
return _moduleHintsProvider.ModuleNameHints
diff --git a/src/ManifestReader/Services/ManifestRepository.cs b/src/ManifestReader/Services/ManifestRepository.cs
index afa5275..00043a6 100644
--- a/src/ManifestReader/Services/ManifestRepository.cs
+++ b/src/ManifestReader/Services/ManifestRepository.cs
@@ -4,6 +4,7 @@
using System.Text;
using System.Threading.Tasks;
using BlazorLazyLoading.Abstractions;
+using BlazorLazyLoading.Extensions;
using BlazorLazyLoading.Models;
using Newtonsoft.Json.Linq;
@@ -70,7 +71,7 @@ private async Task FetchAllManifests()
private async Task ReadAllManifests()
{
var manifestDataTasks = new Dictionary>();
- var manifestPaths = _manifestLocator.GetManifestPaths();
+ var manifestPaths = _manifestLocator.GetManifestPaths().Except(_loadedPaths.Keys);
foreach (var path in manifestPaths)
{
@@ -79,11 +80,16 @@ private async Task ReadAllManifests()
await Task.WhenAll(manifestDataTasks.Values.ToArray()).ConfigureAwait(false);
- var manifestData = manifestDataTasks
+ var pathData = manifestDataTasks
.Select(i => new { Path = i.Key, Data = i.Value.Result })
.ToList();
- var manifestModels = manifestData
+ foreach (var path in pathData)
+ {
+ _loadedPaths.TryAdd(path.Path, path.Data);
+ }
+
+ var manifestModels = pathData
.Where(i => i.Data != null)
.ToDictionary(i => i.Path, i => JObject.Parse(Encoding.UTF8.GetString(i.Data!)));