Henry
Henry

Reputation: 1

@Html.MvcSiteMap().SiteMapPath() exception: "Ambiguous controller. Found multiple controller types

I'm using @Html.MvcSiteMap().SiteMapPath() to build breadcrumbs trail.

I have two controllers, one is DealsController which inherited from class of DealsContorllerBase. The both classes has their own Index() methods, where child's Index() overrides Index() of parent class. In my Mvc.sitemap:

<mvcSiteMapNode title="Deals" controller="Deals" action="Index"  >
<mvcSiteMapNode title="Deal" controller="Deals" action="ShowSpecificDeal" />
</mvcSiteMapNode>

In RegisterRoute function:

...

        routes.MapRoute(
            "Deals",                                            // Route name
            "Deals",                                            // URL with parameters
            new { controller = "Deals", action = "Index" },     // Parameter defaults
            new[] { "InvestX.UI.Web", "InvestX.UI.Controllers" } // namespaces
        );

...

The DealsController defined inside "InvestX.UI.Web" project and DealsControllerBase defined inside of "InvestX.UI.Controllers" project.

The whole exception as this:

Ambiguous controller. Found multiple controller types for 'DealsController'. The types that matched are:

InvestX.UI.Web.Controllers.DealsController InvestX.UI.Controllers.DealsControllerBase`1

Consider narrowing the places to search by adding your controller namespaces to ControllerBuilder.Current.DefaultNamespaces or exluding namespaces by adding them to the areaNamespacesToIgnore parameter of ControllerTypeResolverFactory.

I know if the DealsController were not inherited from DealsControllerBase and just use DealsController, everything would be fine. The exception was caused by which Index() action to take: DealsController's or DealsControllerBase's. My question is how does mvcSiteMapNode handle parent/child controller hierarky like this?

Upvotes: 0

Views: 650

Answers (1)

NightOwl888
NightOwl888

Reputation: 56869

Option #1

I know if the DealsController were not inherited from DealsControllerBase and just use DealsController, everything would be fine.

Correct. And this is your best option provided the time involved in fixing it is not prohibitive. There are no good reasons for using a base controller in MVC. This is the way things had to be done in ASP.NET, but with MVC inheritance is no longer the best option for cross-cutting concerns. You can accomplish everything you need with other extensions to the framework (especially global filters).

Option #2

Make your base class abstract - the ControllerTypeResolver filters abstract classes out, since they cannot be used as controllers directly by MVC. It is unlikely that your base class controller needs to be instantiated directly anyway.

public abstract class DealsControllerBase : Controller
{
    ...
}

Option #3

As pointed out in the error message, add your base controller namespace to AreaNamespacesToIgnore.

Internal DI

<add key="MvcSiteMapProvider_ControllerTypeResolverAreaNamespacesToIgnore" value="InvestX.UI.Controllers"/>

External DI

Ninject example shown - other DI containers are similar.

this.Kernel.Bind<IControllerTypeResolverFactory>().To<ControllerTypeResolverFactory>()
            .WithConstructorArgument("areaNamespacesToIgnore", new string[] { "InvestX.UI.Controllers" });

Option #4

As pointed out in the error message, you can also add all of your controller namespaces explicitly to the ControllerBuilder.Current.DefaultNamespaces.

ControllerBuilder.Current.DefaultNamespaces.Clear();
ControllerBuilder.Current.DefaultNamespaces.Add("InvestX.UI.Web.Controllers");
ControllerBuilder.Current.DefaultNamespaces.Add("SomeOtherNamespace.Controllers");

Note that this only works if you don't register any namespaces in your routes.

Option #5

I don't recommend this, but you could also avoid this problem by removing the word Controller from your DealsControllerBase class name. For example, name it DealsCtrlBase.

Upvotes: 1

Related Questions