merken / Prise

A .NET Plugin Framework.
https://merken.github.io/Prise
MIT License
361 stars 39 forks source link

DependencyInjection with MvcPluginLoader #52

Open martijnmelchers opened 2 years ago

martijnmelchers commented 2 years ago

Hi,

First off nice library, second of all I'm trying to get the Prise.Mvc to work with Dependency Injection. I got it to work when when loading the plugins in ConfigureServices however when I move it to Configure it can't initialize the plugin.

This is my code for activating the plugins:

           using (var scope = services.BuildServiceProvider().CreateScope())
            {
                var pathService = scope.ServiceProvider.GetRequiredService<IPathService>();
                var mvcPluginLoader = scope.ServiceProvider.GetRequiredService<IMvcPluginLoader>();
                var pluginLoader = scope.ServiceProvider.GetRequiredService<IPluginLoader>();
                var builder = scope.ServiceProvider.GetRequiredService<ApplicationPartManager>();

                var plugins = await mvcPluginLoader.FindPlugins<IPluginMvc>(pathService.GetPluginPath());

                foreach (var assemblyScanResult in plugins)
                {
                    Console.WriteLine(assemblyScanResult.ContractType);

                    var plugin = await mvcPluginLoader.LoadPluginAssembly<IPluginMvc>(assemblyScanResult);
                    builder.ApplicationParts.Add(new PluginAssemblyPart(plugin.Assembly));
                }
            }

And this is my controller in the plugin:

public class TestController : ControllerBase
    {
        private readonly ILogger<TestController> _logger;
        private readonly IPathService _pathService;

        public TestController(ILogger<TestController> logger, IPathService pathService)
        {
            _logger = logger;
            _pathService = pathService;
        }

        [HttpGet("Test")]
        public IActionResult Test()
        {
            _logger.LogInformation("Test called!");
            return Ok(_pathService.GetPluginPath());
        }
    }

I'm using the latest version of the plugin and .NET 5.0

merken commented 2 years ago

Hi Martijn,

Have you looked at the example (Example.Mvc.Razor) https://github.com/merken/Prise/blob/master/samples/Example.Mvc.Razor/Controllers/HomeController.cs ?

All service registration should be done in the ConfigureServices method in .NET, not sure why you'd move that to the Configure method (this is mainly for App configuration, not services)

https://github.com/merken/Prise/blob/master/samples/Example.Mvc.Razor/Startup.cs#L31

martijnmelchers commented 2 years ago

Thanks. I've looked at the code provided however I want to enable these plugins as soon as the application boots. Which is why I am doing it in ConfigureServices however it's not recommended to ues BuildServiceProvider() inside of ConfigureServices hence why I wanted to move it to Configure

However when the plugins are booted in ConfigureServices the controllers correctly construct with DepedencyInjecten but if it's called in Configure() it is not.

martijnmelchers commented 2 years ago

Also when I followed the example code in the repository this error occurs when trying to go to the route:

Prise.Activation.PluginActivationException: Plugin of type TestController could not be activated.
   at Prise.Activation.DefaultRemotePluginActivator.CreateRemoteInstance(IPluginActivationContext pluginActivationContext, IPluginBootstrapper bootstrapper, IServiceCollection hostServices)
   at Prise.Mvc.DefaultPriseMvcControllerActivator.Create(ControllerContext context)
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

However when loaded in ConfigureServices it works perfectly fine.

merken commented 1 year ago

Hi Martijn,

Please provide a reproduction repo for this.