Files
Telegram-Panel/src/TelegramPanel.Web/Modules/ModuleLoadContext.cs
T

74 lines
3.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Reflection;
using System.Runtime.Loader;
namespace TelegramPanel.Web.Modules;
public sealed class ModuleLoadContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
private readonly string _baseDir;
public ModuleLoadContext(string mainAssemblyPath) : base(isCollectible: false)
{
if (string.IsNullOrWhiteSpace(mainAssemblyPath))
throw new ArgumentException("mainAssemblyPath 不能为空", nameof(mainAssemblyPath));
_resolver = new AssemblyDependencyResolver(mainAssemblyPath);
_baseDir = Path.GetDirectoryName(mainAssemblyPath) ?? "";
}
protected override Assembly? Load(AssemblyName assemblyName)
{
var name = (assemblyName.Name ?? "").Trim();
if (name.Length == 0)
return null;
// 1) 与宿主共享的“边界程序集”必须由 Default ALC 解析,避免同名程序集被模块再次加载导致类型身份不一致。
// 典型问题:模块包携带 Microsoft.Extensions.DependencyInjection*.dll / Microsoft.AspNetCore.Components.dll,
// 会让 IServiceCollection / EventCallback 等类型在不同 ALC 中变成“不同类型”,最终出现:
// - TypeLoadException(看起来像模块没有实现 ConfigureServices)
// - 组件参数绑定失败(表现为 DraftChanged 不生效)
if (name.StartsWith("Microsoft.Extensions.", StringComparison.OrdinalIgnoreCase)
|| name.StartsWith("Microsoft.AspNetCore.", StringComparison.OrdinalIgnoreCase)
|| name.StartsWith("Microsoft.JSInterop", StringComparison.OrdinalIgnoreCase)
|| name.StartsWith("TelegramPanel.", StringComparison.OrdinalIgnoreCase)
|| string.Equals(name, "MudBlazor", StringComparison.OrdinalIgnoreCase)
// TelegramPanel 宿主内置依赖:模块可直接复用宿主版本,避免重复携带导致包体积膨胀。
|| string.Equals(name, "WTelegramClient", StringComparison.OrdinalIgnoreCase)
|| string.Equals(name, "PhoneNumbers", StringComparison.OrdinalIgnoreCase)
|| string.Equals(name, "SixLabors.ImageSharp", StringComparison.OrdinalIgnoreCase)
|| string.Equals(name, "Microsoft.Data.Sqlite", StringComparison.OrdinalIgnoreCase)
|| name.StartsWith("Microsoft.EntityFrameworkCore", StringComparison.OrdinalIgnoreCase)
|| name.StartsWith("SQLitePCLRaw", StringComparison.OrdinalIgnoreCase))
return null;
// 2) 其次:如果宿主已经加载过同名程序集,则同样交给 Default ALC(避免重复加载)。
if (AssemblyLoadContext.Default.Assemblies.Any(a =>
string.Equals(a.GetName().Name, name, StringComparison.OrdinalIgnoreCase)))
return null;
var path = _resolver.ResolveAssemblyToPath(assemblyName);
if (!string.IsNullOrWhiteSpace(path) && File.Exists(path))
return LoadFromAssemblyPath(path);
// fallback:如果模块未提供 deps.json,则在 lib 目录按名称寻找
if (name.Length == 0 || string.IsNullOrWhiteSpace(_baseDir))
return null;
var candidate = Path.Combine(_baseDir, name + ".dll");
if (File.Exists(candidate))
return LoadFromAssemblyPath(candidate);
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
var path = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (!string.IsNullOrWhiteSpace(path) && File.Exists(path))
return LoadUnmanagedDllFromPath(path);
return base.LoadUnmanagedDll(unmanagedDllName);
}
}