diff --git a/docs/en/PlugIn-Modules.md b/docs/en/PlugIn-Modules.md index 5df505ade0..1d613582ab 100644 --- a/docs/en/PlugIn-Modules.md +++ b/docs/en/PlugIn-Modules.md @@ -53,4 +53,181 @@ There are two more built-in Plug-In Source implementations: * `PlugInSources.AddFiles()` gets a list of assembly (typically `dll`) files. This is a shortcut of using `FilePlugInSource` class. * `PlugInSources.AddTypes()` gets a list of module class types. If you use this, you need to load the assemblies of the modules yourself, but it provides flexibility when needed. This is a shortcut of using `TypePlugInSource` class. -If you need, you can create your own `IPlugInSource` implementation and add to the `options.PlugInSources` just like the others. \ No newline at end of file +If you need, you can create your own `IPlugInSource` implementation and add to the `options.PlugInSources` just like the others. + +## Example: Creating a Simple Plug-In + +Create a simple **Class Library Project** in a solution: + +![simple-plugin-library](images/simple-plugin-library.png) + +You can add ABP Framework packages you need to use in the module. At least, you should add the `Volo.Abp.Core` package to the project: + +```` +Install-Package Volo.Abp.Core +```` + +Every [module](Module-Development-Basics.md) must declare a class derived from the `AbpModule`. Here, a simple module class that resolves a service and initializes it on the application startup: + +````csharp +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.Modularity; + +namespace MyPlugIn +{ + public class MyPlungInModule : AbpModule + { + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var myService = context.ServiceProvider + .GetRequiredService(); + + myService.Initialize(); + } + } +} +```` + +`MyService` can be any class registered to [Dependency Injection](Dependency-Injection.md) system, as show below: + +````csharp +using Microsoft.Extensions.Logging; +using Volo.Abp.DependencyInjection; + +namespace MyPlugIn +{ + public class MyService : ITransientDependency + { + private readonly ILogger _logger; + + public MyService(ILogger logger) + { + _logger = logger; + } + + public void Initialize() + { + _logger.LogInformation("MyService has been initialized"); + } + } +} +```` + +Build the project, open the build folder, find the `MyPlugIn.dll`: + +![simple-plug-in-dll-file](images/simple-plug-in-dll-file.png) + +Copy `MyPlugIn.dll` into the plug-in folder (`D:\Temp\MyPlugIns` for this example). + +If you have configured the main application like described above (see Basic Usage section), you should see the `MyService has been initialized` log in the application startup. + +## Example: Creating a Plug-In With Razor Pages + +Creating plug-ins with views inside requires a bit more attention. + +> This example assumes you've [created a new web application](https://abp.io/get-started) using the application startup template and MVC / Razor Pages UI. + +Create a new **Class Library** project in a solution: + +![simple-razor-plugin](images/simple-razor-plugin.png) + +Edit the `.csproj` file content: + +````xml + + + + net5.0 + Library + true + + + + + + + +```` + +* Changed `Sdk` to `Microsoft.NET.Sdk.Web`. +* Added `OutputType` and `IsPackable` properties. +* Added `Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared` NuGet package. + +> Depending on [Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared) package is not required. You can reference to a more base package like [Volo.Abp.AspNetCore.Mvc](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc/). However, if you will build a UI page/component, it is suggested to reference to the [Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared) package since it is the most high-level package without depending on a particular [theme](UI/AspNetCore/Theming.md). If there is no problem to depend on a particular theme, you can directly reference to the theme's package to be able to use the theme-specific features in your plug-in. + +Then create your module class in the plug-in: + +````csharp +using System.IO; +using System.Reflection; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; +using Volo.Abp.Modularity; + +namespace MyMvcUIPlugIn +{ + [DependsOn(typeof(AbpAspNetCoreMvcUiThemeSharedModule))] + public class MyMvcUIPlugInModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(mvcBuilder => + { + //Add plugin assembly + mvcBuilder.PartManager.ApplicationParts.Add(new AssemblyPart(typeof(MyMvcUIPlugInModule).Assembly)); + + //Add views assembly + var viewDllPath = Path.Combine(Path.GetDirectoryName(typeof(MyMvcUIPlugInModule).Assembly.Location), "MyMvcUIPlugIn.Views.dll"); + var viewAssembly = new CompiledRazorAssemblyPart(Assembly.LoadFrom(viewDllPath)); + mvcBuilder.PartManager.ApplicationParts.Add(viewAssembly); + }); + } + } +} +```` + +* Depending on the `AbpAspNetCoreMvcUiThemeSharedModule` since we added the related NuGet package. +* Adding the plug-in's assembly to the `PartManager` of ASP.NET Core MVC. This is required by ASP.NET Core. Otherwise, your controllers inside the plug-in doesn't work. +* Adding the plug-in's views assembly to the `PartManager` of ASP.NET Core MVC. This is required by ASP.NET Core. Otherwise, your views inside the plug-in doesn't work. + +You can now add a razor page, like `MyPlugInPage.cshtml` inside the `Pages` folder: + +````html +@page +@model MyMvcUIPlugIn.Pages.MyPlugInPage +

Welcome to my plug-in page

+

This page is located inside a plug-in module! :)

+```` + +Now, you can build the plug-in project. It will produce the following output: + +![simple-razor-plug-in-dll-file](images/simple-razor-plug-in-dll-file.png) + +Copy the `MyMvcUIPlugIn.dll` and `MyMvcUIPlugIn.Views.dll` into the plug-in folder (`D:\Temp\MyPlugIns` for this example). + +If you have configured the main application like described above (see Basic Usage section), you should be able to visit the `/MyPlugInPage` URL when your application: + +![simple-plugin-output](images/simple-plugin-output.png) + +## Discussions + +In real world, your plug-in may have some external dependencies. Also, your application might be designed to support plug-ins. All these are your own system requirements. What ABP does is just loading modules on the application startup. What you do inside that modules is up to you. + +However, we can provide a few suggestions for some common cases. + +### Library Dependencies + +For package/dll dependencies, you can copy the related dlls to the plug-in folder. ABP automatically loads all assemblies in the folder and your plug-in will work as expected. + +> See [Microsoft's documentation](https://docs.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support#plugin-with-library-dependencies) for some additional explanations for that case. + +### Database Schema + +If your module uses a relational database and [Entity Framework Core](Entity-Framework-Core.md), it will need to have its tables available in the database. There are different ways to ensure the tables have been created when an application uses the plug-in. Some examples; + +1. The Plugin may check if the database tables does exists and create the tables on the application startup or migrate them if the plug-in has been updated and requires some schema changes. You can use EF Core's migration API to do that. +2. You can improve the `DbMigrator` application to find migrations of the plug-ins and execute them. + +There may be other solutions. For example, if your DB admin doesn't allow you to change the database schema in the application code, you may need to manually send a SQL file to the database admin to apply it to the database. \ No newline at end of file diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 19a8589e1a..edfebc9544 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -349,7 +349,8 @@ "path": "Module-Development-Basics.md" }, { - "text": "Plug-In Modules" + "text": "Plug-In Modules", + "path": "PlugIn-Modules.md" }, { "text": "Customizing the Application Modules", diff --git a/docs/en/images/simple-plug-in-dll-file.png b/docs/en/images/simple-plug-in-dll-file.png new file mode 100644 index 0000000000..3155708d68 Binary files /dev/null and b/docs/en/images/simple-plug-in-dll-file.png differ diff --git a/docs/en/images/simple-plugin-library.png b/docs/en/images/simple-plugin-library.png new file mode 100644 index 0000000000..9fefd57dda Binary files /dev/null and b/docs/en/images/simple-plugin-library.png differ diff --git a/docs/en/images/simple-plugin-output.png b/docs/en/images/simple-plugin-output.png new file mode 100644 index 0000000000..71f6a78c0e Binary files /dev/null and b/docs/en/images/simple-plugin-output.png differ diff --git a/docs/en/images/simple-razor-plug-in-dll-file.png b/docs/en/images/simple-razor-plug-in-dll-file.png new file mode 100644 index 0000000000..06b7a565fe Binary files /dev/null and b/docs/en/images/simple-razor-plug-in-dll-file.png differ diff --git a/docs/en/images/simple-razor-plugin.png b/docs/en/images/simple-razor-plugin.png new file mode 100644 index 0000000000..92e0e00d29 Binary files /dev/null and b/docs/en/images/simple-razor-plugin.png differ