Reputation: 118
Is there a way to pass a collection of Enums to an ActionMethod (on a GET) automatically?
For example, if I have the following enum definition:
enum DaysEnum {Sat, Sun, Mon, Tue, Wed, Thu, Fri};
and, I have an ActionMethod definition of:
ActionResult SampleActionMethod ( List<DaysEnum> days)
Is there a way I could render, in a View, a URL that would pass in a collection of DayEnums. Something like:
var someDays = new List<DaysEnum> {DaysEnum.Sat, DaysEnum.Sun, DaysEnum.Mon};
Url.Route(new { days = someDays, controller="whatever", action="SampleActionMethod"});
The default model binder doesn't seem to support this, since I'm currently getting the following rendered:
http://.../System.Collections.Generic.List`1[DaysEnum]
I know I can do this by manually flattening the collection to, say, a dash-deliniated string, and then recreate the collection in the ActionMethod, but I was looking at something more elegant. Various blog posts talk about passing in collections, but that is more about when doing POSTS.
Upvotes: 1
Views: 3147
Reputation: 17784
I'm afraid complex data types can not be carried with GET request. You can use some hidden values and post the data if you do want to send these values as collection.
Upvotes: 0
Reputation: 11608
We do this using a custom model binder for the controller parameter, and an extension method to form the parameter in the URL.
The model binder looks like this:
/// <summary>
/// Custom binder class.
/// </summary>
public class DaysEnumModelBinder : IModelBinder
{
/// <summary>
/// Convert a comma-separated string to a list.
/// </summary>
/// <param name="rawValue">Raw value from binding context.</param>
/// <returns>List of enum values.</returns>
public static List<DaysEnum> ConvertArray(object rawValue)
{
var results = new List<DaysEnum>();
string[] query = rawValue as string[];
if (query != null && query.Length != 0)
{
string[] parts = query[0].Split(',');
foreach (string part in parts)
{
try
{
DaysEnum resultValue = (DaysEnum)Enum.Parse(typeof(DaysEnum), part, true);
results.Add(resultValue);
}
catch (ArgumentException)
{
}
}
}
return results;
}
/// <summary>
/// Implement IModelBinder to bind a comma-separated array of int values to a single array.
/// </summary>
/// <param name="controllerContext">The controller context.</param>
/// <param name="bindingContext">The binding context.</param>
/// <returns>Int array where applied to the correct type of object.</returns>
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName))
{
return new DefaultModelBinder().BindModel(controllerContext, bindingContext);
}
if (bindingContext.ModelType == typeof(DaysEnum[]))
{
List<DaysEnum> results = ConvertArray(bindingContext.ValueProvider.GetValue(bindingContext.ModelName).RawValue);
return results.ToArray();
}
return new DefaultModelBinder().BindModel(controllerContext, bindingContext);
}
}
Then in your controller method you hook up the binder like so:
ActionResult MyMethod([ModelBinder(typeof(DaysEnumModelBinder))] DaysEnum[] days)
{
...
}
Finally in your view render the URL with something like string.Join(",", days.Select(d => d.ToString()))
.
Upvotes: 2
Reputation: 1038870
<%= Html.ActionLink("test enums", "SampleActionMethod", new RouteValueDictionary {
{ "days[0]", DaysEnum.Sun }, { "days[1]", DaysEnum.Mon }
}) %>
Upvotes: 1