0

While creating a Plugin for a MVC site, I'm facing an issue about controller resolution.

Here are all the routes that concern the current context :

routes.MapRoute("Route1",
    "Admin/Product/{action}/{id}",
    new { controller = "Product", action = "Index", area = "Admin", id = UrlParameter.Optional },
    new[] { "Plugin.Controllers" }
)

routes.MapRoute(
    "Admin_default",
    "Admin/{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", area = "Admin", id = "" },
    new[] { "Admin.Controllers" }
);

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    new[] { "Public.Controllers" }
);

There are 3 ProductController classes in the entire solution :

  • Plugin.Controllers.ProductController (inherits from the Admin one)
  • Admin.Controllers.ProductController
  • Public.Controllers.ProductController

In my view "/Views/Home/Index.cshtml" (i.e. the "Index" action of the HomeController without any area) I have the following code :

@Html.Action("HomepageProducts", "Product")

It refers to the action "HomepageProducts" of the Public.Controllers.ProductController.

MVC looks for the plugin controller instead of the public one and throws the following exception :

[HttpException (0x80004005): A public action method 'HomepageProducts' was not found on controller 'Plugin.Controllers.ProductController'.] System.Web.Mvc.Controller.HandleUnknownAction(String actionName) +291 System.Web.Mvc.Controller.b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +31 System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +29 System.Web.Mvc.Async.WrappedAsyncResultBase1.End() +49 System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +36 System.Web.Mvc.Controller.b__15(IAsyncResult asyncResult, Controller controller) +12 System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +22 System.Web.Mvc.Async.WrappedAsyncResultBase1.End() +49 System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +26 System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10 System.Web.Mvc.MvcHandler.b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +21 System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +29 System.Web.Mvc.Async.WrappedAsyncResultBase1.End() +49 System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +28 System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9 System.Web.Mvc.<>c__DisplayClassa.b__9() +22 System.Web.Mvc.<>c__DisplayClass4.b__3() +10 System.Web.Mvc.ServerExecuteHttpHandlerWrapper.Wrap(Func`1 func) +27

[HttpException (0x80004005): Execution of the child request failed. Please examine the InnerException for more information.] System.Web.Mvc.ServerExecuteHttpHandlerWrapper.Wrap(Func`1 func) +102 System.Web.Mvc.ServerExecuteHttpHandlerWrapper.Wrap(Action action) +64 System.Web.Mvc.ServerExecuteHttpHandlerAsyncWrapper.EndProcessRequest(IAsyncResult result) +71 System.Web.HttpServerUtility.ExecuteInternal(IHttpHandler handler, TextWriter writer, Boolean preserveForm, Boolean setPreviousPage, VirtualPath path, VirtualPath filePath, String physPath, Exception error, String queryStringOverride) +1436

[HttpException (0x80004005): Erreur d'exécution de la demande enfant pour le gestionnaire 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'.] System.Web.HttpServerUtility.ExecuteInternal(IHttpHandler handler, TextWriter writer, Boolean preserveForm, Boolean setPreviousPage, VirtualPath path, VirtualPath filePath, String physPath, Exception error, String queryStringOverride) +3428452 System.Web.HttpServerUtility.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm, Boolean setPreviousPage) +76 System.Web.HttpServerUtility.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm) +29 System.Web.HttpServerUtilityWrapper.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm) +24 System.Web.Mvc.Html.ChildActionExtensions.ActionHelper(HtmlHelper htmlHelper, String actionName, String controllerName, RouteValueDictionary routeValues, TextWriter textWriter) +464 System.Web.Mvc.Html.ChildActionExtensions.Action(HtmlHelper htmlHelper, String actionName, String controllerName, RouteValueDictionary routeValues) +83 System.Web.Mvc.Html.ChildActionExtensions.Action(HtmlHelper htmlHelper, String actionName, String controllerName) +10 ASP._Page_Views_Home_Index_cshtml.Execute() in c:\Path\To\My\Project\Views\Home\Index.cshtml:14

If this helps, the solution use Autofac.MVC5... Any help/comment/idea is welcome.

Am I missing something or only the routes are implied in controllers resolution ?

UPDATE

When browsing the "Admin" area all actions are well performed, the plugin and the admin controllers works fine.

If I comment the plugin route, the error is not thrown anymore. Of course my plugin controller is not called anymore.

// routes.MapRoute("Route1",
//     "Admin/Product/{action}/{id}",
//     new { controller = "Product", action = "Index", area = "Admin", id = UrlParameter.Optional },
//     new[] { "Plugin.Controllers" }
// )

So I guess the issue is due to the plugin route, the plugin controller itself or the way the route is resolved by MVC ?

UPDATE 2

I tried to uncomment all my routes and specify area in @Html.Action() as follow:

@Html.Action("HomepageProducts", "Product", new { area = "" })

But MVC ignore the area and try to load the Plugin controller...

[HttpException (0x80004005): A public action method 'HomepageProducts' was not found on controller 'Plugin.Controllers.ProductController'.]

Toine Seiter
  • 437
  • 4
  • 20
  • In your controller, `Plugin.Controllers.ProductController`.. do you have an `ActionResult` method named `HomepageProducts` that returns a `View`? – Grizzly Oct 26 '16 at 12:33
  • No I don't. Only the public one have this action – Toine Seiter Oct 26 '16 at 12:34
  • I would assume this is happening because in your Route.config, specifically for `Route 1`.. you're calling `@Html.Action("HomepageProducts", "Product")` which is going to the `Product` Controller, which matches the URL you provided in `Route 1`.. – Grizzly Oct 26 '16 at 12:53
  • I'm not sure to understand. The HomeController is a "public" one (not in an area) and the Html.Action() doesn't specify any "area". Why is MVC looking for a controller associated to an area route ? – Toine Seiter Oct 26 '16 at 13:05
  • Are you using Area for `Admin`? If so see http://stackoverflow.com/questions/7842293/multiple-types-were-found-that-match-the-controller-named-home – James P Oct 26 '16 at 18:49
  • Yes, my plugin controller and the admin controller use an area as you can see in the routes of my post. I also defined namespaces for each route. – Toine Seiter Oct 27 '16 at 08:29
  • try @Html.Action("HomepageProducts", "Product", new {Area=""}) – KevDevMan Oct 27 '16 at 15:48
  • Good suggestion. That's what I've tried in my **UPDATE 2**. – Toine Seiter Oct 27 '16 at 15:56

1 Answers1

0

I'm definitely sure I miss something but I bypass my issue by register each Plugin.ProductController action routes I've overridden from the Admin one.

routes.MapRoute("Route1",
    "Admin/Product/{action}/{id}",
    new { controller = "Product", action = "Index", area = "Admin", id = UrlParameter.Optional },
    new[] { "Plugin.Controllers" }
)

//...becomes...

routes.MapRoute("Product.Action1",
    "Admin/Product/Action1/{id}",
    new { controller = "Product", action = "Action1", area = "Admin", id = UrlParameter.Optional },
    new[] { "Plugin.Controllers" }
)

routes.MapRoute("Product.Action2",
    "Admin/Product/Action2/{id}",
    new { controller = "Product", action = "Action2", area = "Admin", id = UrlParameter.Optional },
    new[] { "Plugin.Controllers" }
)

//...

All works fine now. With this, I'm able to derive controllers.

Toine Seiter
  • 437
  • 4
  • 20