MVC Futures strongly typed RenderAction with Areas and duplicate Controller Names
I'm trying to use an Html.RenderAction in a strongly typed nature from the MVC Futures library
I have a Navigation action on my primary HomeController (area = "") that I'm trying to call from my Site.Master
<% Html.RenderAction<HomeController>(x=>x.Navigation()); %>
This worked great until I added another HomeController to my Admin portable area. Then I started to the following error:
Multiple types were found that match the controller named 'Home'. This can happen if the route that services this request does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.
The request for 'Home' has found the following matching controllers: Areas.Admin.Controllers.HomeController Web.Controllers.HomeController
I was able to resolve the issue by using the following non-mvccontrib strongly typed开发者_如何学C RenderAction method. I would rather not have this non-strongly typed method on the page, is there a way to make the mvccontrib methods accept an area, or for them to know to generate the area based on routes or the namespace of the Controller being specified.
<% Html.RenderAction("Navigation", "Home", new { area = "" }); %>
I have already added to my global.asax.cs file to support the multiple controllers with a default area as shown below, and I've also verified that this is the correct namespace for my controllers.
_routeCollection.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }, new[] { "Web.Controllers" });
Apparently there is not support for this in the futures project at this time, or they changed it since Futures v1.0. I used reflector to disassemble the futures library and found the source for the RenderAction method and modified it to parse the area out of the Controller's namespace and add it into the RouteValues before it processes. I'm assuming that I'll likely have to do something similar for ActionLink's as well if I want them to behave in the same way.
Here is a sample of the code that I used, so hopefully this can help someone else in the future. I will likely create new methods indicating that it will parse the areas, rather than an overload as I have thus far
public static void RenderAction<TController>(this HtmlHelper helper, Expression<Action<TController>> action) where TController : Controller
{
var ctrlNamespace = typeof(TController).Namespace;
int areaStart = ctrlNamespace.IndexOf(".Areas.");
if (areaStart > -1)
{
ctrlNamespace = ctrlNamespace.Substring(areaStart + 7);
int areaEnd = ctrlNamespace.IndexOf(".");
ctrlNamespace = ctrlNamespace.Substring(0, areaEnd);
}
else
{
ctrlNamespace = String.Empty; //default area
}
helper.ViewContext.RouteData.Values.Add("area", ctrlNamespace);
RouteValueDictionary routeValuesFromExpression = ExpressionHelper.GetRouteValuesFromExpression<TController>(action);
foreach (KeyValuePair<string, object> pair in helper.ViewContext.RouteData.Values)
{
if (!routeValuesFromExpression.ContainsKey(pair.Key))
{
routeValuesFromExpression.Add(pair.Key, pair.Value);
}
}
helper.RenderRoute(routeValuesFromExpression);
}
In order to support multiple controllers in MVC you will need to add a namespace to your default route.
See this answer for more detail: Question 2627699
精彩评论