We're currently developping a dynamic system which need to load some extensions at runtime.
An extension is build on the same architecture that MVC app, which means a controller folder, with classes that ends with Controller, associated Views and ViewComponents that are located in /View/ControllerName, and related models.
It is not possible to add this project as reference to the project, so I created a middleware that load them at runtime :
foreach (var item in extensions)
{
Assembly.LoadFrom($@"extensions\{item.Name}.dll");
}
So far so good, they are loaded on runtime. But, when I try to access a route that is created in one of extension's controller, WebSite gives me a 404 response.
I tried to add the extension as reference and it works well, so this is not an issue inside my extension.
How can I manage to register my dll's controller into the MVC main site ?
This is NOT ASP MVC 4, this is ASP Core, therefore it seems that this answer is not valid : asp.net mvc put controllers into a separate project
Although Dependency Injection could be a solution, I don't found any solution to make my extension register services for itself (and it's complicated for extensions creators).
My routing for extensions is defined Controller side :
[Route("[controller]/[action]")]
public class LotteryController : Controller { ... }
On my Startup.cs, I've kept the default route actually :
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
The fact is, I want my extension to enable route : http://localhost/Lottery/Index
And it gives me a blank page. My current Index action, for testing purpose, is
// GET Index
public IActionResult Index()
{
return Content("From extension");
//return View();
}
And here's my extension project hierarchy
MVC will automatically find controllers that inherit from Controller
on startup and include them. That will work for external assemblies but will more than likely only work for those that are statically linked.
If you are dynamically loading them you may want to either try and delay the startup of MVC or register the assemblies separately.
This code snippet shows how to search other assemblies for controllers. The full article explaining this can be found here. You may be able to use this once you have loaded your assemblies to force MVC to search them for controllers.
services.AddMvc().AddControllersAsServices(new[]
{
typeof(MyController).Assembly,
typeof(ExternalPocoController).Assembly
});
This will search those assemblies for controllers. The only caveat is that it may not be able to find the associated views. I'm honestly not sure on this as I've only ever done it with WebAPI controllers. Edit: This looks like it might be a way to get the views working
As a side note, I'd probably suggest either using Route decorators OR mapping routes via the old way. Using both seems like a recipe for trouble.
When you loaded assembly you need add ActionDescriptor instances for your actions manually. Actually MVC6 using 'Composite root' and creating immutable routes table. You can implement your custom IActionDescriptorCollectionProvider and replace the default in DI:
services.Replace(ServiceDescriptor.Describe(typeof(IActionDescriptorCollectionProvider), typeof(CustomActionDescriptorCollectionProvider), ServiceLifetime.Singleton));
and in getter of property:
public ActionDescriptorCollection ActionDescriptors
return default (build at startup) and your own ActionDescriptor's. In my solution i create intermediate store where i put my own ActionDescriptor's when i load dynamic assembly and in IActionDescriptorCollectionProvider i'm just check this store
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With