你这个需求「ASP.NET Core 插件开发 + DLL 热加载」本质上就是做一个 可扩展插件系统,类似于 CMS、框架里那种“外部程序集即插即用”,并且支持在运行时加载/卸载 DLL,不重启应用。下面我帮你整理下第二阶段(进阶版)的思路和方案。
一、插件系统的核心问题
- 插件发现:如何找到可用的 DLL(比如放在
/plugins
文件夹下)。 - 插件隔离:如何避免插件之间类型冲突,如何防止卸载失败(.NET Core 中 Assembly 默认不能卸载)。
- 插件加载:如何把 DLL 动态加载到 ASP.NET Core 容器里。
- 热更新:如何替换旧 DLL,加载新 DLL,并能释放旧 DLL 占用。
二、ASP.NET Core 插件热加载实现路径
1. 使用 AssemblyLoadContext
- 在 .NET Core 里,默认的 AssemblyLoadContext (ALC) 无法卸载程序集。
- 解决方案:使用 自定义 ALC(可卸载),这样卸载插件时整个 ALC 被回收,DLL 文件才能释放。
示例代码:
public class PluginLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
public PluginLoadContext(string pluginPath) : base(isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}
protected override Assembly Load(AssemblyName assemblyName)
{
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
return LoadFromAssemblyPath(assemblyPath);
}
return null;
}
}
2. 插件接口定义
定义一个插件标准接口,插件必须实现:
public interface IPlugin
{
string Name { get; }
Task ExecuteAsync(HttpContext context);
}
3. 动态加载插件
加载插件:
public class PluginManager
{
private readonly Dictionary<string, (PluginLoadContext, IPlugin)> _plugins = new();
public void LoadPlugin(string pluginDllPath)
{
var loadContext = new PluginLoadContext(pluginDllPath);
var assembly = loadContext.LoadFromAssemblyPath(pluginDllPath);
var pluginType = assembly.GetTypes()
.FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract);
if (pluginType != null)
{
var plugin = (IPlugin)Activator.CreateInstance(pluginType);
_plugins[plugin.Name] = (loadContext, plugin);
}
}
public void UnloadPlugin(string name)
{
if (_plugins.TryGetValue(name, out var entry))
{
_plugins.Remove(name);
entry.Item1.Unload(); // 卸载 ALC
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
public async Task ExecutePlugin(string name, HttpContext context)
{
if (_plugins.TryGetValue(name, out var entry))
{
await entry.Item2.ExecuteAsync(context);
}
}
}
4. ASP.NET Core 集成
在 Startup.cs
/ Program.cs 里挂载路由:
app.Map("/plugin/{name}", builder =>
{
builder.Run(async ctx =>
{
var name = ctx.Request.RouteValues["name"]?.ToString();
await pluginManager.ExecutePlugin(name, ctx);
});
});
三、热加载流程
- 把新的插件 DLL 放到
/plugins
目录。 - 调用
pluginManager.LoadPlugin(path)
动态加载。 - 如果要替换旧版本:
- 调用
pluginManager.UnloadPlugin(oldName)
卸载旧的 ALC。 - 再调用
pluginManager.LoadPlugin(newPath)
加载新 DLL。
- 调用
这样就能做到 不重启 ASP.NET Core 服务的 DLL 热加载与替换。
四、进阶优化(二阶段要点)
- 🔹 加入 文件监控(
FileSystemWatcher
),自动检测/plugins
文件夹 DLL 变动 → 自动卸载/加载。 - 🔹 插件生命周期:
OnLoad
、OnUnload
回调。 - 🔹 日志隔离:插件内异常要捕获,不能影响主系统。
- 🔹 安全策略:可限制插件只调用某些 API,避免恶意代码。
👉 问你一下:你是打算做
- Web API 插件系统(比如模块化接口扩展)
- 还是 业务规则插件系统(像规则引擎,热更新业务逻辑)
我可以根据你的用途帮你写一个 完整 demo(含 Program.cs、插件 DLL、热加载逻辑)。
要不要我直接帮你写一个 最小可运行的 ASP.NET Core 插件热加载 Demo?
发表回复