3

I want to set up a ASP.NET MVC route that looks like:

routes.MapRoute(
  "Default", // Route name
  "{controller}/{action}/{idl}", // URL with parameters
  new { controller = "Home", action = "Index", idl = UrlParameter.Optional } // Parameter defaults
);

That routes requests that look like this...

Example/GetItems/1,2,3

...to my controller action:

public class ExampleController : Controller
{
    public ActionResult GetItems(List<int> id_list)
    {
        return View();
    }
}

The question is, what do I set up to transform the idl url parameter from a string into List<int> and call the appropriate controller action?

I have seen a related question here that used OnActionExecuting to preprocess a string, but did not change the type. I don't think that will work for me here, because when I override OnActionExecuting in my controller and inspect the ActionExecutingContext parameter, I see that the ActionParameters dictionary already has an idl key with a null value- presumably, an attempted cast from string to List<int>... this is the part of the routing I want to be in control of.

Is this possible?

2 Answers 2

8

A nice version is to implement your own Model Binder. You can find a sample here

I try to give you an idea:

public class MyListBinder : IModelBinder
{   
     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
     {   
        string integers = controllerContext.RouteData.Values["idl"] as string;
        string [] stringArray = integers.Split(',');
        var list = new List<int>();
        foreach (string s in stringArray)
        {
           list.Add(int.Parse(s));
        }
        return list;  
     }  
}


public ActionResult GetItems([ModelBinder(typeof(MyListBinder))]List<int> id_list) 
{ 
    return View(); 
} 
Sign up to request clarification or add additional context in comments.

Comments

3

Like slfan's says, a custom model binder is the way to go. Here's a another approach from my blog which is generic and supports multiples data types. It also elegantly falls back to default the model binding implementation:

public class CommaSeparatedValuesModelBinder : DefaultModelBinder
{
    private static readonly MethodInfo ToArrayMethod = typeof(Enumerable).GetMethod("ToArray");

    protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
    {
        if (propertyDescriptor.PropertyType.GetInterface(typeof(IEnumerable).Name) != null)
        {
            var actualValue = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);

            if (actualValue != null && !String.IsNullOrWhiteSpace(actualValue.AttemptedValue) && actualValue.AttemptedValue.Contains(","))
            {
                var valueType = propertyDescriptor.PropertyType.GetElementType() ?? propertyDescriptor.PropertyType.GetGenericArguments().FirstOrDefault();

                if (valueType != null && valueType.GetInterface(typeof(IConvertible).Name) != null)
                {
                    var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(valueType));

                    foreach (var splitValue in actualValue.AttemptedValue.Split(new[] { ',' }))
                    {
                        list.Add(Convert.ChangeType(splitValue, valueType));
                    }

                    if (propertyDescriptor.PropertyType.IsArray)
                    {
                        return ToArrayMethod.MakeGenericMethod(valueType).Invoke(this, new[] { list });
                    }
                    else
                    {
                        return list;
                    }
                }
            }
        }

        return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.