我正在尝试使用以下viewmodel生成Html.ActionLink:
public class SearchModel
{
public string KeyWords {get;set;}
public IList<string> Categories {get;set;}
}
要生成我的链接,请使用以下调用:
@Html.ActionLink("Index", "Search", Model)
其中Model是SearchModel的实例
生成的链接是这样的:
http://www.test.com/search/index?keywords=bla&categories=System.Collections.Generic.List
因为它显然只是在每个属性上调用ToString方法。
我希望看到的是:
http://www.test.com/search/index?keywords=bla&categories=Cat1&categories=Cat2
有什么办法可以通过Html.ActionLink
答案 0 :(得分:2)
在MVC 3中,您只是运气不好,因为路由值存储在RouteValueDictionary
中,顾名思义内部使用Dictionary
,这使得无法将多个值关联到一把钥匙。路由值可能应存储在NameValueCollection
中,以支持与查询字符串相同的行为。
但是,如果您可以对类别名称施加一些约束,并且您能够以以下格式支持查询字符串:
http://www.test.com/search/index?keywords=bla&categories=Cat1|Cat2
然后你理论上可以将它插入Html.ActionLink
,因为MVC使用TypeDescriptor
,而[TypeDescriptionProvider(typeof(SearchModelTypeDescriptionProvider))]
public class SearchModel
{
public string KeyWords { get; set; }
public IList<string> Categories { get; set; }
}
在运行时又是可扩展的。下面的代码是为了证明它是可能的,但我不建议使用它,至少不需要进一步重构。
话虽如此,您需要首先关联自定义类型描述提供程序:
Categories
提供程序的实现和覆盖class SearchModelTypeDescriptionProvider : TypeDescriptionProvider
{
public override ICustomTypeDescriptor GetTypeDescriptor(
Type objectType, object instance)
{
var searchModel = instance as SearchModel;
if (searchModel != null)
{
var properties = new List<PropertyDescriptor>();
properties.Add(TypeDescriptor.CreateProperty(
objectType, "KeyWords", typeof(string)));
properties.Add(new ListPropertyDescriptor("Categories"));
return new SearchModelTypeDescriptor(properties.ToArray());
}
return base.GetTypeDescriptor(objectType, instance);
}
}
class SearchModelTypeDescriptor : CustomTypeDescriptor
{
public SearchModelTypeDescriptor(PropertyDescriptor[] properties)
{
this.Properties = properties;
}
public PropertyDescriptor[] Properties { get; set; }
public override PropertyDescriptorCollection GetProperties()
{
return new PropertyDescriptorCollection(this.Properties);
}
}
属性的属性描述符的自定义描述符:
GetValue
然后我们需要自定义属性描述符能够返回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();
}
}
中由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));
}
最后证明它适用于模仿MVC路由值创建的示例应用程序:
{{1}}
该死的,这可能是我在这里给出的最长的答案。
答案 1 :(得分:0)
当然还有linq ......
string.Join("", Model.Categories.Select(c=>"&categories="+c))