2010-04-16 92 views
11

我已经几乎在这里问了一个问题:ASP.NET MVC Html.ActionLink维护路由值

asp.net mvc Html.ActionLink() keeping route value I don't want

然而,最终的解决方案是组装机,纯粹而简单,我真的会想了解为什么会发生这种情况,如果有人可以请向我解释一下?

为了完整,可以非常容易地重新创建的场景:

  • 创建一个新的MVC的Web应用程序。
  • 运行它。
  • 访问About选项卡修改URL以读取/ Home/About/Flib - 显然,这会将您带到ID为'Flib'的行动,我们不关心。

注意,顶部菜单链接到关于现在实际上链接到/首页//Flib - 这是错误的,据我所看到的,因为我现在绝对没有使用网站的链接要回的方式/首页/关于

我真的不明白为什么我应该强制修改我的所有Html.ActionLinks,以包含new { id = string.Empty }作为routevalues,null作为htmlAttribs。这看起来尤其突出,因为我已经指定id = 0作为路线本身的一部分。

希望我在这里错过了一招。

回答

12

当你看看源代码,操作链接你发现

<%= Html.ActionLink("LinkText", "Action", "Controller"); %> 

将匹配

public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName) { 
     return ActionLink(htmlHelper, linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary()); 
    } 

现在到目前为止,这看起来不错,因为它正在创造一个新的路径值字典,以便它不会将当前上下文中的值传递给新链接,这将会发生。

然而,在代码进一步下降,其中正在生成的网址:

public static string GenerateUrl(string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, bool includeImplicitMvcValues) { 
     if (routeCollection == null) { 
      throw new ArgumentNullException("routeCollection"); 
     } 

     if (requestContext == null) { 
      throw new ArgumentNullException("requestContext"); 
     } 

     RouteValueDictionary mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, requestContext.RouteData.Values, routeValues, includeImplicitMvcValues); 

     VirtualPathData vpd = routeCollection.GetVirtualPathForArea(requestContext, routeName, mergedRouteValues); 
     if (vpd == null) { 
      return null; 
     } 

     string modifiedUrl = PathHelpers.GenerateClientUrl(requestContext.HttpContext, vpd.VirtualPath); 
     return modifiedUrl; 
    } 

可以看到的RequestContext被引用其中有访问的RouteData和routeCollections,其中将包含ID数据。当创建VirtualPathForArea,下面一行是其中的id值将出现在您的网址:

internal static VirtualPathData GetVirtualPathForArea(this RouteCollection routes, RequestContext requestContext, string name, RouteValueDictionary values, out bool usingAreas) { 
     if (routes == null) { 
      throw new ArgumentNullException("routes"); 
     } 

     if (!String.IsNullOrEmpty(name)) { 
      // the route name is a stronger qualifier than the area name, so just pipe it through 
      usingAreas = false; 
      return routes.GetVirtualPath(requestContext, name, values); 
     } 

     string targetArea = null; 
     if (values != null) { 
      object targetAreaRawValue; 
      if (values.TryGetValue("area", out targetAreaRawValue)) { 
       targetArea = targetAreaRawValue as string; 
      } 
      else { 
       // set target area to current area 
       if (requestContext != null) { 
        targetArea = AreaHelpers.GetAreaName(requestContext.RouteData); 
       } 
      } 
     } 

     // need to apply a correction to the RVD if areas are in use 
     RouteValueDictionary correctedValues = values; 
     RouteCollection filteredRoutes = FilterRouteCollectionByArea(routes, targetArea, out usingAreas); 
     if (usingAreas) { 
      correctedValues = new RouteValueDictionary(values); 
      correctedValues.Remove("area"); 
     } 

     VirtualPathData vpd = filteredRoutes.GetVirtualPath(requestContext, correctedValues); 
     return vpd; 
    } 

行:

VirtualPathData vpd = filteredRoutes.GetVirtualPath(requestContext, correctedValues); 

需要的虚拟路径(这只是路线)并返回它。因此,虚拟路径将是/Home/About/Flib

然后当返回的虚拟路径下面一行用它来设置客户端的URL跳转链接:

string modifiedUrl = PathHelpers.GenerateClientUrl(requestContext.HttpContext, vpd.VirtualPath); 

所以几乎是它看起来像ActionLink的设置使用区域的虚拟路径,这只是匹配的路线,在这种情况下恰好是默认路线。

+5

@Carl,@amurra:这是不是解决问题或者这只是中无法充分解决的症状的解释吗? – 2011-02-24 20:03:28

+0

@Robert Koritnik - 这只是对ASP.NET MVC 2中发生了什么的解释。不知道这段代码是否随ASP.NET MVC 3发生了变化。 – amurra 2011-02-24 20:27:40

+0

我不知道,老实说我并不在乎,因为我也使用MVC2 RTM(因为我必须使用.net 3.5)。所以我也有这个问题。但我做了这似乎工作:http://stackoverflow.com/questions/3475981/ambient-values-in-mvc2-net-routing/5110032#5110032 – 2011-02-24 20:30:33

3

如果我理解正确,听起来好像需要注册第二个“Action Only”路径并使用Html.RouteLink。首先注册一个这样的路线在你的应用程序启动:

routes.MapRoute("ActionOnly", "{controller}/{action}", new { controller = "Home", action = "Index" }); 

然后,而不是ActionLink的创建这些链接中使用:

Html.RouteLink("About","ActionOnly") 
1

如果你要么不知道值需要加以明确覆盖或者你只是想避免额外的参数列表,你可以使用像下面这样的扩展方法。

<a href="@Url.Isolate(u => u.Action("View", "Person"))">View</a> 

实施细节为in this blog post