在前一篇文章已經講敘Controller.Execute(…)方法的執行流程中會調用 ControllerActionInvoker類的 InvokeAction(ControllerContext controllerContext, string actionName)方法,在InvokeAction(…)方法內又調用了GetParameterValues(…) 方法,這個方法為Action中的每個參數賦值,追蹤到 GetParameterValues(…)方法內部 會發現其實每個參數的值是由GetParameterValue(…)返回的,觀察 GetParameterValue( …)方法的源碼:
protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) {
// collect all of the necessary binding properties
Type parameterType = parameterDescriptor.ParameterType;
IModelBinder binder = GetModelBinder (parameterDescriptor);
IDictionary<string, ValueProviderResult> valueProvider = controllerContext.Controller.ValueProvider;
string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
Predicate<string> propertyFilter = GetPropertyFilter (parameterDescriptor);
// finally, call into the binder
ModelBindingContext bindingContext = new ModelBindingContext() {
FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null), // only fall back if prefix not specified
ModelName = parameterName,
ModelState = controllerContext.Controller.ViewData.ModelState,
ModelType = parameterType,
PropertyFilter = propertyFilter,
ValueProvider = valueProvider
};
object result = binder.BindModel(controllerContext, bindingContext);
return result;
}
代碼的邏輯分為:
獲取繼承了IModelBinder接口的對象
執行IModelBinder對象的BindModel(…)方法並返回Action的參數值
繼承IModelBinder接口的對象有三個類:DefaultModelBinder, FormCollectionModelBinder和HttpPostedFileBaseModelBinder
在GetParameterValue(…)方法中可以看到由GetModelBinder(parameterDescriptor) 負責返回IModelBinder對象,但他是如何確定返回哪個ModeBinder的呢?
當Action的參數類型為FormCollection時,GetParameterValue(…)方法返回 FormCollectoinModelBinder對象
當Action的參數類型為HttpPostedFileBase時,GetParameterValue(…)方法返回 HttpPostedFileBaseModelBinder對象
當Action的參數類型除了FormCollection和HttpPostedFileBase外(int, string, boolean或強類型),GetParameterValue(…)方法返回DefaultModelBinder對象, DefaultModelBinder也是最常用的ModelBinder
來深入到GetModelBinder(…)方法的內部
private IModelBinder GetModelBinder(ParameterDescriptor parameterDescriptor) {
// look on the parameter itself, then look in the global table
return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);
}
通常情況下IModelBinder都是由 Binders.GetBinder (parameterDescriptor.ParameterType)返回, Binders屬性調用ModelBinders.Binders 返回ModelBinderDictionary的靜態實例,在調用 ModelBinders.Binders屬性返回 ModelBinderDictionary對象之前先初始化 ModelBinderDictionary對象並將 HttpPostedFileBaseModelBinder保存到 ModelBinderDictionary對象中(代碼如下),最 後其實是調用ModelBinderDictionary的GetBinder()方法返回的IModelBinder對象。
public static class ModelBinders {
private static readonly ModelBinderDictionary _binders = CreateDefaultBinderDictionary();
public static ModelBinderDictionary Binders {
get {
return _binders;
}
}
private static ModelBinderDictionary CreateDefaultBinderDictionary() {
// We can't add a binder to the HttpPostedFileBase type as an attribute, so we'll just
// prepopulate the dictionary as a convenience to users.
ModelBinderDictionary binders = new ModelBinderDictionary() {
{ typeof(HttpPostedFileBase), new HttpPostedFileBaseModelBinder() }
};
return binders;
}
}