sohail
sohail

Reputation: 118

Pass collection of enums to ASP.NET MVC ActionMethod

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

Answers (3)

Muhammad Adeel Zahid
Muhammad Adeel Zahid

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

Simon Steele
Simon Steele

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

Darin Dimitrov
Darin Dimitrov

Reputation: 1038870

<%= Html.ActionLink("test enums", "SampleActionMethod", new RouteValueDictionary { 
    { "days[0]", DaysEnum.Sun }, { "days[1]", DaysEnum.Mon } 
}) %>

Upvotes: 1

Related Questions