Asp.net MVC源碼分析–擷取ModelBinder的優先順序

來源:互聯網
上載者:User

在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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.