0

Here are the symptoms I am experiencing:

I have a brand new empty controller in an area:

public class JamController : Controller
{
    public JamController()
    {
        throw new Exception("Not implemented!");
    }

If I visit http://myprojectserver.example.com:12345/urlthatdoesnotexist, I get the following error:

[CompositionException: The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.

1) Not implemented!

Resulting in: An exception occurred while trying to create an instance of type 'MyWebProject.Areas.Users.Controllers.JamController'.

Resulting in: Cannot activate part 'MyWebProject.Areas.Users.Controllers.JamController'.
Element: MyWebProject.Areas.Users.Controllers.JamController -->  MyWebProject.Areas.Users.Controllers.JamController

Resulting in: Cannot get export 'MyWebProject.Areas.Users.Controllers.JamController (ContractName="System.Web.Mvc.IController")' from part 'MyWebProject.Areas.Users.Controllers.JamController'.
Element: MyWebProject.Areas.Users.Controllers.JamController (ContractName="System.Web.Mvc.IController")
]
   System.ComponentModel.Composition.Hosting.CompositionServices.GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition) +55
   System.ComponentModel.Composition.Hosting.CatalogExportProvider.GetExportedValue(CatalogPart part, ExportDefinition export, Boolean isSharedPart) +78
   System.ComponentModel.Composition.Hosting.CatalogExport.GetExportedValueCore() +47
   System.ComponentModel.Composition.Primitives.Export.get_Value() +57
   System.ComponentModel.Composition.ExportServices.GetCastedExportedValue(Export export) +40
   System.ComponentModel.Composition.<>c__DisplayClassa`1.<CreateStronglyTypedLazyOfT>b__6() +39
   System.Lazy`1.CreateValue() +416
   System.Lazy`1.LazyInitValue() +382
   System.Lazy`1.get_Value() +75
   MefContrib.Web.Mvc.<>c__DisplayClass4.<GetControllerType>b__0(Lazy`1 e) +53
   System.Linq.WhereSelectEnumerableIterator`2.MoveNext() +204
   System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) +381
   System.Linq.Enumerable.ToList(IEnumerable`1 source) +58
   MefContrib.Web.Mvc.CompositionControllerFactory.GetControllerType(RequestContext requestContext, String controllerName) +412
   System.Web.Mvc.DefaultControllerFactory.System.Web.Mvc.IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, String controllerName) +61
   System.Web.Mvc.MvcRouteHandler.GetSessionStateBehavior(RequestContext requestContext) +122
   System.Web.Mvc.MvcRouteHandler.GetHttpHandler(RequestContext requestContext) +33
   System.Web.Mvc.MvcRouteHandler.System.Web.Routing.IRouteHandler.GetHttpHandler(RequestContext requestContext) +10
   System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context) +9709884
   System.Web.Routing.UrlRoutingModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e) +82
   System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +136
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +699

My question is "Why?" Why on earth is it trying to instantiate this controller? Where do I look to determine what is causing this. I'm pretty sure it isn't expected behaviour to instantiate every controller on "page not found".

I'm at a loss to know where to look. Can someone point me in the direction of something that will help?

UPDATE:

Turns out that the SetControllerFactory method below is causing the issue:

// Tell MVC3 to use MEF as its dependency resolver.
var dependencyResolver = new CompositionDependencyResolver(catalog);
DependencyResolver.SetResolver(dependencyResolver);

// Tell MVC3 to resolve dependencies in controllers
ControllerBuilder.Current.SetControllerFactory(
    new CompositionControllerFactory(
        new CompositionControllerActivator(dependencyResolver)));

Commenting out the "Tell MVC3 to resolve dependencies in controllers" section fixes my issue, and no controllers except those I ask for get instantiated. Luckily, that's only needed if you're not using the standard Asp.Net controller resolution (and we are).

Jonathan
  • 25,873
  • 13
  • 66
  • 85
  • 1
    You could always place a breakpoint in that action and look at the stack trace. – Brad Christie Aug 09 '13 at 12:04
  • 1
    Maybe messed up routing? – Leri Aug 09 '13 at 12:05
  • @Brad Tried looking at the stack trace. It jumped straight into System.ComponentModel.Composition, and didn't hit any user code. – Jonathan Aug 09 '13 at 12:05
  • Can we see your routing configuration? – JTMon Aug 09 '13 at 12:08
  • 2
    Do you have DI container that may be pre-loading any type of `IController`? (using Reflection to locate and instantiate any `IController` types?) – Brad Christie Aug 09 '13 at 12:08
  • @PLB I do have non-standard routing which uses the namespace of two separate projects for that Area, but why would that cause instantiation of all the controllers in that Area? – Jonathan Aug 09 '13 at 12:08
  • @Jonathan Take a .NET Reflector and debug. This is all I can suggest. – Leri Aug 09 '13 at 12:13
  • @BradChristie I'm using MEF for dependency injection, yes. – Jonathan Aug 09 '13 at 12:17
  • 1
    @Jonathan: that's got to be it. There's no reason (natively) for MVC to spin up a controller, but a DI container might. Look at your configuration settings and make sure it doesn't spin up every controller (even though it's not necessary). (It may have something to do with scope supplied (e.g. transient, request thread, singleton)) – Brad Christie Aug 09 '13 at 12:25
  • 1
    Unless MEF is completely misconfigured, it's still the routing which is most likely incorrectly directing MEF to create these errant controllers. – DavidN Aug 09 '13 at 12:29
  • Check your routing configuration. The stack trace clearly shows it's deliberately routing to that controller. – DavidN Aug 09 '13 at 12:04

2 Answers2

2

This is down to a bug in MefContrib.Web.Mvc. This assembly implements it's own ControllerFactory that inherits from DefaultControllerFactory.

The factory overrides GetControllerType, I assume to try and resolve controllers that live in assemblies somewhere other than the default application or it's references. The implementation of GetControllerType first calls into base.GetControllerType to see if Defaultcontroller can resolve it.

If it can't - which is the case for urls that don't exist - it asks MEF for all exports that implement IController. This returns an IEnumerable<Lazy<IController>> with one item for every class that implements IController in the bin/ folder (by default).

It then runs a linq query over the IEnumerable, calling GetType() on the Value property of each Lazy<IController>. Requesting the Value of a Lazy<T> forces the instance to be created. This is why every controller in the bin/ is being constructed for a page that doesn't exist.

I don't think this is an easy problem to fix properly as there is no way of getting the Type instance from Lazy<T>.Value without creating the value. However by removing the lines from AppStart_MefContribMVC3.cs that register the ControllerFactory with Asp.Net - you have effectively stopped using MefContrib.Web.Mvc's ControllerFactory and just used Asp.Net's DefaultControllerFactory instead.

Fordio
  • 3,410
  • 2
  • 14
  • 18
0

Turns out that the SetControllerFactory method below is causing the issue:

// Tell MVC3 to use MEF as its dependency resolver.
var dependencyResolver = new CompositionDependencyResolver(catalog);
DependencyResolver.SetResolver(dependencyResolver);

// Tell MVC3 to resolve dependencies in controllers
ControllerBuilder.Current.SetControllerFactory(
    new CompositionControllerFactory(
        new CompositionControllerActivator(dependencyResolver)));

Commenting out the "Tell MVC3 to resolve dependencies in controllers" section fixes my issue, and no controllers except those I ask for get instantiated. Luckily, that's only needed if you're not using the standard Asp.Net controller resolution (and we are).

Jonathan
  • 25,873
  • 13
  • 66
  • 85