Monday, February 6, 2012

ASP.NET MVC: Binding Arrays

The modelbinder in ASP.NET MVC is a piece of black art, converting your inputs to a nice strongly typed model. However, when binding to arrays, the DefaultModelbinder has an irritating bug. If the array is already instantiate in the model, the model binder will attempt to call .Clear() and then .Add() on the array. However arrays are fixed size lists. As a result, calling .Clear() throws a NotSupportedException as shown in the following stack trace:
System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.--System.NotSupportedException : Collection is read-only.at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, ref SignatureStruct sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
To resolve this, you can use the following model binder. It simply forces the model to null before invoking the default binder, effectively sidestepping the problem.
public class ArrayModelBinder : DefaultModelBinder
{
 public override object BindModel(
            ControllerContext controllerContext, 
            ModelBindingContext bindingContext)
 {
  var originalMetadata = bindingContext.ModelMetadata;
 
  bindingContext.ModelMetadata = new ModelMetadata(
   ModelMetadataProviders.Current,
   originalMetadata.ContainerType,
   () => null,  //Forces model to null
   originalMetadata.ModelType,
   originalMetadata.PropertyName
   );
 
  return base.BindModel(controllerContext, bindingContext);
 }
}
UPDATE:I noticed that the problem is fixed in .NET 4.5 with the introduction of the ArrayModelBinder.

4 comments:

Sergi Gisbert said...

Great tip. May you please show how to register this Binder in the globas.asax?

Thanks a lot,
Sergi

Bart Wullems said...

I used the [ModelBinder] attribute. But I guess you can use the following code: ModelBinders.Binders.Add(typeof (string[]), new ArrayModelBinder());

Mias Van Den Berg said...

OMG Sergi thank you for taking the time to share this. You saved my life! And I couldn't find any other work arounds... pressed for time right now

Mias Van Den Berg said...

LOL I mean thanks Bart! I see you wrote it. My I just caught the picture profile. Thanks.