404 on controllers in external assemblies

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. :)

+12
asp.net-mvc asp.net-mvc-4 assemblies
Feb 15 '13 at 19:26
source share
3 answers

It turns out that this is a bug in Asp.Net itself.

Having discussed the problem with Eilon Lipton from the Asp.Net team and thinking that it was something wrong, in the MVC Framework, Eilon and members of several teams broke into things and found that the error was at a lower level for this conversation: http: //aspnetwebstack.codeplex.com/discussions/403529

They also suggested a workaround that included another call to BuildManager after calling AddReferencedAssembly , which I implemented using the following code:

  // Add the plugin as a reference to the application BuildManager.AddReferencedAssembly(assembly); BuildManager.AddCompilationDependency(assembly.FullName); 

This allows you to add additional controllers / compiled views at startup during the initialization phase before the application. Now I look at the list of DLLs in my plugins directory and click on the BuildManager as above.

The only limitation here is that you cannot delete assemblies or clear the cache dynamically. The only way I found for this is to add a previously unknown assembly to the reference assemblies and compilation dependencies. I am experimenting with dynamically emitting a new assembly during initialization before the application so that I can effectively clear the cache and remove previously enabled plugins by creating a new assembly.

Hope this helps someone else.

Greetings.

+4
Mar 04 '13 at 16:29
source share

Looks like this problem:

MVC uses the assembly type name for the assembly, corresponding to disambiguate to view cache entries from different viewers. So, it is impossible to have more than one PrecompiledMvcEngine object (as when you have precompiled views in more than one assembly) . The problem can be solved by creating another derived class from PrecompiledMvcEngine for each assembly. Or by creating a single generic derived class parameterized by some type from the assembly.

The article is here .

+1
Feb 21 '13 at 17:49
source share

@MisterJames, look at this:

Asp.Net Mvc Pluggable App

Hope this is helpful.

0
Feb 21 '13 at 19:21
source share



All Articles