Добавление массива сложных типов в RouteValueDictionary
Мне было интересно, если есть элегантный способ добавить массив сложных типов в RouteValueDictionary или совместимый тип?
Например, если у меня есть класс и действие:
public class TestObject
{
public string Name { get; set; }
public int Count { get; set; }
public TestObject()
{
}
public TestObject(string name, int count)
{
this.Name = name;
this.Count = count;
}
}
public ActionResult Test(ICollection<TestObjects> t)
{
return View();
}
тогда я знаю, что если я вызову это действие через URL-адрес "/Test?t[0].Name=One&t[0].Count=1&t[1].Name=Two&t[1].Count=2", MVC отобразит эти параметры строки запроса возвращаются в тип ICollection автоматически. Однако, если я вручную создаю ссылку где-нибудь с помощью Url.Action() и хочу передать RouteValueDictionary параметров, когда я добавляю ICollection в RouteValueDictionary, Url.Action просто отображает ее как тип, например &t=System.Collections.Generic.List.
Например:
RouteValueDictionary routeValDict = new RouteValueDictionary();
List<TestObject> testObjects = new List<TestObject>();
testObjects.Add(new TestObject("One", 1));
testObjects.Add(new TestObject("Two", 2));
routeValDict.Add("t", testObjects);
// Does not properly create the parameters for the List<TestObject> collection.
string url = Url.Action("Test", "Test", routeValDict);
Есть ли способ заставить его автоматически отображать эту коллекцию в формате, который MVC также понимает, как отображать, или я должен сделать это вручную?
Чего мне не хватает, почему они сделали так, чтобы это красивое отображение существовало в действии, но не предоставило способ вручную работать в обратном направлении для создания URL-адресов?
2 ответа
Я также столкнулся с этой проблемой и использовал код Зака, но обнаружил в нем ошибку. Если IEnumerable является массивом строк (string[]), тогда возникает проблема. Так что я хотел бы поделиться своей расширенной версией.
public static RouteValueDictionary ToRouteValueDictionaryWithCollection(this RouteValueDictionary routeValues)
{
var newRouteValues = new RouteValueDictionary();
foreach(var key in routeValues.Keys)
{
object value = routeValues[key];
if(value is IEnumerable && !(value is string))
{
int index = 0;
foreach(object val in (IEnumerable)value)
{
if(val is string || val.GetType().IsPrimitive)
{
newRouteValues.Add(String.Format("{0}[{1}]", key, index), val);
}
else
{
var properties = val.GetType().GetProperties();
foreach(var propInfo in properties)
{
newRouteValues.Add(
String.Format("{0}[{1}].{2}", key, index, propInfo.Name),
propInfo.GetValue(val));
}
}
index++;
}
}
else
{
newRouteValues.Add(key, value);
}
}
return newRouteValues;
}
Что ж, я открыт для других (более элегантных) решений, но я все же заработал, приняв метод расширения, описанный в этом документе: /questions/38681242/aspnet-mvc-routedata-i-massivyi/38681273#38681273 и адаптировав его для использования отражения для свойств сложного типа вместо использования массивов примитивного типа.
Мой код:
public static RouteValueDictionary ToRouteValueDictionaryWithCollection(this RouteValueDictionary routeValues)
{
RouteValueDictionary newRouteValues = new RouteValueDictionary();
foreach (var key in routeValues.Keys)
{
object value = routeValues[key];
if (value is IEnumerable && !(value is string))
{
int index = 0;
foreach (object val in (IEnumerable)value)
{
PropertyInfo[] properties = val.GetType().GetProperties();
foreach (PropertyInfo propInfo in properties)
{
newRouteValues.Add(
String.Format("{0}[{1}].{2}", key, index, propInfo.Name),
propInfo.GetValue(val));
}
index++;
}
}
else
{
newRouteValues.Add(key, value);
}
}
return newRouteValues;
}