MVC Model Binder
这篇博客是借助一个自己写的工程来理解model binder的过程.
MVC通过路由系统,根据url找到对应的Action,然后再执行action,在执行action的时候,根据action的参数和数据来源比对,生成各个参数的值,这就是model binder.
IActionInvoker
MVC中这个核心处理逻辑都在ControllerActionInvoker里,用reflector看,能看能到这个类继承了IActionInvoker接口
1 public interface IActionInvoker 2 { 3 bool InvokeAction(ControllerContext controllerContext, string actionName); 4 }
所以咱们可以根据代码模拟写出自己的CustomActionInvoker
以下是我自己写的ActionInvoker类
1 public class CustomActionInvoker : IActionInvoker 2 { 3 4 public bool InvokeAction(ControllerContext controllerContext, string actionName) 5 { 6 bool flag = false; 7 try 8 { 9 //get controller type 10 Type controllerType = controllerContext.Controller.GetType(); 11 //get controller descriptor 12 ControllerDescriptor controllerDescriptor =
new ReflectedControllerDescriptor(controllerType); 13 //get action descriptor 14 ActionDescriptor actionDescriptor =
controllerDescriptor.FindAction(controllerContext, actionName); 15 Dictionary<string, object> parameters =
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); 16 //get parameter-value entity 17 foreach (ParameterDescriptor parameterDescriptor in actionDescriptor.GetParameters()) 18 { 19 Type parameterType = parameterDescriptor.ParameterType; 20 //get model binder 21 IModelBinder modelBinder = new CustomModelBinder(); 22 IValueProvider valueProvider = controllerContext.Controller.ValueProvider; 23 string str = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName; 24 ModelBindingContext bindingContext = new ModelBindingContext(); 25 bindingContext.FallbackToEmptyPrefix = parameterDescriptor.BindingInfo.Prefix == null; 26 bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType); 27 bindingContext.ModelName = str; 28 bindingContext.ModelState = controllerContext.Controller.ViewData.ModelState; 29 bindingContext.ValueProvider = valueProvider; 30 parameters.Add(parameterDescriptor.ParameterName,
modelBinder.BindModel(controllerContext, bindingContext)); 31 } 32 ActionResult result = (ActionResult)actionDescriptor.Execute(controllerContext, parameters); 33 result.ExecuteResult(controllerContext); 34 flag = true; 35 } 36 catch (Exception ex) 37 { 38 //log 39 } 40 return flag; 41 } 42 }
以下详细解释下执行过程
*Descriptor
执行过程中涉及到三个Descriptor,ControllerDescriptor,ActionDescriptor,ParameterDescriptor
ControllerDescriptor主要作用是根据action name获取到ActionDescriptor,代码中使用的是MVC自带的ReflectedControllerDescriptor,从名字就可以看出来,主要是靠反射获取到action.
ActionDescriptor,主要作用是获取parameterDescriptor,然后execute action.
parameterDescriptor,描述的是action的参数信息,包括name、type等
ModelBinder
最核心的方法. 将传递的数据和参数一一对应,笔者是自己写的CustomModelBinder,MVC默认用的是DefaultModelBinder 都实现了接口IModelBinder
1 public interface IModelBinder 2 { 3 object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext); 4 }
其中CustomModelBinder的代码如下
1 public class CustomModelBinder : IModelBinder 2 { 3 4 public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 5 { 6 return this.GetModel(controllerContext, bindingContext.ModelType, bindingContext.ValueProvider, bindingContext.ModelName); 7 } 8 9 public object GetModel(ControllerContext controllerContext, Type modelType, IValueProvider valueProvider, string key) 10 { 11 if (!valueProvider.ContainsPrefix(key)) 12 { 13 return null; 14 } 15 return valueProvider.GetValue(key).ConvertTo(modelType); 16 } 17 }
注:我只是实现了简单的基本类型
中间有最核心的方法
valueProvider.GetValue(key).ConvertTo(modelType)
ValueProvider
MVC默认提供了几种ValueProvider,每种都有对应的ValueProviderFactory,每种ValueProvider都对应着自己的数据源
1 ValueProviderFactoryCollection factorys = new ValueProviderFactoryCollection(); 2 factorys.Add(new ChildActionValueProviderFactory()); 3 factorys.Add(new FormValueProviderFactory()); 4 factorys.Add(new JsonValueProviderFactory()); 5 factorys.Add(new RouteDataValueProviderFactory()); 6 factorys.Add(new QueryStringValueProviderFactory()); 7 factorys.Add(new HttpFileCollectionValueProviderFactory());
注册ActionInvoker
上述过程讲完之后,还缺一个怎么应用上自己写的ActionInvoker,在Controller里提供了虚方法CreateActionInvoker
1 protected override IActionInvoker CreateActionInvoker() 2 { 3 return new CustomActionInvoker(); 4 }
到此,整个过程已讲完。