1. 程式人生 > >從零開始實現ASP.NET Core MVC的外掛式開發(一) - 使用ApplicationPart動態載入控制器和檢視

從零開始實現ASP.NET Core MVC的外掛式開發(一) - 使用ApplicationPart動態載入控制器和檢視

標題:從零開始實現ASP.NET Core MVC的外掛式開發(一) - 使用Application Part動態載入控制器和檢視
作者:Lamond Lu
地址:https://www.cnblogs.com/lwqlun/p/11137788.html
原始碼:https://github.com/lamondlu/DynamicPlugins

前言

如果你使用過一些開源CMS的話,肯定會用過其中的的外掛化功能,使用者可以通過啟用或者上傳外掛包的方式動態新增一些功能,那麼在ASP.NET Core MVC中如何實現外掛化開發呢,下面我們來探究一下。

本系列只是筆者的一些嘗試,並不表示一定正確,只是為了分享一些思路,大家可以一起討論一下,後續會不斷更新。

什麼是ApplicationPart?

在ASP.NET Core中進行外掛話開發,就不得不說ApplicationPartApplicationPart是ASP.NET Core一個重要元件,它是應用程式資源的一種抽象,通過它可以發現程式集中包含的控制器、檢視元件、TagHelper和預編譯Razor檢視等MVC功能。

預設情況下,當一個ASP.NET Core MVC應用啟動時,它只會嘗試在當前應用啟動的專案及引用的專案中載入控制器,如果想從未直接引用的程式集中載入控制器和預編譯Razor檢視,我們就需要藉助ApplicationPart了。

而ASP.NET Core MVC中,有一個ApplicaitonPartManager

類, 通過ApplicationPartManager我們可以來配置當前應用中使用哪一些ApplicationPart

例:

var assembly = Assembly.LoadFile("demo.dll");

var assemblyPart = new AssemblyPart(assembly);

var mvcBuilders = services.AddMvc();

mvcBuilders.ConfigureApplicationPartManager(apm =>
{
    apm.ApplicationParts.Add(assemblyPart);
});

下面呢,我們通過一個最簡單的例項,給大家演示一下如何藉助ApplicationPart,動態載入第三方程式集中的控制器和預編譯檢視。

建立專案

首先我們建立一個ASP.NET Core MVC的站點,命名為DynamicPluginsDemoSite

然後我們同時建立一個.NET Core Class Library專案,命名為DemoPlugin1, 同時對該專案引用

  • Microsoft.AspNetCore.App
  • Microsoft.AspNetCore.Razor
  • Microsoft.AspNetCore.Razor.Design

注意: 針對以上3個程式集,需要保證DynamicPluginsDemoSite和DemoPluigin1使用的相同的版本。

這裡為了保證Razor檢視的預編譯,我們需要開啟DemoPlugin1專案的工程檔案DemoPlugin1.csproj。將專案使用的SDK從"Microsoft.NET.Sdk"改為"Microsoft.Net.Sdk.Razor"。

<Project Sdk="Microsoft.NET.Sdk.Razor">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <OutputPath>C:\Users\Lamond Lu\source\repos\DynamicPlugins\DynamicPluginsDemoSite\bin\Debug</OutputPath>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
    <PackageReference Include="Microsoft.AspNetCore.Razor" Version="2.2.0" />
    <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" />
  </ItemGroup>

</Project>

注:如果不做此修改,最後專案編譯之後,不會產生預編譯的Razor檢視程式集。(這裡如果有其他更優雅的修改方式,請大家留言, 我後續會先嚐試先編寫一個專案模板來避免這個重複操作)。

新增控制器和檢視

下面我們開始編寫我們的外掛。

這裡我們首先建立一個Plugin1Controller.cs.

    public class Plugin1Controller : Controller
    {
        public IActionResult HelloWorld()
        {
            return View();
        }
    }

然後我們新增一個對應的檢視檔案HelloWorld.cshtml。

@{

}

<h1>This is Demo Plugin1.</h1>

最終專案檔案目錄如下:

最後我們需要修改一個專案的輸出目錄,我們需要將專案編譯的dll傳送到DynamicPluginsDemoSite專案的Debug目錄中。

以上我們就完成了第一個元件的所有修改,下面我們開始修改DynamicPluginsDemoSite專案。

如何動態載入外掛中的控制器?

由於DynamicPluginsDemoSite專案不能直接引用DemoPlugin1, 所以當專案啟動時,不能自主發現DemoPlugin1專案中的控制器,所以這裡我們需要使用ApplicationPart將DemoPlugin1的程式集載入到當前的執行環境中。

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });


    var assembly = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + "DemoPlugin1.dll");
    var mvcBuilders = services.AddMvc();
    var controllerAssemblyPart = new AssemblyPart(assembly);

    mvcBuilders.ConfigureApplicationPartManager(apm =>
    {
        apm.ApplicationParts.Add(controllerAssemblyPart);
    });

    mvcBuilders.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

程式碼解釋:

  • 由於前一步中, 我們將DemoPlugin1的程式集輸出到了DynamicPluginsDemoSite的Debug目錄中,所以這裡我們可以使用Assembly.LoadFile方法將它載入。
  • 這裡我們使用AssemblyPart類,將載入程式集封裝成一個ApplicationPart.
  • mvcBuilders物件的ConfigureApplicationPartManager方法可以用來配置當前專案中使用的ApplicationPart

如何載入元件的預編譯Razor檢視?

載入完控制器之後,我們還需要載入外掛的預編譯Razor檢視。這裡和之前的稍有不同,我們需要使用CompileRazorAssemblyPart來封裝載入的預編譯Razor檢視。

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });


    var assembly = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + "DemoPlugin1.dll");
    var assemblyView = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + "DemoPlugin1.Views.dll");
    var viewAssemblyPart = new CompiledRazorAssemblyPart(assemblyView);

    var controllerAssemblyPart = new AssemblyPart(assembly);
    var mvcBuilders = services.AddMvc();

    mvcBuilders.ConfigureApplicationPartManager(apm =>
    {
        apm.ApplicationParts.Add(controllerAssemblyPart);
        apm.ApplicationParts.Add(viewAssemblyPart);
    });

    mvcBuilders.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

最終效果

現在我們啟動DynamicPluginsDemoSite,在瀏覽器中輸入/Plugin1/HelloWorld, 我們的外掛就正常啟用了。

注意:在啟動DynamicPluginsDemoSite站點之前,請務必先編譯DemoPlugin1專案,這樣DemoPlugin1產生的程式集才會輸出到DynamicPluginsDemoSite中。

總結

以上只是實現了一個最簡單的MVC外掛功能,要想完善整個專案,後續還有很多工作要做

  • 需要建立一個外掛模板,來避免一些重複操作。
  • 需要將外掛的模型和業務抽象出來。
  • 需要改用資料庫來儲存外掛資訊。
  • 需要支援實現外掛的管理以及外掛的升級。

後續我會慢慢實現以上功能,大家敬請期待