I have found that I build a lot of the same logic into my Model Binders. I typically use Reflection to either instantiate a new object or call a method on a Factory that will do that for me. So I’ve put together a ModelBinder using the Template pattern to help accomplish this more easily.
First, the Template:
1: public abstract class ReflectionModelBinder : DefaultModelBinder
2: {
3: private IModelBinderValidator _validator = null;
4: public ReflectionModelBinder() { }
5: public ReflectionModelBinder(IModelBinderValidator validator)
6: {
7: _validator = validator;
8: }
9: protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
10: {
11: ParameterInfo[] parameters = GetParameters();
12: List<object> paramValues = new List<object>();
13: IModelBinder binder;
14: string oldModelName = bindingContext.ModelName;
15: foreach (ParameterInfo param in parameters)
16: {
17: string name = CreateSubPropertyName(oldModelName, param.Name);
18: bindingContext.ModelType = param.ParameterType;
19: bindingContext.ModelName = name;
20: if (!System.Web.Mvc.ModelBinders.Binders.TryGetValue(param.ParameterType, out binder))
21: binder = System.Web.Mvc.ModelBinders.Binders.DefaultBinder;
22: object model = binder.BindModel(controllerContext, bindingContext);
23: paramValues.Add(model);
24: }
25: bindingContext.ModelType = modelType;
26: bindingContext.ModelName = oldModelName;
27: object obj = CreateModel(paramValues.ToArray());
28:
29: if (_validator != null)
30: _validator.Validate(bindingContext, obj);
31:
32: return obj;
33: }
34: protected abstract ParameterInfo[] GetParameters();
35: protected abstract object CreateModel(object[] parameters);
36: }
(don’t mind the IModelBinderValidator, that’ll have to be another post, but you can probably figure it out)
So now I have this template. Here’s an example of how I can bind a model by using a Factory (I have a Lease object with a LeaseFactory that creates the lease and all the associated charges like back rent etc.):
1: public class LeaseReflectionModelBinder : ReflectionModelBinder
2: {
3: public LeaseReflectionModelBinder(IModelBinderValidator validator) : base(validator)
4: {
5: _createLeaseMethod = typeof(LeaseFactory).GetMethod("CreateLease", BindingFlags.Static | BindingFlags.Public);
6: }
7: private MethodInfo _createLeaseMethod;
8:
9: protected override ParameterInfo[] GetParameters()
10: {
11: return _createLeaseMethod.GetParameters();
12: }
13:
14: protected override object CreateModel(object[] parameters)
15: {
16: return _createLeaseMethod.Invoke(null, parameters);
17: }
18: }
Easy, huh?