Skip to content

RsCode.AspnetCore提供的插件服务,支持动态加载,可以方便的安装与卸载插件程序。

插件原理

默认情况下,当一个ASP.NET Core MVC应用启动时,它只会尝试在当前应用启动的项目及引用的项目中加载控制器,如果想从未直接引用的程序集中加载控制器和预编译Razor视图,我们就需要借助ApplicationPart了。

ApplocationPart可以发现程序集中包含的控制器、视图组件、TagHelper和预编译Razor视图等MVC功能。

程序运行期间,动态加载和卸载程序集,可以使用AssemblyLoadContext , 它表示程序集加载上下文的类,允许你控制程序集是如何被加载和卸载的

csharp
//创建一下可以被垃圾收集的AssemblyLoadContext
var context=new AssemblyLoadContext(true);

// 确保没有强引用指向这个AssemblyLoadContext
// 当没有任何强引用指向它时,垃圾收集器最终会清理它
context = null;

插件演示

插件演示

插件开发

插件项目开发步骤:

1.创建asp.net core Web应用项目做为插件项目,修改应用程序-输出类型类库

2.引用RsCode.AspNetCore 2.2.3以上的版本

3.实现IPluginSetup接口

添加文件PluginSetup.cs,内容如下:

csharp
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内容如下:

json
{
  "Name": "Plugin.Discount",      //插件项目英文名称
  "Version": "1.0.0",           //插件项目版本号
  "Description": "Plugin Discount"  //插件项目说明
}

删除非必要文件Program.cs, appsettings.json,

5.将插件生成到主项目的Plugins文件夹,将插件生成到主项目的Plugins文件夹下,不复制重复的程序集

打开vs,执行编辑项目文件, 修改OutputPath为插件程序输出位置,并将插件项目CopyLocalLockFileAssemblies设置为false

xml
<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中增加插件服务

csharp
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.复制插件dllbin

打开主项目,vs执行编辑项目文件增加内容

xml
<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,并复制插件dllPlugins文件夹下

3.重新生成插件项目,运行主项目

示例中提供了对插件禁用与开启的显示

插件配置示例

csharp
internal class PluginSetup : IPluginSetup
{
    public int Order => 200;
    public void Configure(IApplicationBuilder app, IHostEnvironment env)
    {
    }

    public void ConfigureServices(IServiceCollection services)
    {
        
    }
}

注意1:插件项目与主项目生成的位置

注意32: 应用程序之间控制器名称不能重复

注意3:插件项目中视图不要移除(具体看项目文件),静态文件要手动复制到主项目中

xml
<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,可以管理插件信息,

csharp
 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);
 }