W MVC 3 masz po prostu pecha, ponieważ wartości trasy są przechowywane w RouteValueDictionary
, które jak sama nazwa wskazuje używa Dictionary
wewnętrznie, co uniemożliwia przypisanie wielu wartości do jednego klucza. Wartości trasy powinny być prawdopodobnie przechowywane w pliku NameValueCollection
, aby obsługiwać to samo zachowanie, co ciąg zapytania.
Jednak czy można nałożyć pewne ograniczenia na nazwy kategorii i jesteś w stanie obsługiwać ciąg kwerendy w formacie:
http://www.test.com/search/index?keywords=bla&categories=Cat1|Cat2
następnie można teoretycznie podłączyć go do Html.ActionLink
od MVC używa TypeDescriptor
które z kolei jest rozszerzalny w czasie wykonywania. Poniższy kod przedstawiono w celu wykazania, że jest to możliwe, , ale nie zalecałbym używania go pod, przynajmniej bez dalszego refaktoryzacji.
Mimo, że trzeba by zacząć od kojarzenia niestandardowy typ Opis Provider:
[TypeDescriptionProvider(typeof(SearchModelTypeDescriptionProvider))]
public class SearchModel
{
public string KeyWords { get; set; }
public IList<string> Categories { get; set; }
}
Implementacja dostawcy i niestandardowych deskryptora który nadpisuje deskryptor właściwości dla właściwości 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);
}
}
Następnie potrzebowalibyśmy niestandardowego deskryptora właściwości, aby móc zwrócić niestandardową wartość w GetValue
, która jest wewnętrznie wywoływana przez 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();
}
}
I wreszcie udowodnić, że działa przykładową aplikację, która naśladuje Wartości trasy tworzenia 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));
}
Cholera, to prawdopodobnie najdłuższy odpowiedź kiedykolwiek dać tutaj na SO.
Wygląda na duplikat: http://stackoverflow.com/q/1752721/25727. Zobacz pierwszą odpowiedź na rozwiązanie z niestandardowym HtmlHelper. – Jan