Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
454 views
in Technique[技术] by (71.8m points)

asp.net mvc 3 - Model binding comma separated query string parameter

How can I bind query string parameter that is comma separated value

http://localhost/Action?ids=4783,5063,5305

to a controller action expecting a list?

public ActionResult Action(List<long> ids)
{
    return View();
}

Note! ids in the controller action must a list (or something IEnumerable based), so string ids is not accepted as an answer because these parameters are passed to many actions and parsing string to an array would add unwanted noise.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here's my improved version of Nathan Taylor’s solution used in archil's answer.

  1. Nathan’s binder could only bind sub-properties of complex models, while mine can also bind individual controller arguments.
  2. My binder also gives you correct handling of empty parameters, by returning an actual empty instance of your array or IEnumerable.

To wire this up, you can either attach this to an individual Controller argument:

[ModelBinder(typeof(CommaSeparatedModelBinder))]

…or set it as the global default binder in Application_Start in global.asax.cs:

ModelBinders.Binders.DefaultBinder = new CommaSeparatedModelBinder();

In the second case it will try and handle all IEnumerables and fall back to ASP.NET MVC standard implementation for everything else.

Behold:

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

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        return BindCsv(bindingContext.ModelType, bindingContext.ModelName, bindingContext)
                ?? base.BindModel(controllerContext, bindingContext);
    }

    protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
    {
        return BindCsv(propertyDescriptor.PropertyType, propertyDescriptor.Name, bindingContext)
                ?? base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
    }

    private object BindCsv(Type type, string name, ModelBindingContext bindingContext)
    {
        if (type.GetInterface(typeof(IEnumerable).Name) != null)
        {
            var actualValue = bindingContext.ValueProvider.GetValue(name);

            if (actualValue != null)
            {
                var valueType = type.GetElementType() ?? type.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[] { ',' }))
                    {
                            if(!String.IsNullOrWhiteSpace(splitValue))
                                list.Add(Convert.ChangeType(splitValue, valueType));
                    }

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

        return null;
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...