5

通常我会想用数据库中的内容渲染一个 ActionLink(想象一个 Id/Name 组合),所以我会在我的视图中添加以下内容:

        @foreach (var row in Model)
        {
            <li>@Html.ActionLink(row.Name, "Action", "Controller", new { id = row.Id })</li>
        }

但是,如果 Name 为空字符串,则会引发异常。有什么办法可以防止这种情况。我想覆盖 ActionLink,但它是一个扩展,所以我无法覆盖。

有什么建议么?

编辑:

首先,我不控制数据,否则我会确保 Name 字段是必需的并且始终填充。不幸的是,这种情况并非如此。

其次,我意识到用户将没有什么可以点击的,但我相信呈现一个空链接比给他们一个 YSOD 更好的选择。

4

3 回答 3

10

事实证明,实例方法总是优于具有相同签名的扩展方法。

我能够加载 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));
    }
}

最后,在文件中设置 pageBaseTypeViews/Web.config以使用新的自定义 WebViewPage:

  <system.web.webPages.razor>
    ...
    <pages pageBaseType="Fully.Qualified.Namespace.CustomWebViewPage">
    ...
    </pages>
  </system.web.webPages.razor>

希望这对其他人有帮助。

于 2013-04-11T15:31:19.180 回答
4

The issue you are going to have is if the name is blank, there is nothing to click on for the user in the UI. Example: <a href="someUrl"></a> gives no clickable object.

What may be better is if you could provide a default "No Name" string to those rows that have an empty string.

@foreach (var row in Model)
        {
            <li>@Html.ActionLink(string.isNullOrEmpty(row.Name) ? "No Name" : row.Name, "Action", "Controller", new { id = row.Id })</li>
        }

EDIT

If you do not want to put a condition in every @Html.ActionLink, you can create your own @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());
        }

Then in your Views folder web.config, add a reference to the namespace that you place this extension so you don't have to have a using statement at the top of every view. Example:

<add namespace="Core.Extensions"/>

Then in your views, use

 @Html.ActionLinkCheckNull(row.Name, "Action", "Controller", new { id = row.Id })
于 2013-04-11T15:06:19.973 回答
0

为了更具体地说明 Tommy 的意思,我想包含受保护的最终调用方法:

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>
}

并且您已经封装了 turnary 以确定状态,linkText但仍然在一行中呈现您的链接。

于 2013-04-11T15:11:37.350 回答