Created
January 4, 2011 13:29
-
-
Save jagregory/764758 to your computer and use it in GitHub Desktop.
Custom MVC1 model binder that handles array properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ArrayModelBinder : IFilteredPropertyBinder | |
{ | |
private static readonly Regex IdRegex = new Regex("(?<=_)[0-9]+(?=_)"); | |
public bool IsMatch(Type modelType) | |
{ | |
return modelType.IsArray; | |
} | |
public void BindProperty(IModelBinder binder, ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) | |
{ | |
var elementType = propertyDescriptor.PropertyType.GetElementType(); | |
var namePrefix = char.ToLower(propertyDescriptor.Name[0]) + propertyDescriptor.Name.Substring(1); | |
var list = new ArrayList(); | |
var relatedFormValues = GetRelatedFormPairs(namePrefix, bindingContext.ValueProvider); | |
var groupedFormValues = GroupByIndex(relatedFormValues); | |
foreach (var elementValues in groupedFormValues.OrderBy(x => x.Key)) | |
{ | |
var modelBindingContext = new ModelBindingContext | |
{ | |
ValueProvider = new PrefixedValueProvider(namePrefix + "_" + elementValues.Key + "_", elementValues), | |
ModelType = elementType | |
}; | |
var model = binder.BindModel(controllerContext, modelBindingContext); | |
list.Add(model); | |
} | |
var array = list.ToArray(elementType); | |
propertyDescriptor.SetValue(bindingContext.Model, array); | |
} | |
static IEnumerable<KeyValuePair<string, ValueProviderResult>> GetRelatedFormPairs(string namePrefix, IEnumerable<KeyValuePair<string, ValueProviderResult>> valueProvider) | |
{ | |
return valueProvider | |
.Where(kvp => kvp.Key.StartsWith(namePrefix + "_")); | |
} | |
static IEnumerable<IGrouping<int, KeyValuePair<string, ValueProviderResult>>> GroupByIndex(IEnumerable<KeyValuePair<string, ValueProviderResult>> formValues) | |
{ | |
return formValues.GroupBy(x => int.Parse(IdRegex.Match(x.Key).Value)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IFilteredPropertyBinder : IPropertyBinder | |
{ | |
bool IsMatch(Type modelType); | |
} | |
public interface IPropertyBinder | |
{ | |
void BindProperty(IModelBinder binder, ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class SmartBinder : DefaultModelBinder | |
{ | |
readonly List<IFilteredModelBinder> modelBinders = new List<IFilteredModelBinder>(); | |
readonly List<IFilteredPropertyBinder> propertyBinders = new List<IFilteredPropertyBinder>(); | |
public SmartBinder Add(IFilteredModelBinder binder) | |
{ | |
modelBinders.Add(binder); | |
return this; | |
} | |
public SmartBinder Add(IFilteredPropertyBinder binder) | |
{ | |
propertyBinders.Add(binder); | |
return this; | |
} | |
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) | |
{ | |
foreach (var binder in modelBinders) | |
{ | |
if (!binder.IsMatch(bindingContext.ModelType)) continue; | |
return binder.BindModel(controllerContext, bindingContext); | |
} | |
return base.BindModel(controllerContext, bindingContext); | |
} | |
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) | |
{ | |
foreach (var binder in propertyBinders) | |
{ | |
if (!binder.IsMatch(propertyDescriptor.PropertyType)) continue; | |
binder.BindProperty(this, controllerContext, bindingContext, propertyDescriptor); | |
} | |
base.BindProperty(controllerContext, bindingContext, propertyDescriptor); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ModelBinders.Binders.DefaultBinder = new SmartBinder() | |
.Add(new ArrayModelBinder()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Very rudimentary implementation of a model binder which will try to populate an array from the posted form values. For this to work your form fields must have the following naming convention:
camelCaseArrayPropertyName_arrayIndex_PascalCaseModelPropertyName
. For example:people_1_Name
.SmartModelBinder
(inspired by Jimmy Bogard's A better Model Binder) allows applying "property" model binders that match a specific criteria.This was written for an MVC1 system, there may be better alternatives for MVC2+