我有一个带有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
的属性,然后将它们传递给Controller。我现在要做的是创建一个ActionLink
,其中包含一个符合上述格式的查询字符串。
我的View从上面生成的查询字符串如下所示:
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, }, },
但是这些不会像上面那样生成查询字符串。
答案 0 :(得分: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
。
根据您的具体需求修改 可能的解决方案
扩展方法 完成工作
请注意,它使用静态框架方法ExpressionHelper
和ModelMetadata
(也由现有帮助程序使用)来确定默认模型绑定器将理解的适当名称和值属性分别。
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>
不是使用string.Format
而是使用TagBuilder
,而是应该对查询字符串进行编码以便在URL中安全传递,并且此扩展方法需要一些额外的验证,但我认为它可能是有用。另请注意,虽然此扩展方法是为MVC 4构建的,但可以轻松修改以前的版本。我没有意识到其中一个MVC标签是针对版本3的。
答案 1 :(得分:1)
您可以从FilterViewModel
实例创建一个RouteValueDictionary,然后使用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);
但需要注意的是,Add
,Remove
,TryGetValue
和this[string key]
方法不会被更改以考虑{{1} }。这可以通过定义这些方法的prefix
版本来实现,但由于它们不是虚拟的,它们只能从知道他们正在与new
而不是{{1}交谈的呼叫者那里工作。 }}