6

我有一个带有 Filter 属性的 ViewModel,该属性具有许多用于过滤数据的属性

例子:

class MyViewModel : IHasFilter
{
     public MyData[] Data { get; set; }
     public FilterViewModel Filter { get; set; }
}

class FilterViewModel
{
    public String MessageFilter { get; set; }
    //etc.
}

这在使用我的视图时工作正常。我可以设置属性Model.Filter并将它们传递给控制器​​。我现在要做的是创建一个ActionLink具有上述格式的查询字符串。

我的视图从上面生成的查询字符串如下所示:

http://localhost:51050/?Filter.MessageFilter=Stuff&Filter.OtherProp=MoreStuff

我需要在一个不同的视图中为网格中的每一行生成一个 ActionLink,这些行转到上面的视图。

我试过了:

Html.ActionLink(
    item.Message,
    "Index",
    "Home",
    new { Filter = new { MessageFilter = item.Message, }, },
    null);

我还尝试将routeValues参数设置为:

new MyViewModel { Filter = new FilterViewModel { MessageFilter = item.Message, }, },

但是这些不会像上面那样生成查询字符串。

4

2 回答 2

2

有趣的问题(+1)。我假设目的是使用默认模型绑定器将查询字符串参数绑定到您的Action参数。

开箱即用我不相信该ActionLink方法会为您做到这一点(当然没有什么可以阻止您自己滚动)。查看反射器我们可以看到,当object被添加到 时RouteValueDictionary,只添加了键值对。这是添加键值对的代码,您可以看到没有遍历对象属性。

foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
{
    object obj2 = descriptor.GetValue(values);
    this.Add(descriptor.Name, obj2);
}

所以对于你的对象

var values = new { Filter = new Filter { MessageFilter = item.Message } }

添加的键是Filter,值是您的Filter对象,它将评估为您的对象类型的完全限定名称。

这样做的结果是Filter=Youre.Namespace.Filter

根据您的确切需求编辑可能的解决方案


扩展方法 完成工作

请注意,它使用静态框架方法ExpressionHelperModelMetadata(现有助手也使用)来确定默认模型绑定器将分别理解的适当名称和属性值。

public static class ExtentionMethods
{
    public static MvcHtmlString ActionLink<TModel, TProperty>(
        this HtmlHelper<TModel> helper,
        string linkText,
        string actionName,
        string controllerName,
        params Expression<Func<TModel, TProperty>>[] expressions)
    {
        var urlHelper = new UrlHelper(helper.ViewContext.HttpContext.Request.RequestContext);

        var url = urlHelper.Action(actionName, controllerName);

        if (expressions.Any())
        {
            url += "?";

            foreach (var expression in expressions)
            {
                var result = ExpressionHelper.GetExpressionText(expression);

                var metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, helper.ViewData);

                url = string.Concat(url, result, "=", metadata.SimpleDisplayText, "&");
            }

            url = url.TrimEnd('&');
        }

        return new MvcHtmlString(string.Format("<a href='{0}'>{1}</a>", url, linkText));
    }
}

样品模型

public class MyViewModel
{
    public string SomeProperty { get; set; }

    public FilterViewModel Filter { get; set; }
}

public class FilterViewModel
{
    public string MessageFilter { get; set; }
}

行动

public ActionResult YourAction(MyViewModel model)
{
    return this.View(
        new MyViewModel
        {
            SomeProperty = "property value",
            Filter = new FilterViewModel
            {
                MessageFilter = "stuff"
            }
        });
}

用法

params可以通过方法的最后一个参数将任意数量的视图模型属性添加到查询字符串中。

@this.Html.ActionLink(
    "Your Link Text",
    "YourAction",
    "YourController",
    x => x.SomeProperty,
    x => x.Filter.MessageFilter)

标记

<a href='/YourAction/YourController?SomeProperty=some property value&Filter.MessageFilter=stuff'>Your Link Text</a>

应该对查询字符串进行编码以安全地在 URL 中传递,而不是使用string.Formatyou could use TagBuilder,并且此扩展方法需要一些额外的验证,但我认为它可能很有用。另请注意,尽管此扩展方法是为 MVC 4 构建的,但它可以很容易地针对以前的版本进行修改。直到现在,我才意识到其中一个 MVC 标签是针对版本 3 的。

于 2012-04-04T12:39:23.673 回答
1

您可以从一个实例创建一个 RouteValueDictionary FilterViewModel,然后使用ToDictionary它传递给另一个 RouteValues,所有键都以 . 为前缀'Filter.'

更进一步,您可以构造一个RouteValueDictionary接受前缀的特殊覆盖(因此使其对其他场景更有用):

public class PrefixedRouteValueDictionary : RouteValueDictionary
{
  public PrefixedRouteValueDictionary(string prefix, object o)
    : this(prefix, new RouteValueDictionary(o))
  { }

  public PrefixedRouteValueDictionary(string prefix, IDictionary<string, object> d)
    : base(d.ToDictionary(kvp=>(prefix ?? "") + kvp.Key, kvp => kvp.Value))
  { }
}

有了它,您现在可以执行以下操作:

Html.ActionLink( 
  item.Message, 
  "Index", 
  "Home", 
  new PrefixedRouteValueDictionary("Filter.", 
    new FilterViewModel() { MessageFilter = item.Message }), 
  null); 

但是,需要注意的是AddRemoveTryGetValuethis[string key]方法不会被更改以考虑prefix. 这可以通过定义new这些方法的版本来实现,但是因为它们不是虚拟的,所以它们只能由知道他们正在与 aPrefixedRouteValueDictionary而不是 a交谈的调用者工作RouteValueDictionary

于 2012-04-04T12:58:48.483 回答