这是我在 Archil 的回答中使用的 Nathan Taylor 解决方案的改进版本。
- Nathan 的绑定器只能绑定复杂模型的子属性,
而我的也可以绑定单独的控制器参数。
- 我的活页夹还可以让您正确处理空参数,方法是返回一个
数组或 IEnumerable 的实际空实例。
要将其连接起来,您可以将其附加到单个控制器参数:
[ModelBinder(typeof(CommaSeparatedModelBinder))]
…或在 global.asax.cs 中的 Application_Start 中将其设置为全局默认绑定器:
ModelBinders.Binders.DefaultBinder = new CommaSeparatedModelBinder();
在第二种情况下,它将尝试处理所有 IEnumerables 并回退到其他所有内容的 ASP.NET MVC 标准实现。
看:
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;
}
}