您的位置:首页 > 编程语言 > ASP

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

2012-11-09 22:51 726 查看
紧接着上篇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这几个东西,所以我们将放到后面来统一讲解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: