ASP.Net MVC RouteData и массивы
Если у меня есть действие как это:
public ActionResult DoStuff(List<string> stuff)
{
...
ViewData["stuff"] = stuff;
...
return View();
}
Я могу поразить это следующим URL:
http://mymvcapp.com/controller/DoStuff?stuff=hello&stuff=world&stuff=foo&stuff=bar
Но в моей ViewPage у меня есть этот код:
<%= Html.ActionLink("click here", "DoMoreStuff", "MoreStuffController", new { stuff = ViewData["stuff"] }, null) %>
К сожалению, MVC не достаточно умен, чтобы признать, что действие принимает массив, и развертывает список, чтобы сформировать правильный URL-маршрут. вместо этого он просто выполняет.ToString () для объекта, который просто перечисляет тип данных в случае List.
Есть ли способ получить Html.ActionLink для генерации правильного URL, когда один из параметров целевого действия является массивом или списком?
-- редактировать --
Как указал Джош ниже, ViewData["stuff"] - это просто объект. Я попытался упростить проблему, но вместо этого вызвал не связанную ошибку! Я на самом деле использую выделенную ViewPage
<%= Html.ActionLink("click here", "DoMoreStuff", "MoreStuffController", new { stuff = ViewData.Model.Stuff }, null) %>
Где ViewData.Model.Stuff печатается в виде списка
6 ответов
Я думаю, что пользовательский HtmlHelper будет в порядке.
public static string ActionLinkWithList( this HtmlHelper helper, string text, string action, string controller, object routeData, object htmlAttributes )
{
var urlHelper = new UrlHelper( helper.ViewContext.RequestContext );
string href = urlHelper.Action( action, controller );
if (routeData != null)
{
RouteValueDictionary rv = new RouteValueDictionary( routeData );
List<string> urlParameters = new List<string>();
foreach (var key in rv.Keys)
{
object value = rv[key];
if (value is IEnumerable && !(value is string))
{
int i = 0;
foreach (object val in (IEnumerable)value)
{
urlParameters.Add( string.Format( "{0}[{2}]={1}", key, val, i ));
++i;
}
}
else if (value != null)
{
urlParameters.Add( string.Format( "{0}={1}", key, value ) );
}
}
string paramString = string.Join( "&", urlParameters.ToArray() ); // ToArray not needed in 4.0
if (!string.IsNullOrEmpty( paramString ))
{
href += "?" + paramString;
}
}
TagBuilder builder = new TagBuilder( "a" );
builder.Attributes.Add("href",href);
builder.MergeAttributes( new RouteValueDictionary( htmlAttributes ) );
builder.SetInnerText( text );
return builder.ToString( TagRenderMode.Normal );
}
Вы можете суффикс routevalues
с индексом массива примерно так:
RouteValueDictionary rv = new RouteValueDictionary();
rv.Add("test[0]", val1);
rv.Add("test[1]", val2);
это приведет к строке запроса, содержащей test=val1&test=val2
что может помочь?
Объединение обоих методов работает хорошо.
public static RouteValueDictionary FixListRouteDataValues(RouteValueDictionary routes)
{
var newRv = new RouteValueDictionary();
foreach (var key in routes.Keys)
{
object value = routes[key];
if (value is IEnumerable && !(value is string))
{
int index = 0;
foreach (string val in (IEnumerable)value)
{
newRv.Add(string.Format("{0}[{1}]", key, index), val);
index++;
}
}
else
{
newRv.Add(key, value);
}
}
return newRv;
}
Затем используйте этот метод в любом методе расширения, который требует routeValues с IEnumerable(s) в нем.
К сожалению, этот обходной шов понадобится и в MVC3.
Это будет просто расширением UrlHelper
и просто предоставьте хороший URL-адрес, готовый для размещения в любом месте, а не целый тег, также он сохранит большинство других значений маршрута для любых других используемых конкретных URL-адресов... предоставляя вам наиболее удобный конкретный URL-адрес, который у вас есть (минус IEnumerable values), а затем просто добавьте значения строки запроса в конце.
public static string ActionWithList(this UrlHelper helper, string action, object routeData)
{
RouteValueDictionary rv = new RouteValueDictionary(routeData);
var newRv = new RouteValueDictionary();
var arrayRv = new RouteValueDictionary();
foreach (var kvp in rv)
{
var nrv = newRv;
var val = kvp.Value;
if (val is IEnumerable && !(val is string))
{
nrv = arrayRv;
}
nrv.Add(kvp.Key, val);
}
string href = helper.Action(action, newRv);
foreach (var kvp in arrayRv)
{
IEnumerable lst = kvp.Value as IEnumerable;
var key = kvp.Key;
foreach (var val in lst)
{
href = href.AddQueryString(key, val);
}
}
return href;
}
public static string AddQueryString(this string url, string name, object value)
{
url = url ?? "";
char join = '?';
if (url.Contains('?'))
join = '&';
return string.Concat(url, join, name, "=", HttpUtility.UrlEncode(value.ToString()));
}
Существует библиотека с именем Unbinder, которую вы можете использовать для вставки сложных объектов в маршруты / URL-адреса.
Это работает так:
using Unbound;
Unbinder u = new Unbinder();
string url = Url.RouteUrl("routeName", new RouteValueDictionary(u.Unbind(YourComplexObject)));
Я не на своей рабочей станции, но как насчет чего-то вроде:
<%= Html.ActionLink("click here", "DoMoreStuff", "MoreStuffController", new { stuff = (List<T>)ViewData["stuff"] }, null) %>
или набрал:
<%= Html.ActionLink("click here", "DoMoreStuff", "MoreStuffController", new { stuff = (List<T>)ViewData.Model.Stuff }, null) %>