跳过 Html.ActionLink 的只读字段的生成

Skip generation of readonly fields for Html.ActionLink

我有一个基本请求对象 RequestBase 定义如下:

public abstract class RequestBase
{
    public abstract string Area { get; }
    public abstract string ActionName { get; }
    public abstract string LinkName { get; }
    public abstract string ControllerName { get; }
}

和这样的子类:

public class RequestTest : RequestBase
{
    public Guid Id { get; set; }

    public RequestTest()
    {
        Id = Guid.Empty;
    }

    #region implementation of RequestBase

    public override string Area
    {
        get { return "MyArea"; }
    }

    public override string ActionName
    {
        get { return "Overview"; }
    }

    public override string ControllerName
    {
        get { return "Test"; }
    }

    public override string LinkName
    {
        get { return "Click me for awesome"; }
    }

    #endregion
}

问题。我想写一个帮助程序以这种方式建立链接:

@Html.ActionLinkByRequest(new RequestTest{Id = Guid.Empty})

我目前已经实现了

public static MvcHtmlString ActionLinkByRequest(this HtmlHelper helper, RequestBase request, object htmlAttributes = null)
{
    return helper.ActionLink(request.LinkName, request.ActionName, request.ControllerName, request, htmlAttributes);
}

不幸的是它呈现为:

<a href="/MyArea/Test/Overview?EventId=00000000-0000-0000-0000-000000000000&ActionName=Overview&LinkName=Click%20me%20for%20awesome&ControllerName=Test">Click me for awesome</a>

但我只想

<a href="/MyArea/Test/Overview?EventId=00000000-0000-0000-0000-000000000000">Click me for awesome</a>

没有只读字段。因为它们是只读的,所以我不需要在查询字符串中显式地使用它们,因为我的 Action 等待具体实现 RequestTest 并且无论如何都会有它们:

public ActionResult Overview(RequestTest request)
{
    // do things here

    return View();
}

有什么想法可以跳过操作链接的只读字段的生成吗?也许通过某种方式使用反射?

通过评论编辑

这行得通吗?

public static MvcHtmlString ActionLinkByRequest(this HtmlHelper helper, RequestBase request, object htmlAttributes = null)
{
    var rvd = new RouteValueDictionary();

    foreach(var prop in request.GetType().GetProperties().Where(p => p.CanRead && p.CanWrite))
    {
        // add the property by name and the value
        // rvd.Add(prop.Name, prop.GetValue());
    }

    // add the area (check if not already existing)
    if(!rvd.ContainsKey("Area"))
        rvd.Add("Area", request.AreaName);

    return helper.ActionLink(request.LinkName, request.ActionName, request.ControllerName, request, htmlAttributes);
}
public static MvcHtmlString ActionLinkByRequest(this HtmlHelper helper, RequestBase request, object htmlAttributes = null)
{
    var rvd = new RouteValueDictionary(request);
    rvd.Remove("ActionName");
    rvd.Remove("ControllerName");
    rvd.Remove("LinkName");
    return helper.ActionLink(request.LinkName, request.ActionName, request.ControllerName, rvd, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}

在 Robert 的评论之后,我想出了以下对我有用的方法:

public static MvcHtmlString ActionLinkByRequest(this HtmlHelper helper, RequestBase request, object htmlAttributes = null)
{
    // create new value-dictionary to be passed
    var rvd = new RouteValueDictionary();

    // loop through properties of the request
    foreach (var prop in request.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead && p.CanWrite))
    {
        // add name/value to route-dictionary
        rvd.Add(prop.Name, prop.GetValue(request));
    }

    // check if area is needed
    if (!rvd.ContainsKey("Area"))
    {
        rvd.Add("Area", request.Area);
    }

    return helper.ActionLink(request.LinkText, request.ActionName, request.ControllerName, rvd, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}