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

來源:互聯網
上載者:User

緊接著上篇asp.net mvc源碼分析-Action篇 Filter 中提到了  IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);首先這個方法的目的很明白擷取當前Action參數名稱和值得一個字典。

 protected virtual IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
            Dictionary<string, object> parametersDict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            ParameterDescriptor[] parameterDescriptors = actionDescriptor.GetParameters();

            foreach (ParameterDescriptor parameterDescriptor in parameterDescriptors) {
                parametersDict[parameterDescriptor.ParameterName] = GetParameterValue(controllerContext, parameterDescriptor);
            }
            return parametersDict;
        }

這個代碼邏輯很簡單吧,有前面的文章我們知道這裡的actionDescriptor 是一個ReflectedActionDescriptor執行個體,我們猜測ParameterDescriptor是Paramete的一個封裝類,具體返回應該是它的子類。 的GetParameters方法如下

  public override ParameterDescriptor[] GetParameters() {
            ParameterDescriptor[] parameters = LazilyFetchParametersCollection();

            // need to clone array so that user modifications aren't accidentally stored
            return (ParameterDescriptor[])parameters.Clone();
        }

 private ParameterDescriptor[] LazilyFetchParametersCollection() {
            return DescriptorUtil.LazilyFetchOrCreateDescriptors<ParameterInfo, ParameterDescriptor>(
                ref _parametersCache /* cacheLocation */,
                MethodInfo.GetParameters /* initializer */,
                parameterInfo => new ReflectedParameterDescriptor(parameterInfo, this) /* converter */);
        }

一看到LazilyFetchOrCreateDescriptors這個名稱我們就知道 如果有就直接擷取,沒有就建立。

 public static TDescriptor[] LazilyFetchOrCreateDescriptors<TReflection, TDescriptor>(ref TDescriptor[] cacheLocation, Func<TReflection[]> initializer, Func<TReflection, TDescriptor> converter) {
            // did we already calculate this once?
            TDescriptor[] existingCache = Interlocked.CompareExchange(ref cacheLocation, null, null);
            if (existingCache != null) {
                return existingCache;
            }

            TReflection[] memberInfos = initializer();
            TDescriptor[] descriptors = memberInfos.Select(converter).Where(descriptor => descriptor != null).ToArray();
            TDescriptor[] updatedCache = Interlocked.CompareExchange(ref cacheLocation, descriptors, null);
            return updatedCache ?? descriptors;
        }

這裡的memberInfos=MethodInfo.GetParameters()擷取Actin的所有參數。而converter=new ReflectedParameterDescriptor(parameterInfo, this),ReflectedParameterDescriptor建構函式如下:

        public ReflectedParameterDescriptor(ParameterInfo parameterInfo, ActionDescriptor actionDescriptor) {
            ParameterInfo = parameterInfo;
            _actionDescriptor = actionDescriptor;
            _bindingInfo = new ReflectedParameterBindingInfo(parameterInfo);
        }

在這個ReflectedParameterDescriptor有個屬性需要我們注意一下,那就是DefaultValue

  public override object DefaultValue {
            get {
                object value;
                if (ParameterInfoUtil.TryGetDefaultValue(ParameterInfo, out value)) {
                    return value;
                }
                else {
                    return base.DefaultValue;
                }
            }
        }

 internal static class ParameterInfoUtil {        public static bool TryGetDefaultValue(ParameterInfo parameterInfo, out object value) {            // this will get the default value as seen by the VB / C# compilers            // if no value was baked in, RawDefaultValue returns DBNull.Value            object defaultValue = parameterInfo.DefaultValue;            if (defaultValue != DBNull.Value) {                value = defaultValue;                return true;            }            // if the compiler did not bake in a default value, check the [DefaultValue] attribute            DefaultValueAttribute[] attrs = (DefaultValueAttribute[])parameterInfo.GetCustomAttributes(typeof(DefaultValueAttribute), false);            if (attrs == null || attrs.Length == 0) {                value = default(object);                return false;            }            else {                value = attrs[0].Value;                return true;            }        }    }

  這段代碼主要意思是先找到對象的parameterInfo.DefaultValue值,如果不是null這設定value=parameterInfo.DefaultValue並返回true,如果沒有找到我們就找參數是否有DefaultValueAttribute特性,如果有就返回設定value=attrs[0].Value並返回true,否則value=default(object) 並返回false。一旦返回false,ReflectedParameterDescriptor的DefaultValue就會返回null。從這段帶代碼我們需要注意在申明預設參數盡量寫成 public ActionResult Index(string name="majiang") 而不是 public ActionResult Index([DefaultValue("majiang")]string name)
現在我們再來看看建構函式中的那個ReflectedParameterBindingInfo,在參數綁定過程中並不是所有的參數都需要綁定資料的,有寫參數是不需要綁定資料。

ReflectedParameterBindingInfo的主要代碼如下:

 public ReflectedParameterBindingInfo(ParameterInfo parameterInfo) {
            _parameterInfo = parameterInfo;
            ReadSettingsFromBindAttribute();
        }
  private void ReadSettingsFromBindAttribute() {
            BindAttribute attr = (BindAttribute)Attribute.GetCustomAttribute(_parameterInfo, typeof(BindAttribute));
            if (attr == null) {
                return;
            }

            _exclude = new ReadOnlyCollection<string>(AuthorizeAttribute.SplitString(attr.Exclude));
            _include = new ReadOnlyCollection<string>(AuthorizeAttribute.SplitString(attr.Include));
            _prefix = attr.Prefix;
        }

ReadSettingsFromBindAttribute方法主要是擷取參數的BindAttribute來初始化exclude排除參數,include包含參數。這個類還有一個比較特殊的屬性
        public override IModelBinder Binder  這個將放到後面來說說。
         預設或則一般情況(簡單類型)下我們不考慮什麼排除參數的情況 擷取到的BindAttribute為null。

現在我們已經得到Action的Parameters的一個封裝對象集合ParameterDescriptor[]。同一個Action看你多次調用為了彼此不影響所以這裡需要把這個ParameterDescriptor[]集合給複製一份。 (ParameterDescriptor[])parameters.Clone();

緊接下來就是根據parameterDescriptor來擷取真正值了,調用GetParameterValue方法。

 protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) {            // collect all of the necessary binding properties            Type parameterType = parameterDescriptor.ParameterType;            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;        }

  這段代碼說白了就是通過 binder.BindModel方法來擷取值,如果沒有找到就返回parameterDescriptor.DefaultValue。有關binder.BindModel這個方法很是複雜,需要IModelBinder、IValueProvider、ModelMetadataProvider這幾個東西,所以我們將放到後面來統一講解。

聯繫我們

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