maartenba / MvcSiteMapProvider

An ASP.NET MVC SiteMapProvider implementation for the ASP.NET MVC framework.
Microsoft Public License
537 stars 220 forks source link

Adding second controller with mixed drop down menu: controller is null #439

Closed mwpowellhtx closed 8 years ago

mwpowellhtx commented 8 years ago

Hello,

Not really sure where to begin with this one. I had one controller, pretty much a single-controller menu system, all was well.

I have everything wired, as far as I can tell correctly, through Autofac Dependency Injection, especially for ctor injection; this was working before as well.

Now I am adding a second controller, mixing bits into the drop down menus, and getting the following error:

What's problematic with this is that I cannot tell which controller is in question. At minimum some exception wrapping an inner exception with some message indicating the controller, name, etc.

System.ArgumentNullException occurred
  HResult=-2147467261
  Message=Value cannot be null.
Parameter name: controller
  Source=MvcSiteMapProvider
  ParamName=controller
  StackTrace:
       at MvcSiteMapProvider.Web.Mvc.MvcContextFactory.CreateControllerContext(RequestContext requestContext, ControllerBase controller)
       at MvcSiteMapProvider.Security.AuthorizeAttributeAclModule.CreateControllerContext(ISiteMapNode node, RouteData routes, Type controllerType, IControllerFactory controllerFactory, Boolean& factoryBuiltController)
       at MvcSiteMapProvider.Security.AuthorizeAttributeAclModule.VerifyController(ISiteMapNode node, RouteData routes, Type controllerType)
       at MvcSiteMapProvider.Security.AuthorizeAttributeAclModule.VerifyNode(ISiteMap siteMap, ISiteMapNode node, HttpContextBase httpContext)
       at MvcSiteMapProvider.Security.AuthorizeAttributeAclModule.IsAccessibleToUser(ISiteMap siteMap, ISiteMapNode node)
       at MvcSiteMapProvider.Security.CompositeAclModule.IsAccessibleToUser(ISiteMap siteMap, ISiteMapNode node)
       at MvcSiteMapProvider.SiteMap.IsAccessibleToUser(ISiteMapNode node)
       at MvcSiteMapProvider.RequestCacheableSiteMap.IsAccessibleToUser(ISiteMapNode node)
       at MvcSiteMapProvider.SiteMapNodeSecurityBase.IsAccessibleToUser()
       at MvcSiteMapProvider.SiteMap.GetChildNodes(ISiteMapNode node)
       at MvcSiteMapProvider.SiteMapNodePositioningBase.get_ChildNodes()
       at MvcSiteMapProvider.SiteMapNodePositioningBase.get_HasChildNodes()
       at MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel.get_Children()
       at ASP._Page_Views_Shared_DisplayTemplates_BootstrapMenuHelperModel_cshtml.<>c__DisplayClass1.<TopMenu>b__0(TextWriter __razor_helper_writer) in d:\Source\Kingdom Software\FootballSimulator\Working\src\Football.Web.AspNet\Views\Shared\DisplayTemplates\BootstrapMenuHelperModel.cshtml:line 15
  InnerException: 

Here is my site map:

<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0"
            xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0 MvcSiteMapSchema.xsd">

    <mvcSiteMapNode title="My App" controller="Home" action="Index" description="Sports football simulator" clickable="false">
        <mvcSiteMapNode title="Home" controller="Home" action="Index">
            <mvcSiteMapNode title="Participation" controller="Participation" action="Index" description="Lists the team collection and participating teams">
                <mvcSiteMapNode title="My" controller="Teams" action="Index" description="Lists your teams and their participation" />
                <mvcSiteMapNode title="New" controller="Participation" action="New" description="Creates a new team collection" />
            </mvcSiteMapNode>
        </mvcSiteMapNode>
        <mvcSiteMapNode title="About" controller="Home" action="About"/>
        <mvcSiteMapNode title="Contact" controller="Home" action="Contact"/>
        <mvcSiteMapNode title="Profile" clickable="false">
            <mvcSiteMapNode title="My Profile" controller="Manage" action="Index" />
            <mvcSiteMapNode title="Subscribe" controller="Subscription" action="Index" />
        </mvcSiteMapNode>
    </mvcSiteMapNode>

</mvcSiteMap>
NightOwl888 commented 8 years ago

There are a couple of possibilities:

  1. You have one of the controllers spelled wrong in Mvc.sitemap.
  2. You have not registered all of the MVC controllers with Autofac.

Most likely it is number 2. But, you can probably make a better determination by temporarily disabling Security Trimming and then navigating to each of the new controllers.

MvcSiteMapProvider comes with an MvcModule that registers all controllers in your MVC assembly when using the package with the composition root. However, if you installed the "modules only" package, you must do that manually. Do note that it doesn't pick up controllers from other libraries than the top level one - you must also add the controllers from other assemblies manually.

Alternatively, Autofac has a way to register controllers right on the builder.

// Create the DI container builder
var builder = new ContainerBuilder();

// Setup configuration of DI
builder.RegisterControllers(Assembly.GetExecutingAssembly());
mwpowellhtx commented 8 years ago

Yes, I am registering my controllers with the RegisterControllers extension method. As I said, it worked fine prior to the latest work. I understand re: identifying the assemblies.

What do you mean by "misspelled"?

mwpowellhtx commented 8 years ago

Does the "other libraries" part include controller base classes from other assemblies? The references are there, the application builds, I did have a minor kerfuffle in a mis-typed ASP.NET view model, but that I corrected easily enough. Not sure how that got past the build, but it did. I am tracing back the Autofac DI bits, and as far as I can tell, I've got them all registering properly.

mwpowellhtx commented 8 years ago

What I need from you is a more descriptive message than "controller" is null; obvious a controller was null. It could be "Participation", but this wasn't changed apart from the base class stuff. More likely it is "Teams", but I don't know which. Where is this happening as an opportunity to provide that detail?

mwpowellhtx commented 8 years ago

My two cents: wrap some or all of ControllerFactoryDecorator.CreateController in a try/catch/re-throw, and provide the controller name as part of the message. I can submit a PR for that bit of it easily enough.

NightOwl888 commented 8 years ago

What is happening is that the AuthorizeAttributeAclModule is using the IControllerFactory that is registered with MVC to get an instance of every controller (not just the one for the node that represents the action you are viewing). The IControllerFactory is returning null.

So what I am saying is that either the controller name that is being passed in doesn't actually exist, or the controller factory is failing to return the controller for some other reason (such as it not being registered). If you are using a custom controller factory, you might want to start looking there.

I suppose another possibility is that the ControllerTypeResolver is returning null. That would be a first, but then that class was barely touched since v3 so I suppose it is possible.

mwpowellhtx commented 8 years ago

Yes, I get that; if there is a problem with any of the injected requirements, Autofac yields null. See prior comment; I need to know which controller(s) might be an issue where that's concerned in order to focus my efforts.

And I'm saying that, yes, the controller does exist, as far as the controller is concerned. More likely, there is a problem with one or more of its injected dependencies, but I don't know which controller is the issue.

I'll have a look at the Security Trimming aspect.

@NightOwl888 Meanwhile, no objections to providing the controller name(s) as part of the exception message?

NightOwl888 commented 8 years ago

Most likely, MVC will also complain if your controller is not created. So, my suggestion is to either disable Security Trimming or the MvcSiteMapProviderModule temporarily and then navigate through the controllers to see why that is the case.

mwpowellhtx commented 8 years ago

I'm tracking. Disabled Security Trimming, then I can more easily see which controller(s) are falling over and why in a detailed Autofac exception. Thanks!