zoukankan      html  css  js  c++  java
  • asp.net mvc源码分析Action篇 ParameterDescriptor

    紧接着上篇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这几个东西,所以我们将放到后面来统一讲解。

  • 相关阅读:
    C# 16 进制字符串转 int
    C# 16 进制字符串转 int
    dotnet 设计规范 · 抽象定义
    dotnet 设计规范 · 抽象定义
    C# 从零开始写 SharpDx 应用 控制台创建 Sharpdx 窗口
    C# 从零开始写 SharpDx 应用 控制台创建 Sharpdx 窗口
    C# 判断两条直线距离
    C# 判断两条直线距离
    PHP file() 函数
    PHP fgetss() 函数
  • 原文地址:https://www.cnblogs.com/majiang/p/2763422.html
Copyright © 2011-2022 走看看