RsCode.AspnetCore
提供的插件服务,支持动态加载,可以方便的安装与卸载插件程序。
插件原理
默认情况下,当一个ASP.NET Core MVC
应用启动时,它只会尝试在当前应用启动的项目及引用的项目中加载控制器,如果想从未直接引用的程序集中加载控制器和预编译Razor视图,我们就需要借助ApplicationPart
了。
ApplocationPart
可以发现程序集中包含的控制器、视图组件、TagHelper和预编译Razor视图等MVC功能。
程序运行期间,动态加载和卸载程序集,可以使用AssemblyLoadContext
, 它表示程序集加载上下文的类,允许你控制程序集是如何被加载和卸载的
//创建一下可以被垃圾收集的AssemblyLoadContext
var context=new AssemblyLoadContext(true);
// 确保没有强引用指向这个AssemblyLoadContext
// 当没有任何强引用指向它时,垃圾收集器最终会清理它
context = null;
插件演示
插件开发
插件项目开发步骤:
1.创建asp.net core Web
应用项目做为插件项目,修改应用程序
-输出类型
为类库
2.引用RsCode.AspNetCore
2.2.3
以上的版本
3.实现IPluginSetup
接口
添加文件PluginSetup.cs
,内容如下:
using RsCode.AspNetCore.Plugin;
internal class PluginSetup : IPluginSetup
{
public int Order => 200; //插件顺序
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
}
public void ConfigureServices(IServiceCollection services)
{
}
}
4.添加插件描述PluginInfo.json
,
PluginInfo.json
内容如下:
{
"Name": "Plugin.Discount", //插件项目英文名称
"Version": "1.0.0", //插件项目版本号
"Description": "Plugin Discount" //插件项目说明
}
删除非必要文件Program.cs
, appsettings.json
,
5.将插件生成到主项目的Plugins
文件夹,将插件生成到主项目的Plugins
文件夹下,不复制重复的程序集
打开vs,执行编辑项目文件
, 修改OutputPath
为插件程序输出位置,并将插件项目CopyLocalLockFileAssemblies
设置为false
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>Library</OutputType>
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>..\..\Application.WebMvc\Plugins\Plugin.Discount</OutputPath>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
</Project>
插件调用
1.添加插件服务
打开主项目,引用RsCode.AspNetCore
2.2.3
以上的版本,Program.cs
中增加插件服务
using RsCode.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseServiceProviderFactory(new DynamicProxyServiceProviderFactory());
//添加插件服务
builder.Services.AddPlugins();
//如果主项目与插件项目同时使用了自定义的程序集,需要添加,否则不添加
//string[] assemblies=new string[]{"Rswl.Core","Rswl.Application"};
//builder.Services.AddPlugins(assemblies);
//把默认插件位置Plugins改其Modules
//builder.Services.AddPlugins(null,Path.Combine(AppContext.BaseDirectory,"Modules"));
//添加插件配置
app.UsePlugins(builder.Environment);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "areas",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
app.Run();
2.复制插件dll
到bin
打开主项目,vs执行编辑项目文件
,增加
内容
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<Folder Include="Plugins\" />
<Content Remove="Plugins\**\Properties\*.json" />
<Content Include="Plugins\**\*.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Target Name="CreatePluginFolder" AfterTargets="AfterPublish">
<MakeDir Directories="$(PublishDir)Plugins" Condition="!Exists('$(PublishDir)Plugins')" />
</Target>
</Project>
这个示例是插件文件夹为Plugins
的配置,它把主项目CopyLocalLockFileAssemblies
设为true
,并复制插件dll
到Plugins
文件夹下
3.重新生成插件项目,运行主项目
示例中提供了对插件禁用与开启的显示
插件配置示例
internal class PluginSetup : IPluginSetup
{
public int Order => 200;
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
}
public void ConfigureServices(IServiceCollection services)
{
}
}
注意1:插件项目与主项目生成的位置
注意32: 应用程序之间控制器名称不能重复
注意3:插件项目中视图不要移除(具体看项目文件),静态文件要手动复制到主项目中
<ItemGroup>
<Content Remove="Views\Plugins\Index.cshtml" /> //如果移除了视图,将无法显示
<Content Remove="wwwroot\js\plugins.js" />
</ItemGroup>-->
示例项目结构,仅供参考
.
├── plugins # 插件项目文件夹
│ └── Plugin.A # 插件项目A
│ └── Plugin.B # 插件项目B
├── MainWebProject # 主项目
│ └── Plugins # 插件应用文件夹 (动态生成)
│ └── Plugin.A # 插件A
│ └── Plugin.A.dll
│ └── Plugin.B # 插件B
│ └── Plugin.B.dll
└── xxx.sln
插件API
通过注入接口IPluginManager
,可以管理插件信息,
public interface IPluginManager
{
/// <summary>
/// 获取所有插件信息
/// </summary>
/// <returns></returns>
List<T> GetAllPlugins<T>() where T : PluginInfo;
/// <summary>
/// 禁用单个插件
/// </summary>
/// <param name="pluginName"></param>
void DisablePlugin(string pluginName);
/// <summary>
/// 启用插件
/// </summary>
/// <param name="pluginName"></param>
void EnablePlugin(string pluginName);
}