I am having problems resolving 404 responses in my Asp.Net MVC 4 project. It is built in VS2012, focused on 4.5.
I have precompiled views and controllers built into standalone DLLs. I can dynamically load DLLs and check them from my main project, even call methods on them; however, the MVC Framework does not seem to know the controllers. I'm close here, but something is missing.
Background on controllers and views
Controllers are built in a stand-alone MVC project and are inherited from Controller
. Nothing interesting happens. Views use RazorGenerator and become classes that live in the project.
The result of the project is a DLL that correctly contains the controllers and views.
DLLs implement a specific interface, we will call it IPlugin
in a separate class (not part of the controller) in the library.
DLL loading
Starting as an administrator in Visual Studio I will compile my application, which is hosted under IIS. With the project built, I drop the plugin DLL into the Plugins directory. Without debugging (this becomes important later) I open IE and go to the site. Please note that at the moment the application is created, but never launches, therefore, launch events are triggered. Everything here is still consistent if I reuse the application pool.
I have a Startup
class with two methods, PreStart
and PostStart
and call methods using WebActivator.PreApplicationStartMethod
and WebActivator.PostApplicationStartMethod
respectively.
PreStart
where I do the following:
- Get a list of all the plugin dlls in my plugins directory
- Copy all the plugins to
AppDomain.CurrentDomain.DynamicDirectory
- Download the type ... if it contains
IPlugin
I, then- Add assembly to BuildManager
- Call some of the methods of the class that implements IPlugin
In 'PostStart' I am doing this bit of code (based on RazorGenerator.Mvc code):
foreach (var assembly in Modules.Select(m=>m.Value)) { var engine = new PrecompiledMvcEngine(assembly) { UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal }; ViewEngines.Engines.Insert(0, engine); VirtualPathFactoryManager.RegisterVirtualPathFactory(engine); }
Modules
in this context is a key / value pair, where the values are loaded assemblies. The purpose of this code is to make sure that MVC is aware of the views by adding a viewer for each assembly that knows how to allow views (this is part of RazorGenerator).
How do I know that I am closed (but obviously not enough cigars)
IPlugin
defines a method called RegisterRoutes
, where, you guessed it, routes should be registered for those who implement the interface. I call this method on PreStart
and the routes are added - I checked that they exist in my route table. For example, on the route defined in my plugin created by a dynamic method call during PreStart
, I see something like this as a DataToken when looking at my routes:
Namespaces = Plugin.Name.Controllers
So, the route is registered, the assembly is loaded, I checked that the DLL was correctly copied to the DynamicDirectory AppDomain. I can call class members that are dynamically loaded at runtime. But when I go to the URL that matches the route , I get 404 . This is not a “failed to find view” of YSOD, it is more akin to not detecting a controller at all.
Here is the part that bothers me because of me : if at the moment, without doing anything, I return to Visual Studio and press F5 ... everything works.
It looks like Visual Studio is aware of the controller in some way that I cannot determine, and the MVC Framework picks it up.
Finally the question
What am I missing and how do I get the MVC Framework about my controller?
And at this point, if you are still reading this, thanks. :)