通常我会想要使用数据库中的内容呈现ActionLink(想象一下Id / Name组合),所以我将把以下内容放在我的视图中:
@foreach (var row in Model)
{
<li>@Html.ActionLink(row.Name, "Action", "Controller", new { id = row.Id })</li>
}
但是,如果Name是空字符串,则会引发异常。有什么办法可以防止这种情况发生。我想覆盖ActionLink,但它是一个扩展,所以我无法覆盖。
有什么建议吗?
修改:
首先,我不控制数据,或者我会确保名称字段是必需的并且始终填充。不幸的是,这种情况并非如此。
其次,我意识到用户没有任何东西可以点击,但我相信渲染空链接比给他们YSOD更好。
答案 0 :(得分:9)
事实证明,实例方法总是优先于具有相同签名的扩展方法。
我能够加载CustomHtmlHelper并将实例方法ActionLink方法放在新类中:
public abstract class CustomWebViewPage<T> : WebViewPage<T>
{
public new CustomHtmlHelper<T> Html { get; set; }
public override void InitHelpers()
{
Ajax = new AjaxHelper<T>(ViewContext, this);
Url = new UrlHelper(ViewContext.RequestContext);
//Load Custom Html Helper instead of Default
Html = new CustomHtmlHelper<T>(ViewContext, this);
}
}
HtmlHelper如下(从Reflector复制的ActionLink方法没有LinkText错误检查:
public class CustomHtmlHelper<T> : HtmlHelper<T>
{
public CustomHtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer) :
base(viewContext, viewDataContainer)
{
}
//Instance methods will always be called instead of extension methods when both exist with the same signature...
public MvcHtmlString ActionLink(string linkText, string actionName)
{
return ActionLink(linkText, actionName, null, new RouteValueDictionary(), new RouteValueDictionary());
}
public MvcHtmlString ActionLink(string linkText, string actionName, object routeValues)
{
return ActionLink(linkText, actionName, null, new RouteValueDictionary(routeValues), new RouteValueDictionary());
}
public MvcHtmlString ActionLink(string linkText, string actionName, string controllerName)
{
return ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary());
}
public MvcHtmlString ActionLink(string linkText, string actionName, RouteValueDictionary routeValues)
{
return ActionLink(linkText, actionName, null, routeValues, new RouteValueDictionary());
}
public MvcHtmlString ActionLink(string linkText, string actionName, object routeValues, object htmlAttributes)
{
return ActionLink(linkText, actionName, null, new RouteValueDictionary(routeValues), AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public MvcHtmlString ActionLink(string linkText, string actionName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
{
return ActionLink(linkText, actionName, null, routeValues, htmlAttributes);
}
public MvcHtmlString ActionLink(string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
{
return ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(routeValues), AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public MvcHtmlString ActionLink(string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
{
return MvcHtmlString.Create(GenerateLink(ViewContext.RequestContext, RouteCollection, linkText, null, actionName, controllerName, routeValues, htmlAttributes));
}
public MvcHtmlString ActionLink(string linkText, string actionName, string controllerName, string protocol, string hostName, string fragment, object routeValues, object htmlAttributes)
{
return ActionLink(linkText, actionName, controllerName, protocol, hostName, fragment, new RouteValueDictionary(routeValues), AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public MvcHtmlString ActionLink(string linkText, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
{
return MvcHtmlString.Create(GenerateLink(ViewContext.RequestContext, RouteCollection, linkText, null, actionName, controllerName, protocol, hostName, fragment, routeValues, htmlAttributes));
}
}
最后,将pageBaseType设置为Views/Web.config
文件以使用新的自定义WebViewPage:
<system.web.webPages.razor>
...
<pages pageBaseType="Fully.Qualified.Namespace.CustomWebViewPage">
...
</pages>
</system.web.webPages.razor>
希望这有助于其他人。
答案 1 :(得分:4)
您将遇到的问题是,如果名称为空白,则无需在UI中为用户单击任何内容。示例:<a href="someUrl"></a>
没有给出可点击的对象。
如果您可以为那些空字符串的行提供默认的“No Name”字符串,那可能会更好。
@foreach (var row in Model)
{
<li>@Html.ActionLink(string.isNullOrEmpty(row.Name) ? "No Name" : row.Name, "Action", "Controller", new { id = row.Id })</li>
}
修改强>
如果您不想在每个@ Html.ActionLink中添加条件,您可以创建自己的@ Html.Helper
public static IHtmlString ActionLinkCheckNull(this HtmlHelper htmlHelper, string linkText, string action, string controller, object routeValues, object htmlAttributes)
{
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
var anchor = new TagBuilder("a") { InnerHtml = string.IsNullOrEmpty(linkText) ? "No Name", linktext };
anchor.Attributes["href"] = urlHelper.Action(action, controller, routeValues);
anchor.MergeAttributes(new RouteValueDictionary(htmlAttributes));
return MvcHtmlString.Create(anchor.ToString());
}
然后在您的Views文件夹web.config中,添加对您放置此扩展名的命名空间的引用,这样您就不必在每个视图的顶部都有一个using语句。例如:
<add namespace="Core.Extensions"/>
然后在您的观看中,使用
@Html.ActionLinkCheckNull(row.Name, "Action", "Controller", new { id = row.Id })
答案 2 :(得分:0)
为了进一步具体说明汤米所说的话,我想包括最终被称为受保护的方法:
private static string GenerateLinkInternal(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool includeImplicitMvcValues)
{
string value = UrlHelper.GenerateUrl(routeName, actionName, controllerName, protocol, hostName, fragment, routeValues, routeCollection, requestContext, includeImplicitMvcValues);
TagBuilder tagBuilder = new TagBuilder("a")
{
InnerHtml = (!string.IsNullOrEmpty(linkText)) ? HttpUtility.HtmlEncode(linkText) : string.Empty
};
tagBuilder.MergeAttributes<string, object>(htmlAttributes);
tagBuilder.MergeAttribute("href", value);
return tagBuilder.ToString(TagRenderMode.Normal);
}
所以,既然你正在构建一个链接,那么外部方法会抛出异常,因为它不会在屏幕上显示任何文本。该代码如下所示:
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
{
if (string.IsNullOrEmpty(linkText))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "linkText");
}
return MvcHtmlString.Create(HtmlHelper.GenerateLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, null, actionName, controllerName, routeValues, htmlAttributes));
}
从技术上讲,代码不会必须受保护,但它是功能。
以下是针对您的解决方法,但您需要构建自己的ActionLink
扩展方法,并将其放在项目中的某个代码文件中:
using System;
using System.Collections.Generic;
using System.Web.Mvc.Resources;
using System.Web.Routing;
namespace System.Web.Mvc.Html
{
public static class MyLinkExtensions
{
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, int identifier)
{
return htmlHelper.ActionLink(string.IsNullOrEmpty(linkText) ? "some default text" : linkText, actionName, controllerName, new { id = identifier });
}
}
}
所以现在你的标记看起来像这样:
@foreach (var row in Model)
{
<li>@Html.ActionLink(row.Name, "Action", "Controller", row.Id)</li>
}
并且您已封装了该转发以确定linkText
的状态,但仍然将您的链接呈现在一行中。