Plug-in development of ASP.NET Core MVC(1) – Dynamically load controllers and views using ApplicationPart

Plug-in development of ASP.NET Core MVC(2) How to create a project template

Plug-in development of ASP.NET Core MVC(3) – How to enable components at runtime

Plug-in development of ASP.NET Core MVC(4) – Plugin installation

Plug-in development of ASP.NET Core MVC(5)- Plugin removal and upgrade

Plug-in development of ASP.NET Core MVC(6) – How to load plugin references

 

source code:
https://github.com/lamondlu/Mystique

Foreword

If you have used some open source CMS, you will definitely use the plug-in function. Users can dynamically add some functions by enabling or uploading the plug-in package. How to implement plug-in development in ASP.NET Core MVC? Let’s explore it below.

This series is just some of the author’s attempts, it does not mean that it must be correct, just to share some ideas, you can discuss together, the follow-up will be updated.

What is an ApplicationPart?

To do plug-in development in ASP.NET Core, you have to say ApplicationPart. ApplicationPartAn important component of ASP.NET Core, it is an abstraction of application resources, which can be used to discover MVC functions such as controllers, view components, TagHelper, and pre-compiled Razor views.

By default, when an ASP.NET Core MVC application starts, it will only try to load the controller in the project that the application is currently launching and the referenced project, if you want to load the controller and precompile in an assembly that is not directly referenced. Razor view, we need to take advantage of ApplicationPartit.

In ASP.NET Core MVC, there is a ApplicaitonPart Managerclass that ApplicationPartManagerallows us to configure which ones are used in the current application ApplicationPart.

example:

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

var assemblyPart = new AssemblyPart(assembly);

var mvcBuilders = services.AddMvc();

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

Below, we will show you how to ApplicationPartdynamically load controllers and precompiled views in third-party assemblies with one of the simplest examples .

Create project

First we create a site for ASP.NET Core MVC, named DynamicPluginsDemoSite


Plug-in_development_of_ASP.NET_Core_MVC(1)_-_Dynamically_load_controllers_and_views_using_ApplicationPart_1.png

 

Then we create a .NET Core Class Library project, named DemoPlugin1, and reference the project at the same time.

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

Note: For the above 3 assemblies, you need to ensure the same version used by DynamicPluginsDemoSite and DemoPluigin1.

In order to ensure the pre-compilation of the Razor view, we need to open the project file DemoPlugin1.csproj of the DemoPlugin1 project. Change the SDK used by the project from “Microsoft.NET.Sdk” to “Microsoft.Net.Sdk.Razor”.

Copy<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>

Note: If you do not make this change, the precompiled Razor view assembly will not be generated after the final project is compiled. (If there are other more elegant modifications, please leave a message, I will try to write a project template to avoid this duplication.)

Add controller and view

Let’s start writing our plugin.

Here we create one first Plugin1Controller.cs.

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

Then we add a corresponding view file HelloWorld.cshtml.

Copy@{

}

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

The final project file directory is as follows:


Plug-in_development_of_ASP.NET_Core_MVC(1)_-_Dynamically_load_controllers_and_views_using_ApplicationPart_2.png

 

Finally, we need to modify the output directory of a project, we need to send the project compiled dll to the Debug directory of the DynamicPluginsDemoSite project.


Plug-in_development_of_ASP.NET_Core_MVC(1)_-_Dynamically_load_controllers_and_views_using_ApplicationPart_3.png

 

Above we have completed all the modifications of the first component, let’s start modifying the DynamicPluginsDemoSite project.

How to dynamically load a controller in a plugin?

Since the DynamicPluginsDemoSite project can’t directly reference DemoPlugin1, when the project starts, you can’t discover the controller in the DemoPlugin1 project, so we need to use ApplicationPartthe demoPlugin1 assembly to load into the current running environment.

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

Code explanation:

  • Since the previous step, we exported the demoPlugin1 assembly to the Debug directory of DynamicPluginsDemoSite, so here we can use the Assembly.LoadFilemethod to load it.
  • Here we use the AssemblyPartclass to wrap the load assembly into one ApplicationPart.
  • mvcBuildersThe object’s ConfigureApplicationPartManagermethod can be used to configure the ApplicationPart used in the current project.

How to load a precompiled Razor view of a component?>

After loading the controller, we also need to load the precompiled Razor view of the plugin. This is slightly different from the previous one, we need to use the CompileRazorAssemblyPartprecompiled Razor view to encapsulate the load.

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

Final effect

Now let’s launch DynamicPluginsDemoSite, type /Plugin1/HelloWorld in the browser, and our plugin will be enabled.

Note: Before starting the DynamicPluginsDemoSite site, be sure to compile the DemoPlugin1 project first, so that the assembly generated by DemoPlugin1 will be output to DynamicPluginsDemoSite.


Plug-in_development_of_ASP.NET_Core_MVC(1)_-_Dynamically_load_controllers_and_views_using_ApplicationPart_4.png

 

Summary

The above just implements one of the simplest MVC plugin functions. To improve the whole project, there is still a lot of work to be done.

  • Need to create a plugin template to avoid some repetitive operations.
  • The model and business of the plugin need to be abstracted out.
  • Need to use the database instead to save the plugin information.
  • Need to support the implementation of plug-in management and plug-in upgrades.

Orignal link:https://www.cnblogs.com/lwqlun/p/11137788.html#4310745