添加到htmlAttributes以获取自定义ActionLink帮助程序扩展

我正在尝试创建一个简单的Html.ActionLink(…)HtmlHelper自定义版本

我想为传入的htmlAttributes匿名对象附加一组额外的属性。

public static MvcHtmlString NoFollowActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes) { var customAttributes = new RouteValueDictionary(htmlAttributes) {{"rel", "nofollow"}}; var link = htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, customAttributes); return link; } 

所以在我看来我会这样:

 @Html.NoFollowActionLink("Link Text", "MyAction", "MyController") 

我期望它呈现如下链接:

 Link Text 

但相反,我得到:

 Link Text 

我已经尝试了将匿名类型转换为RouteValueDictionary的各种方法,添加到它然后将其传递到根ActionLink(…)方法或转换为Dictionary,或使用HtmlHelper.AnonymousObjectToHtmlAttributes并执行相同但似乎没有工作。

您得到的结果是由此源代码引起的:

 public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes) { return ActionLink(htmlHelper, linkText, actionName, controllerName, TypeHelper.ObjectToDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } 

如您所见,在内部调用HtmlHelper.AnonymousObjectToHtmlAttributes 。 这就是你传递RouteValueDictionary对象时得到valueskeys的原因。

只有两种方法匹配您的参数列表:

 public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes) public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary htmlAttributes) 

第二个重载对你的参数没有任何作用,只是传递它们。 您需要修改代码以调用其他重载:

 public static MvcHtmlString NoFollowActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues = null, object htmlAttributes = null) { var routeValuesDict = new RouteValueDictionary(routeValues); var customAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); if (!customAttributes.ContainsKey("rel")) customAttributes.Add("rel", "nofollow"); return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValuesDict, customAttributes); } 

当你传递routeValues作为RouteValueDictionary ,会选择另一个重载( RouteValueDictionary正在实现IDictionary所以它很好),并且返回的链接是正确的。

如果rel属性已经存在于htmlAttributes对象中,则会抛出exception:

 System.ArgumentException: An item with the same key has already been added. 

当您想要更新收到的htmlAttributes对象以便添加新属性(rel)时,您需要将匿名htmlAttributes对象转换为IDictionary (因为您无法向匿名对象添加新属性)。

这意味着您需要调用ActionLink方法的这个重载 ,这也需要将匿名routeValues转换为RouteValueDictionary

您可以使用new RouteValueDictionary(routeValues)轻松转换路径值。 要转换html属性,您需要一些reflection逻辑,例如在此问题中 。 (正如slawek在他的回答中已经提到的,你也可以利用RouteValueDictionary已经实现了一个字典并以相同的方式转换htmlAttributes)

最后你的扩展将是这样的:

 public static MvcHtmlString NoFollowActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues = null, object htmlAttributes = null) { var htmlAttributesDictionary = new Dictionary(); if (htmlAttributes != null) { foreach (var prop in htmlAttributes.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) { htmlAttributesDictionary.Add(prop.Name, prop.GetValue(htmlAttributes, null)); } } htmlAttributesDictionary["rel"] = "nofollow"; var routeValuesDictionary = new RouteValueDictionary(routeValues); var link = htmlHelper.ActionLink(linkText, actionName, controllerName, routeValuesDictionary, htmlAttributesDictionary); return link; } 

如果你这样称呼它:

 @Html.NoFollowActionLink("Link Text", "MyAction", "MyController", new { urlParam="param1" }, new { foo = "dummy" }) 

您将获得以下html:

 Link Text 

注意,因为在将原始属性添加到字典中之后添加/更新rel属性,即使调用者为属性指定了另一个值,它也总是强制rel="nofollow"

希望能帮助到你!