在asp.net mvc 架構中我們可以對System.Web.Mvc.Binders 進行擴充我們自訂的binder 類型,但是同時它還有一些其它的方法可以實現自訂的model binder.而且mvc在使用的時候還有一些策略,現分析如下:
擷取ModelBinder 對象的入口方法是GetParameterValue, 其中
IModelBinder binder = GetModelBinder(parameterDescriptor);
這一句代碼決定了ModelBinder 的使用原則。
System.Web.Mvc.ControllerActionInvoker
protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) { // collect all of the necessary binding properties Type parameterType = parameterDescriptor.ParameterType; //Hey 請過來擷取binder 的入口在這裡,csdn 代段沒法加高亮,垃極啊~! IModelBinder binder = GetModelBinder(parameterDescriptor); IValueProvider 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 ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType), ModelName = parameterName, ModelState = controllerContext.Controller.ViewData.ModelState, PropertyFilter = propertyFilter, ValueProvider = valueProvider }; object result = binder.BindModel(controllerContext, bindingContext); return result ?? parameterDescriptor.DefaultValue; } private IModelBinder GetModelBinder(ParameterDescriptor parameterDescriptor) { // look on the parameter itself, then look in the global table return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType); }
這裡優先從 parameterDescriptor.BindingInfo中得到IModelBinder, 接下來我們看一下怎麼從parameterDescriptor中擷取binder
首先找到 ReflectedActionDescriptor對象,可以看到在GetParameters方法中產生了ReflectedParameterDescriptor 對象,
System.Web.Mvc.ReflectedActionDescriptor
public override ParameterDescriptor[] GetParameters() { ParameterDescriptor[] parameters = LazilyFetchParametersCollection(); // need to clone array so that user modifications aren't accidentally stored return (ParameterDescriptor[])parameters.Clone(); } view plainprivate ParameterDescriptor[] LazilyFetchParametersCollection() { return DescriptorUtil.LazilyFetchOrCreateDescriptors<ParameterInfo, ParameterDescriptor>( ref _parametersCache /* cacheLocation */, MethodInfo.GetParameters /* initializer */, parameterInfo => new ReflectedParameterDescriptor(parameterInfo, this) /* converter */); }
這個對象中調用了ModelBinders.GetBinderFromAttributes方法來擷取binder
ReflectedParameterDescriptor -> ReflectedParameterBindingInfo
public override IModelBinder Binder { get { IModelBinder binder = ModelBinders.GetBinderFromAttributes(_parameterInfo, () => String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedParameterBindingInfo_MultipleConverterAttributes, _parameterInfo.Name, _parameterInfo.Member)); return binder; } }
System.Web.Mvc.ModelBinders
internal static IModelBinder GetBinderFromAttributes(ICustomAttributeProvider element, Func<string> errorMessageAccessor) { CustomModelBinderAttribute[] attrs = (CustomModelBinderAttribute[])element. GetCustomAttributes(typeof(CustomModelBinderAttribute), true /* inherit */); return GetBinderFromAttributesImpl(attrs, errorMessageAccessor); }
接下來我們看
return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);
語句後面的的分支.
System.Web.Mvc.ModelBinderDictionary
private IModelBinder GetBinder(Type modelType, IModelBinder fallbackBinder) { // Try to look up a binder for this type. We use this order of precedence: // 1. Binder returned from provider // 2. Binder registered in the global table // 3. Binder attribute defined on the type // 4. Supplied fallback binder IModelBinder binder = _modelBinderProviders.GetBinder(modelType); if (binder != null) { return binder; } if (_innerDictionary.TryGetValue(modelType, out binder)) { return binder; } binder = ModelBinders.GetBinderFromAttributes(modelType, () => String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderDictionary_MultipleAttributes, modelType.FullName)); return binder ?? fallbackBinder; } internal static IModelBinder GetBinderFromAttributes(Type type, Func<string> errorMessageAccessor) { AttributeCollection allAttrs = TypeDescriptorHelper.Get(type).GetAttributes(); CustomModelBinderAttribute[] filteredAttrs = allAttrs.OfType<CustomModelBinderAttribute>().ToArray(); return GetBinderFromAttributesImpl(filteredAttrs, errorMessageAccessor); }
到這裡我們就清楚的知道了擷取IModelBinder的優先順序。
結論:
1.如果在Action參數中的對象中標記了中繼資料的IModelBinder 實現時則調用該實現。//注:如果這句不理解可以繼續看下面的執行個體
2.如果在參數中沒有標記中繼資料的IModelBinder 實現 ,則調用的優先順序為:
// (1).從全域的ModelBinderProviders.BinderProviders 中尋找,我們也可以註冊自己的provider
// (2).從全域的System.Web.Mvc.Binders 對象中尋找,自己也可以註冊自己的binder
// (3). 從參數對象的類型中的attribute 找到 ModelBinderAtribute中的IModelBinder 實現.
// (4). 最後如果經過以上步驟都沒有找到IModelBinder的實現的話,就用MVC預設的實現DefaultModelBinder類,
public virtual IModelBinder GetBinder(Type modelType, bool fallbackToDefault) { if (modelType == null) { throw new ArgumentNullException("modelType"); } return GetBinder(modelType, (fallbackToDefault) ? DefaultBinder : null); }
範例程式碼:
下面我們通過執行個體代碼來印證以上的結論。
//Model 類:public class FormTestModel { [Required] [DataType(DataType.Text)] [Display(Name = "Room Name")] public string RoomName { get; set; } [Required] [DataType(DataType.Text)] [Display(Name = "Room Count")] public string RoomCount { get; set; } } //Action類:[HttpPost] public ActionResult FormTest(FormTestModels model, string returnUrl) { <span style="white-space:pre"> </span>return view(); } //我們建立四個IModelBinder 的實現;internal sealed class FormTestModelBinderImpl1 : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { DefaultModelBinder defBinder = new DefaultModelBinder(); return defBinder.BindModel(controllerContext, bindingContext); } } internal sealed class FormTestModelBinderImpl2 : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { DefaultModelBinder defBinder = new DefaultModelBinder(); return defBinder.BindModel(controllerContext, bindingContext); } } internal sealed class FormTestModelBinderImpl3 : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { DefaultModelBinder defBinder = new DefaultModelBinder(); return defBinder.BindModel(controllerContext, bindingContext); } } internal sealed class FormTestModelBinderImpl4 : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { DefaultModelBinder defBinder = new DefaultModelBinder(); return defBinder.BindModel(controllerContext, bindingContext); } } public sealed class FormTestModelBinderProviderImpl : IModelBinderProvider { public IModelBinder GetBinder(Type modelType) { if (modelType == typeof(FormTestModels)) { //"provider implementition"; return new FormTestModelsModelBinderImpl2(); } return null; } }
然後我們分別加入://第一優先:[HttpPost] public ActionResult FormTest([ModelBinder(typeof(FormTestModelBinderImpl1))] FormTestModel model, string returnUrl) { return view(); } //第二優先:ModelBinderProviders.BinderProviders.Add(new FormTestModelBinderProviderImpl2()); //第三優先:System.Web.Mvc.ModelBinders.Binders[typeof(FormTestModel)] = new FormTestModelBinderImpl3();
//第四優先: [ModelBinder(typeof(FormTestModelBinderImpl4))] public class FormTestModel { //codes }
最後我們在四個binder上面設定斷點,看一下代碼執行的情況,就可以清楚的看到我們的結論是多麼的無比正確.:)
最後感謝大家觀看,以上分析如有雷同純屬巧合。
謝謝大家。
後記:
其它的相關文章:
MVC運行機制之源碼剖析http://blog.csdn.net/study_live/article/details/4871745
轉載請註明出處:http://www.cnblogs.com/RobbinHan/archive/2011/12/05/2270707.html
本文作者: 十一月的雨 http://www.cnblogs.com/RobbinHan