asp.net mvc源碼分析-Action篇 IModelBinder

來源:互聯網
上載者:User

緊接著上篇asp.net mvc源碼分析-Action篇 ParameterDescriptor 文章 在ReflectedParameterBindingInfo有這個public override IModelBinder Binder屬性,同時在ControllerActionInvoker中也有一個類似的 protected internal ModelBinderDictionary Binders   看見這兩個屬性名稱我們就應該知道ModelBinderDictionary是IModelBinder的一個集合類,public class ModelBinderDictionary : IDictionary<Type, IModelBinder>  這裡是一個字典集合。

我們首先還是看看ReflectedParameterBindingInfo的Binder屬性吧:

  public override IModelBinder Binder {
            get {
                IModelBinder binder = ModelBinders.GetBinderFromAttributes(_parameterInfo,
                    () => String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedParameterBindingInfo_MultipleConverterAttributes,
                        _parameterInfo.Name, _parameterInfo.Member));

                return binder;
            }
        }

在ModelBinders中有一個屬性public static ModelBinderDictionary Binders,這個binders內容如下  

 ModelBinderDictionary binders = new ModelBinderDictionary() {
                { typeof(HttpPostedFileBase), new HttpPostedFileBaseModelBinder() },
                { typeof(byte[]), new ByteArrayModelBinder() },
                { typeof(Binary), new LinqBinaryModelBinder() }
            };
           說明預設的情況下就提供者3個Binder,但是這個屬性是共有靜態,所以我們可以往裡面添加自己的binder類,和前面文章講的Filiter以及後面要講的ValueProvider一樣,

如在 Application_Start()方法中 ModelBinders.Binders.Add(xxx,xxxx) 很不是很方便擴充了。

ModelBinders的GetBinderFromAttributes這個方法一看我們就能猜到它的邏輯了,

   CustomModelBinderAttribute[] attrs = (CustomModelBinderAttribute[])element.GetCustomAttributes(typeof(CustomModelBinderAttribute), true /* inherit */);

擷取 當前參數的CustomModelBinderAttribute特性,如果有該特性就調用第一個特性的GetBinder()方法並返回其值,沒有特性則返回null,如果有多個特性則拋出異常,說明一個參數上是不可以有多個CustomModelBinderAttribute特性的,正樣 ReflectedParameterBindingInfo的binder屬性就設定好了。

下面 該輪到ControllerActionInvoker的Binders屬性,

 protected internal ModelBinderDictionary Binders {
            get {
                if (_binders == null) {
                    _binders = ModelBinders.Binders;
                }
                return _binders;
            }
            set {
                _binders = value;
            }
        }

可以 看到預設他返回的是ModelBinders.Binders。

接下來看看  IModelBinder binder = GetModelBinder(parameterDescriptor)這句究竟怎麼返回的binder, 

     return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);

太簡單了 ,首先看看參數是否有binder特性,如果有就返回相應的binder,否者根據參數類型擷取對應的binder。

其 方法如下:

  public IModelBinder GetBinder(Type modelType) {            return GetBinder(modelType, true /* fallbackToDefault */);        }        public virtual IModelBinder GetBinder(Type modelType, bool fallbackToDefault) {            if (modelType == null) {                throw new ArgumentNullException("modelType");            }            return GetBinder(modelType, (fallbackToDefault) ? DefaultBinder : null);        }        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;        }

 這裡需要注意binder選著的優先順序,(1)從_modelBinderProviders裡面找相應的binder

 

 private ModelBinderProviderCollection _modelBinderProviders;
        public ModelBinderDictionary()
            : this(ModelBinderProviders.BinderProviders) {
        }
        internal ModelBinderDictionary(ModelBinderProviderCollection modelBinderProviders) {
            _modelBinderProviders = modelBinderProviders;
        }

 public static class ModelBinderProviders {
        private readonly static ModelBinderProviderCollection _binderProviders = new ModelBinderProviderCollection {
        };
        public static ModelBinderProviderCollection BinderProviders {
            get {
                return _binderProviders;
            }
        }
    }

從這些代碼我們可以得知 預設情況下_modelBinderProviders裡面是沒有資料 ,那麼什麼時候這個集合有資料了,當我們在Application_Start()中調用ModelBinderProviders.BinderProviders.Add(xxx)就有資料了,

(2)從_innerDictionary中取資料,這個資料時什麼時候添加上去的了,看了ModelBinderDictionary的add放就明白了

 public void Add(Type key, IModelBinder value) {
            _innerDictionary.Add(key, value);
        }

其實 ModelBinderDictionary內部很多方法都是圍繞著_innerDictionary集合操作的。

(3)從參數資料類型上擷取binder

(4)返貨預設的DefaultBinder,該屬性預設= new DefaultModelBinder()

現在 我們可以總結一下binder的優先順序(1)參數上的CustomModelBinderAttribute特性;(2)ModelBinderProviders.BinderProviders.Add(xxx)註冊的IModelBinderProvider;(3)ModelBinders的Binders;(4)參數類型上的CustomModelBinderAttribute特性;(5)返回預設的DefaultModelBinder

IValueProvider valueProvider = controllerContext.Controller.ValueProvider; 這句的講解我們放到後面的文章中吧,

 string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;這句擷取參數名稱,

  Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor);這個就只控制該參數時候需要綁定對應的值。

private static Predicate<string> GetPropertyFilter(ParameterDescriptor parameterDescriptor) {
            ParameterBindingInfo bindingInfo = parameterDescriptor.BindingInfo;
            return propertyName => BindAttribute.IsPropertyAllowed(propertyName, bindingInfo.Include.ToArray(), bindingInfo.Exclude.ToArray());
        }

BindAttribute.IsPropertyAllowed如下:

  internal static bool IsPropertyAllowed(string propertyName, string[] includeProperties, string[] excludeProperties) {
            // We allow a property to be bound if its both in the include list AND not in the exclude list.
            // An empty include list implies all properties are allowed.
            // An empty exclude list implies no properties are disallowed.
            bool includeProperty = (includeProperties == null) || (includeProperties.Length == 0) || includeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
            bool excludeProperty = (excludeProperties != null) && excludeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
            return includeProperty && !excludeProperty;
        }

現在 終於看到了BindAttribute在申明地方用到了。

 現在 我們來做一個自定的ModelBinder類怎麼做。

public class UserInfo    {        public string Name { set; get; }        public string Age { set; get; }    }    public class UserInfoModelBinder : IModelBinder    {        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)        {            object obj = Activator.CreateInstance(bindingContext.ModelType);            foreach (PropertyInfo p in bindingContext.ModelType.GetProperties())            {               ValueProviderResult vpResult=  bindingContext.ValueProvider.GetValue(p.Name);               if (vpResult != null)               {                   object value = vpResult.ConvertTo(p.PropertyType);                   p.SetValue(obj, value, null);               }            }            return obj;        }    }    public class HomeController : Controller    {        public ActionResult Index([ModelBinder(typeof(UserInfoModelBinder))]UserInfo userInfo)        {            return Content("Name:" + userInfo.Name + " Age:" + userInfo.Age);          //  return View();        }   }

運行結果

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.