问题
I have the following problem: one of the system I'm working in most important features is a search page. In this page I have some options, like records per page, starting date, ending date, and the problematic one: type. One must have the possibility to choose more than one type (most of the time, all of them will be selected). To make that work, i created the following:
<div>
<label>Eventos:</label>
<div>
@Html.ListBox("events", Model.Events, new { style = "width: 100%" })
</div>
</div>
It creates a listbox where I can choose more than one option, and when the form is submited, my query string will look like this:
/5?period=9&events=1&events=3&recordsPerPage=10
There it is possible to see that two events (which is the type I was talking before) are created. The action method to this page takes a List<long>
as one of its arguments, which represents that two events
values. The problem begins when I want to use that with MVC Contrib. Their pager works just fine, but as I was requested, I created another pager, which displays links to five pages after and before the one the user is at. To do this, in a part of my code I have to do the following (which is very similar to the MVC Contrib pager, that works):
public RouteValueDictionary GetRoute(int page)
{
var routeValues = new RouteValueDictionary();
foreach (var key in Context.Request.QueryString.AllKeys.Where(key => key != null))
{
routeValues[key] = Context.Request.QueryString[key];
}
routeValues["page"] = page;
return routeValues;
}
And then:
@Html.ActionLink(page.ToString(), action, controller, GetRoute(page), null)
The problem is that it is a Dictionary, which makes the second time I set the value for routeValues["events"]
erase the previous.
Do you guys have any idea on how to work with it?
回答1:
Very good question. Unfortunately it is not easy to generate an url which has multiple query string parameters with the same name using the Html.ActionLink
helper. So I can see two possible solutions:
Write a custom model binder for
long[]
that is capable of parsing a comma separated values. This way you can keep yourGetRoute
method which will generate the following url:period=9&events=1%2C3&recordsPerPage=10&page=5
.public class CommaSeparatedLongArrayModelBinder : DefaultModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var values = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (values != null && !string.IsNullOrEmpty(values.AttemptedValue)) { // TODO: A minimum of error handling would be nice here return values.AttemptedValue.Split(',').Select(x => long.Parse(x)).ToArray(); } return base.BindModel(controllerContext, bindingContext); } }
which you will register in
Application_Start
:ModelBinders.Binders.Add(typeof(long[]), new CommaSeparatedLongArrayModelBinder());
and then the following controller action will be able to understand the previous URL:
public ActionResult Foo(long[] events, int page, int period, int recordsPerPage) { ... }
Manually generate this anchor:
<a href="@string.Format("{0}?{1}&page=5", Url.Action("action", "controller"), Request.QueryString)">abc</a>
回答2:
Try looking at WinTellect's PowerCollections, allows you to create a MultiDictionary, still can't have duplicate keys, but you can have multiple values per key.
回答3:
You should write either extension methods that target the routeValue collection or a custom model binder that transforms your Event parameter into a list always. If you view Event always being a list, just a commonly length 1 list will alleviate most of the problems you face.
At this point you will just interact with a list interface. You could then write a custom binder to allow you to directly place that into the route correctly or you could unpack the list back into the query string. There's a software project based on this called Unbinder for unpacking objects into property/value pairs that you can easily use in query strings or other purposes.
来源:https://stackoverflow.com/questions/6048218/how-to-deal-with-more-than-one-value-per-key-in-asp-net-mvc-3