Reputation: 1
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
Reputation: 56869
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).
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
{
...
}
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" });
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.
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