Skip to content
This repository has been archived by the owner on Sep 4, 2023. It is now read-only.

Assembly loading improvements #2

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
Expand Down
13 changes: 7 additions & 6 deletions samples/CG.Blazor.Plugins.QuickStart/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@
}
},
"AllowedHosts": "*",
"Plugins.1": {
"Plugins": {
"Modules": [
{
"AssemblyNameOrPath": "../CG.Blazor.Plugins.TestPlugin/bin/Debug/net6.0/CG.Blazor.Plugins.TestPlugin.dll",
"AssemblyNameOrPath.1": "CG.Blazor.Plugins.TestPlugin",
"AssemblyNameOrPath.1": "C:/CG.Blazor.Plugins/samples/CG.Blazor.Plugins.TestPlugin/bin/Debug/net6.0/CG.Blazor.Plugins.TestPlugin.dll",
"AssemblyNameOrPath.2": "CG.Blazor.Plugins.TestPlugin",
"IsDisabled": false,
"IsRouted": true
"IsRouted": true,
"Scripts": [
"/scripts/utility.js"
]
}
]
},
"Plugins": {
"Modules": []
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
<PropertyGroup>
<Nullable>enable</Nullable>
<TargetFramework>net6.0</TargetFramework>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>

<ItemGroup>
<EmbeddedResource Include="wwwroot\**\*" />
</ItemGroup>


<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>
Expand All @@ -17,10 +22,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="6.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
</ItemGroup>

<ItemGroup>
<Folder Include="wwwroot\" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="7.0.2" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<h4 class="pb-5">This page is contained within a Blazor plugin!</h4>

<p>Don't believe me? Go set <code>IsEnabled</code> property to false, in the <code>Plugins:Modules</code> section of the <code>appsettings.json</code> file, and restart the app. You won't be able to get to this page then ...</p>
<p>Don't believe me? Go set <code>IsDisabled</code> property to false, in the <code>Plugins:Modules</code> section of the <code>appsettings.json</code> file, and restart the app. You won't be able to get to this page then ...</p>

<p class="pt-5 muted-text">Dependent Class: <code>@typeof(CG.Blazor.Plugins.TestPlugin.Dependency.Class1).FullName</code> was resolved by the plugin loader, even though the <code>Assembly.Load</code> method initially failed to find the assembly in the <code>@typeof(CG.Blazor.Plugins.TestPlugin.Dependency.Class1).Assembly.Location</code> folder. </p>

Expand Down
124 changes: 124 additions & 0 deletions samples/CG.Blazor.Plugins.TestPlugin/wwwroot/scripts/utility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Generate a random number within a specified range
const getRandomNumberInRange = (lower = 0, upper = 10) => {
if (isNaN(lower) || isNaN(upper)) {
console.error("lower and upper must be valid numbers")
return
}
lower = Math.ceil(lower)
upper = Math.floor(upper)
return Math.floor(Math.random() * (upper - lower + 1)) + lower
}

// Do something x times. e.g. times(3, () => console.log("hello"))
const times = (times, func) => {
if (isNaN(times)) {
console.error("times to run must be specified")
return
}
if (typeof func !== "function") {
console.error(`func must be a valid function, ${typeof func} provided`)
return
}
Array.from(Array(times)).forEach(() => {
func()
})
}

// Shorten a string with ellipsis. e.g. shorten("I am some text", 4, 2) => I am..
const shorten = (text, length = 10, ellipsisCount = 3) => {
if (!(typeof text === "string" || text instanceof String)) {
console.error(`expecting a string, ${typeof text} provided`)
return ""
}
if (isNaN(length) || isNaN(ellipsisCount)) {
console.error("length and ellipsisCount must be valid numbers")
return
}

if (text.length <= length) {
return text
}
const ellipsis = Array.from(Array(ellipsisCount)).map(() => ".").join("")
return `${text.substr(0, length)}${ellipsis}`
}

// Remove duplicates from an array. e.g. removeDuplicates(["Tom", "Simon", "Tom", "Sarah"]) => ["Tom", "Simon", "Sarah"]
const removeDuplicates = (arr) => {
if (!Array.isArray(arr)) {
console.error(`array expected, ${typeof arr} provided`)
return
}
return [...new Set(arr)]
}

// Debounce (or delay) a function
const debounce = (func, timeout = 2500) => {
if (typeof func !== "function") {
console.error(`func must be a valid function, ${typeof func} provided`)
return
}
let timer
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, timeout)
}
}

// Measure the performance/time taken of a function. e.g. measureTime("findPeople", someExpensiveFindPeopleFunction) => findPeople: 13426.336181640625ms
const measureTime = (func, label = "default") => {
if (typeof func !== "function") {
console.error(`func must be a valid function, ${typeof func} provided`)
return
}
console.time(label)
func()
console.timeEnd(label)
}

// Slugify a string. e.g. slugify("Hello, everyone!") => hello-everyone
const slugify = (text) => {
if (!(typeof text === "string" || text instanceof String)) {
console.error(`string expected, ${typeof str} provided`)
return str
}
return text.toLowerCase()
.replace(/ /g, "-")
.replace(/[^\w-]+/g, "")
}

// Camel case to snake case. e.g. camelToSnakeCase("camelCaseToSnakeCase") => camel_case_to_snake_case
const camelToSnakeCase = (text) => {
if (!(typeof text === "string" || text instanceof String)) {
console.error(`string expected, ${typeof text} provided`)
return text
}
return text.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
}

// Snake case to camel case. e.g. snakeToCamelCase("snake_case_to_camel_case") => snakeCaseToCamelCase
const snakeToCamelCase = (text) => {
if (!(typeof text === "string" || text instanceof String)) {
console.error(`string expected, ${typeof text} provided`)
return text
}
text
.toLowerCase()
.replace(/([-_][a-z])/g, group =>
group
.toUpperCase()
.replace("-", "")
.replace("_", "")
)
}

// Validate an email address. e.g. emailIsValid("[email protected]") => true | emailIsValid("nobody@nowhere") => false
const emailIsValid = (email) => {
if (!(typeof email === "string" || email instanceof String)) {
console.error(`string expected, ${typeof email} provided`)
return false
}
const expression = /\S+@\S+\.\S+/
return expression.test(email)
}
5 changes: 1 addition & 4 deletions src/CG.Blazor.Plugins/CG.Blazor.Plugins.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>Martin Cook</Authors>
<Company>CodeGator</Company>
<Description>This package contains server side Blazor plugin extensions used by other CodeGator packages.

Platforms supported:
.NET 6.x or above</Description>
<Description>This package contains server side Blazor plugin extensions used by other CodeGator packages. Platforms supported: .NET 6.x or above</Description>
<Copyright>Copyright © 2018 - 2023 by CodeGator. All rights reserved.</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/CodeGator/CG.Blazor.Plugins</PackageProjectUrl>
Expand Down
82 changes: 35 additions & 47 deletions src/CG.Blazor.Plugins/Extensions/WebApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

using System.IO;

namespace Microsoft.Extensions.DependencyInjection;

/// <summary>
Expand Down Expand Up @@ -115,68 +117,54 @@ out var pluginOptions

// If the AssemblyNameOrPath ends with a .dll then we'll assume the
// property contains a path and treat it as such.
if (module.AssemblyNameOrPath.EndsWith(".dll"))
if (module.AssemblyNameOrPath.EndsWith(".dll", true, null))
{
// Log what we are about to do.
bootstrapLogger?.LogDebug(
"Deciding whether the assembly path is rooted, or " +
"not, for the plugin loader."
);

// Check for relative paths.
if (false == Path.IsPathRooted(module.AssemblyNameOrPath))
try
{
// Log what we are about to do.
bootstrapLogger?.LogDebug(
"Building a complete path to a plugin assembly, " +
"for the plugin loader"
);

// Expand the path (the load expects a rooted path).
var completePath = Path.GetFullPath(
module.AssemblyNameOrPath
);

// Log what we are about to do.
bootstrapLogger?.LogDebug(
"Loading assembly by path: {path}, for the plugin " +
"loader",
completePath
);
"Deciding whether the assembly path is rooted, or " +
"not, for the plugin loader."
);

// Load the assembly from the path.
asm = Assembly.LoadFile(
completePath
);
}
else
{
try
// Check for relative paths.
if (false == Path.IsPathRooted(module.AssemblyNameOrPath) || Path.IsPathFullyQualified(module.AssemblyNameOrPath))
{
// Log what we are about to do.
bootstrapLogger?.LogDebug(
"Loading assembly by name: {name}, for the " +
"plugin loader",
"Building a complete path to a plugin assembly, " +
"for the plugin loader"
);

// Expand the path (the load expects a rooted path).
var completePath = Path.GetFullPath(
module.AssemblyNameOrPath
);

// Load the assembly from the path.
asm = Assembly.Load(
new AssemblyName(module.AssemblyNameOrPath)
// Log what we are about to do.
bootstrapLogger?.LogDebug(
"Loading assembly by path: {path}, for the plugin " +
"loader",
completePath
);
}
catch (FileNotFoundException ex)
{
// Provide better context for the error.
throw new BlazorPluginException(
innerException: ex,
message: "When loading from an assembly name, remember that the " +
"assembly itself must have been loaded through a project reference. " +
"To dynamically load a plugin assembly, use a path to the assembly, " +
"instead of a name."

// Load the assembly from the path.
asm = Assembly.LoadFile(
completePath
);
}
}
catch (FileNotFoundException ex)
{
// Provide better context for the error.
throw new BlazorPluginException(
innerException: ex,
message: "When loading from an assembly name, remember that the " +
"assembly itself must have been loaded through a project reference. " +
"To dynamically load a plugin assembly, use a path to the assembly, " +
"instead of a name."
);
}
}
else
{
Expand Down
Loading