Asp.net mvc 在传递到Html.ActionLink时序列化模型上的IList属性
我正在尝试使用以下viewmodel生成Html.ActionLink:Asp.net mvc 在传递到Html.ActionLink时序列化模型上的IList属性,asp.net-mvc,asp.net-mvc-routing,Asp.net Mvc,Asp.net Mvc Routing,我正在尝试使用以下viewmodel生成Html.ActionLink: public class SearchModel { public string KeyWords {get;set;} public IList<string> Categories {get;set;} } 其中Model是SearchModel的一个实例 生成的链接如下所示: 因为它显然只对每个属性调用ToString方法 我希望看到的是: 我有没有办法通过使用Html.Action
public class SearchModel
{
public string KeyWords {get;set;}
public IList<string> Categories {get;set;}
}
其中Model是SearchModel的一个实例
生成的链接如下所示:
因为它显然只对每个属性调用ToString方法
我希望看到的是:
我有没有办法通过使用
Html.ActionLink
和linq一起实现这一点?当然
string.Join("", Model.Categories.Select(c=>"&categories="+c))
在MVC 3中,您只是运气不佳,因为路由值存储在
RouteValueDictionary
中,顾名思义,它在内部使用字典,这使得不可能将多个值与单个键关联。路由值可能应该存储在NameValueCollection
中,以支持与查询字符串相同的行为
但是,如果可以对类别名称施加一些约束,并且能够支持以下格式的查询字符串:
http://www.test.com/search/index?keywords=bla&categories=Cat1|Cat2
然后理论上可以将它插入Html.ActionLink
,因为MVC使用TypeDescriptor
,而TypeDescriptor在运行时是可扩展的。下面的代码演示了它的可行性,但我不建议使用它,至少在没有进一步重构的情况下
话虽如此,您需要从关联自定义类型描述提供程序开始:
[TypeDescriptionProvider(typeof(SearchModelTypeDescriptionProvider))]
public class SearchModel
{
public string KeyWords { get; set; }
public IList<string> Categories { get; set; }
}
然后,我们需要自定义属性描述符能够在GetValue
中返回一个自定义值,该值由MVC内部调用:
class ListPropertyDescriptor : PropertyDescriptor
{
public ListPropertyDescriptor(string name)
: base(name, new Attribute[] { }) { }
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { throw new NotImplementedException(); }
}
public override object GetValue(object component)
{
var property = component.GetType().GetProperty(this.Name);
var list = (IList<string>)property.GetValue(component, null);
return string.Join("|", list);
}
public override bool IsReadOnly { get { return false; } }
public override Type PropertyType
{
get { throw new NotImplementedException(); }
}
public override void ResetValue(object component) { }
public override void SetValue(object component, object value) { }
public override bool ShouldSerializeValue(object component)
{
throw new NotImplementedException();
}
}
类ListPropertyDescriptor:PropertyDescriptor
{
公共ListPropertyDescriptor(字符串名称)
:base(名称,新属性[]{}{}
公共覆盖布尔CanResetValue(对象组件)
{
返回false;
}
公共重写类型ComponentType
{
获取{抛出新的NotImplementedException();}
}
公共覆盖对象GetValue(对象组件)
{
var property=component.GetType().GetProperty(this.Name);
var list=(IList)property.GetValue(component,null);
返回字符串。Join(“|”,list);
}
公共重写bool IsReadOnly{get{return false;}}
公共覆盖类型PropertyType
{
获取{抛出新的NotImplementedException();}
}
公共重写无效重置值(对象组件){}
公共重写void SetValue(对象组件,对象值){}
公共重写bool ShouldSerializeValue(对象组件)
{
抛出新的NotImplementedException();
}
}
最后,为了证明它是一个模拟MVC路由值创建的示例应用程序:
static void Main(string[] args)
{
var model = new SearchModel { KeyWords = "overengineering" };
model.Categories = new List<string> { "1", "2", "3" };
var properties = TypeDescriptor.GetProperties(model);
var dictionary = new Dictionary<string, object>();
foreach (PropertyDescriptor p in properties)
{
dictionary.Add(p.Name, p.GetValue(model));
}
// Prints: KeyWords, Categories
Console.WriteLine(string.Join(", ", dictionary.Keys));
// Prints: overengineering, 1|2|3
Console.WriteLine(string.Join(", ", dictionary.Values));
}
static void Main(字符串[]args)
{
var模型=新搜索模型{KeyWords=“overengineering”};
model.Categories=新列表{“1”、“2”、“3”};
var properties=TypeDescriptor.GetProperties(模型);
var dictionary=newdictionary();
foreach(属性中的PropertyDescriptor p)
{
Add(p.Name,p.GetValue(model));
}
//印刷品:关键词、类别
WriteLine(string.Join(“,”,dictionary.Keys));
//版画:过度设计,1 | 2 | 3
WriteLine(string.Join(“,”,dictionary.Values));
}
该死,这可能是我在这里给出的最长答案。看起来像是重复的:。请参阅使用自定义HtmlHelper的解决方案的第一个答案。如何将其插入到Html.ActionLink调用中?嗯,我不知道,我会用手写的方式感谢您的回答。它非常接近于让它吐出正确的值,只是如果使用|作为分隔符,那么在返回的过程中必须使用自定义模型绑定器,所以我更改了版本,使分隔符为string.Format(&{0},property.Name)。。。工作起来很有魅力!“过度工程”这个词在这里确实有它的位置:)这是一个可笑的复杂的解决方案,可以用linq来解决:)但是很好的概念证明。@rouen这不是过度工程,实际上linq也不能解决。字符串的连接是,但这不是全部问题,它只是问题的一部分。另一部分是让它在路由模型中作为一等公民使用。这就是乔试图证明的
class ListPropertyDescriptor : PropertyDescriptor
{
public ListPropertyDescriptor(string name)
: base(name, new Attribute[] { }) { }
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { throw new NotImplementedException(); }
}
public override object GetValue(object component)
{
var property = component.GetType().GetProperty(this.Name);
var list = (IList<string>)property.GetValue(component, null);
return string.Join("|", list);
}
public override bool IsReadOnly { get { return false; } }
public override Type PropertyType
{
get { throw new NotImplementedException(); }
}
public override void ResetValue(object component) { }
public override void SetValue(object component, object value) { }
public override bool ShouldSerializeValue(object component)
{
throw new NotImplementedException();
}
}
static void Main(string[] args)
{
var model = new SearchModel { KeyWords = "overengineering" };
model.Categories = new List<string> { "1", "2", "3" };
var properties = TypeDescriptor.GetProperties(model);
var dictionary = new Dictionary<string, object>();
foreach (PropertyDescriptor p in properties)
{
dictionary.Add(p.Name, p.GetValue(model));
}
// Prints: KeyWords, Categories
Console.WriteLine(string.Join(", ", dictionary.Keys));
// Prints: overengineering, 1|2|3
Console.WriteLine(string.Join(", ", dictionary.Values));
}