Вот моя улучшенная версия решения Натана Тейлора, использованная в ответе Арчила.
- Связыватель Натана мог связывать только подсвойства сложных моделей,
в то время как мой может также связать отдельные аргументы контроллера.
- Мой компоновщик также дает вам правильную обработку пустых параметров, возвращая
фактический пустой экземпляр вашего массива или IEnumerable.
Чтобы подключить это, вы можете присоединить это к отдельному аргументу контроллера:
[ModelBinder(typeof(CommaSeparatedModelBinder))]
… или установите его в качестве глобального связующего по умолчанию в Application_Start в global.asax.cs:
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;
}
}