Model的綁定

  • ReflectedControllerDescriptor:ControllerDescriptor
    • Controller的 public override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName)方法內部,調用ActionMethodSelector.FindActionMethod方法查找Action.
    • public override ActionDescriptor[] GetCanonicalActions()方法會根據ActionMethodSelector的兩個方法列表,建立ActionDescription列表
  • ActionNameAttribute :ActionNameSelectorAttribute
    • 根據名稱對Action進行篩選.
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
        {

            return String.Equals(actionName, Name, StringComparison.OrdinalIgnoreCase);

        }
  • ActionMethodSelectorAttribute

ASP.NETMVC 定義了以下7 個基於相應HTTP 方法(GET 、POST 、PUT 、DELETE 、Head 、Options 和Patch) 的ActionMethodSelectorAttribute 類型.當將它們應用到某個Action 方法上時,只有在當前請求的HTTP 方法與之相匹配的狀況下目標Action 方法纔會被選擇。他們的內部也是用AcceptVerbsAttribute 實現的. html

public sealed class HttpGetAttribute : ActionMethodSelectorAttribute
        {

            private static readonly AcceptVerbsAttribute _innerAttribute = new AcceptVerbsAttribute(HttpVerbs.Get);

            public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
            {

                return _innerAttribute.IsValidForRequest(controllerContext, methodInfo);

            }

        }
  • HttpVerbs 這個枚舉應用了Flags標記,能夠HttpVerbs.Get|HttpVerbs.Post這樣使用.按位或操做
[Flags] //指示能夠將枚舉做爲位域(即一組標誌)處理。

        public enum HttpVerbs
        {

            Get = 1 << 0,//移位

            Post = 1 << 1,

            Put = 1 << 2,

            Delete = 1 << 3,

            Head = 1 << 4,

            Patch = 1 << 5,

            Options = 1 << 6,

        }
public class AcceptVerbsAttribute : ActionMethodSelectorAttribute
        {
            public ICollection<string> Verbs { get; private set; }//支持的請求類型 
            public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
            {

                if (controllerContext == null) {

                    throw new ArgumentNullException("controllerContext");

                }

                string incomingVerb = controllerContext.HttpContext.Request.GetHttpMethodOverride();

                return Verbs.Contains(incomingVerb, StringComparer.OrdinalIgnoreCase);

            }
        }//過濾的方法體
  • NonActionAttribute:ActionMethodSelectorAttribute 他的IsValidForRequest方法直接返回FALSE.
  • ActionMethodSelector FindActionMethod方法執行查找對應ActionNAme的方法.查找過程:
    • 在初始化此實例時,會生成應用了ActionNameSelectorAttribute特性的具備別名的方法列表,和沒有別名的方法列表,保存兩個列表.從Controller繼承的方法和靜態方法,非Public的方法不會被選擇
    • 調用FindActionMethod(ControllerContext controllerContext, string actionName)時,首先從具備別名的方法列表中,根據別名的特性過濾,沒有別名的方法直接根據方法名稱過濾.獲得一個總列表.
    • 從請求中找到請求類型,Get,post等,根據Action方法的ActionMethodSelectorAttribute特性進行過濾.
    • 若是結果不是一個MethodInfo,則拋出異常.
  • Action應用別名以後,原方法名不能用了.
  • Array.FindAll
  • ActionSelector[] selectors = Array.ConvertAll(attrs, attr => (ActionSelector)(controllerContext => attr.IsValidForRequest(controllerContext, methodInfo)));
  • 異步ActionDescriprot的建立過程
private ActionDescriptorCreator GetActionDescriptorDelegate(MethodInfo entryMethod)
        {

            // Does the action return a Task?

            if (entryMethod.ReturnType != null && typeof(Task).IsAssignableFrom(entryMethod.ReturnType)) {

                return (actionName, controllerDescriptor) => new TaskAsyncActionDescriptor(entryMethod, actionName, controllerDescriptor);

            }

            // Is this the FooAsync() / FooCompleted() pattern?

            if (IsAsyncSuffixedMethod(entryMethod)) {

                string completionMethodName = entryMethod.Name.Substring(0, entryMethod.Name.Length - "Async".Length) + "Completed";

                MethodInfo completionMethod = GetMethodByName(completionMethodName);

                if (completionMethod != null) {

                    return (actionName, controllerDescriptor) => new ReflectedAsyncActionDescriptor(entryMethod, completionMethod, actionName, controllerDescriptor);

                } else {

                    throw Error.AsyncActionMethodSelector_CouldNotFindMethod(completionMethodName, ControllerType);

                }

            }

            // Fallback to synchronous method

            return (actionName, controllerDescriptor) => new ReflectedActionDescriptor(entryMethod, actionName, controllerDescriptor);

        }
  • clip_image001

 

  • BindAttribute ","分割
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;

        }
  • ValueProvider

Controller 使用的ValueProvider 能夠經過定義在ContollerBase 中的ValueProvider 屬性進行獲取和設置。數組

public interface IValueProvider
        {

            bool ContainsPrefix(string prefix);

            ValueProviderResult GetValue(string key);

        }

ValueProviderResult RawValue表示提供的原始數據,AttemptedValue表示數據值的字符串表示。ConvertTo進行數據轉換異步

  • NameValueCollectionValueProvider:IUnvalidatedValueProvider,IEnumerableValueProvider,IValueProvider
  • 數據綁定時使用的前綴分隔符有. []這兩個.根據前綴查找時,只查找一級,不查找下一級別.
  • clip_image002
  • clip_image003
  • clip_image004
  • NameValueCollectionValueProvider中用的數據存儲容器是NameValueCollection,NameValueCollection一個Key對應的是一個列表.如
public virtual string Get(int index)
        {

            ArrayList list = (ArrayList)base.BaseGet(index);

            return GetAsOneString(list);

        }

        private static string GetAsOneString(ArrayList list)
        {

            int num = (list != null) ? list.Count : 0;

            if (num == 1) {

                return (string)list[0];

            }

            if (num <= 1) {

                return null;

            }

            StringBuilder builder = new StringBuilder((string)list[0]);

            for (int i = 1; i < num; i++) {

                builder.Append(',');

                builder.Append((string)list[i]);

            }

            return builder.ToString();

        }
  • FormValueprovider : NameValueCollectionValueProvider
  • QueryStringValueProvider : NameValueCollectionValueProvider
  • clip_image005
  • DictionaryValueProvider

經過NameValueCollection ValueProvider 提供的數據源將保存在一個NameValueCollection對象中, DictionaryValueProvider 天然將數據源存放在一個真正的字典對象之中。它們之間的不一樣之處在於NameValueCollection 中的元素僅限於字符串,而且不對Key 做惟一性約束(每一個Key對應的是一個存儲值的列表):字典中的Key 則是惟一的, Value 也不只僅侷限於字符串。 public DictionaryValueProvider(IDictionary<string, TValue> dictionary, CultureInfo culture)ide

  • RouteDataValueProvider: DictionaryValueProvider<object> 經過URL 路由系統解析請求地址獲得的路由數據能夠做爲Model 綁定的數據來源,
public RouteDataValueProvider(ControllerContext controllerContext)

: base(controllerContext.RouteData.Values, CultureInfo.InvariantCulture)

{

}
  • HtlpFileCollectionValueProvider: DictionaryValueProvider<HttpPostedFileBase[]>

this.Request.Files類型是HttpFileCollectionBase,元素類型是HttpPostedFileBase函數

clip_image006

當咱們根據當前ControllerContext 構建一個H即FileCollectionValueProvider 的時候,ASP.NETMVC 會從當前HTTP 請求的Files 屬性中獲取全部的HttpPostedFileBase 對象。多個HttpPostedFileBase 能夠共享相同的名稱,做爲數據源容器的字典將HttpPostedFileBase 的名稱做爲Key ,具備相同名稱的一個或者多個HttpPostedFileBase 對象構成一個數組做爲對應的Value 。post

  • ChildActionValueProvider:DictionaryValueProvider<object>

子Action 和普通Action 的不一樣之處在於它不能用於響應來自客戶端的請求,只是在某個View 中被調用以生成某個部分的HTML.HtmIHelper.Action方法調用指定名稱的Action生成相應的Html的代碼.ui

做爲子Action 方法參數的數據來源與普通Action 方法有所不一樣, Model 綁定過程當中具體的數據提供由一個類型爲System. Web.Mvc.ChildAction ValueProvider 的對象來完成。this

public ChildActionValueProvider(ControllerContext controllerContext)

: base(controllerContext.RouteData.Values, CultureInfo.InvariantCulture)

{根據路由數據的Values生成數據容器

}

可是ChildActionValueProvider 的GetValue 方法針對給定的Key 獲取的值卻並非簡單地來源於原始的路由數據,否則ChildActionValueProvider 就和RouteDataValueProvider 沒有什麼分別了。實際上Chil dActionValueProvider 的GetValue 方法獲取的值來源於調用spa

HtmHelper 的擴展方法Action 時,經過參數routeValues 指定的RouteValueDictionary 對象。code

當咱們經過HtmIHelper 的擴展方法Action 調用某個指定的子Action 時,若是參數routeValues 指定的RouteValueDictionary 不爲空, HtmIHelper 會據此建立一個DictionaryValueProvider<Object>對象,並將這個對象添加到經過routeValues 參數表示的原始的RouteValueDictionary 對象中,對應的Key 就是Chil dActionValueProvider 的靜態屬性_ childActionValuesKey 所表示的GUID 。

這個RouteValueDictionary 被進一步封裝成表示請求上下文的RequestContext 對象,隨後被調子Action 所在的Controller 會在該請求上下文中被激活,在Controller 激活過程當中表示ControllerContext 的ControllerContext 被建立出來,毫無疑問它包含了以前建立的

Route ValueDictionary 對象。當咱們針對當前ControllerContext 建立ChildActionValueProvider的時候,做爲數據源的RouteValueDictionary 就是這麼一個對象。

clip_image007

當調用ChildActionValueProvider 的GetValue 方法獲取指定Key 的值時,實際上它並不會直接根據指定的Key 去獲取對應的值,而是根據經過其靜態字段_childAction ValuesKey 值去獲取對應的DictionaryValueProvider<objec t>對象,而後再調用該對象的GetValue 根據指定的Key 去得到相應的值。代碼以下

public override ValueProviderResult GetValue(string key)
        {

            if (key == null) {

                throw new ArgumentNullException("key");

            }

            ValueProviderResult explicitValues = base.GetValue(ChildActionValuesKey);

            if (explicitValues != null) {

                DictionaryValueProvider<object> rawExplicitValues = explicitValues.RawValue as DictionaryValueProvider<object>;

                if (rawExplicitValues != null) {

                    return rawExplicitValues.GetValue(key);

                }

            }

            return null;

        }

在調用HtmlHelper.Action時,代碼以下

internal static void ActionHelper(HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues, TextWriter textWriter)
        {

            if (htmlHelper == null) {

                throw new ArgumentNullException("htmlHelper");

            }

            if (String.IsNullOrEmpty(actionName)) {

                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");

            }

            RouteValueDictionary additionalRouteValues = routeValues;

            routeValues = MergeDictionaries(routeValues, htmlHelper.ViewContext.RouteData.Values);

            routeValues["action"] = actionName;

            if (!String.IsNullOrEmpty(controllerName)) {

                routeValues["controller"] = controllerName;

            }

            bool usingAreas;

            VirtualPathData vpd = htmlHelper.RouteCollection.GetVirtualPathForArea(htmlHelper.ViewContext.RequestContext, null /* name */, routeValues, out usingAreas);

            if (vpd == null) {

                throw new InvalidOperationException(MvcResources.Common_NoRouteMatched);

            }

            if (usingAreas) {

                routeValues.Remove("area");

                if (additionalRouteValues != null) {

                    additionalRouteValues.Remove("area");

                }

            }

            if (additionalRouteValues != null) {

                routeValues[ChildActionValueProvider.ChildActionValuesKey] = new DictionaryValueProvider<object>(additionalRouteValues, CultureInfo.InvariantCulture);

            }

            RouteData routeData = CreateRouteData(vpd.Route, routeValues, vpd.DataTokens, htmlHelper.ViewContext);

            HttpContextBase httpContext = htmlHelper.ViewContext.HttpContext;

            RequestContext requestContext = new RequestContext(httpContext, routeData);

            ChildActionMvcHandler handler = new ChildActionMvcHandler(requestContext);//經過這個Handle(繼承MVCHandle)去處理請求

            httpContext.Server.Execute(HttpHandlerUtil.WrapForServerExecute(handler), textWriter, true /* preserveForm */);

        }
  • ValueProviderCollection
//System. Web.Mvc. ValueProviderCollection 表示一個元素類型爲IValueProvider 的集合,除此以外,它自己也是一個ValueProvider

        public virtual ValueProviderResult GetValue(string key, bool skipValidation)//循環遍歷其中的Provider查找,返回第一個.
        {

            return (from provider in this

                    let result = GetValueFromProvider(provider, key, skipValidation)

                    where result != null

                    select result).FirstOrDefault();

        }
  • ValueProviderFactory每個Provider都有一個對應的Factory.
    • 如FormValueProviderFactory
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
        {

            if (controllerContext == null) {

                throw new ArgumentNullException("controllerContext");

            }

            return new FormValueProvider(controllerContext, _unvalidatedValuesAccessor(controllerContext));

        }
  • ValueProviderFactories包含全部的Factory.
public static class ValueProviderFactories
        {

            private static readonly ValueProviderFactoryCollection _factories = new ValueProviderFactoryCollection()

                        {

                        new ChildActionValueProviderFactory(),

                        new FormValueProviderFactory(),

                        new JsonValueProviderFactory(),

                        new RouteDataValueProviderFactory(),

                        new QueryStringValueProviderFactory(),

                        new HttpFileCollectionValueProviderFactory(),

                        };

            public static ValueProviderFactoryCollection Factories
            {
                get { return _factories; }

            }
        }

ValueProviderFactoryCollection的方法:返回全部的Provider.他們的順序決定了使用的優先級.

public IValueProvider GetValueProvider(ControllerContext controllerContext)
        {

            var valueProviders = from factory in _serviceResolver.Current

                                 let valueProvider = factory.GetValueProvider(controllerContext)

                                 where valueProvider != null

                                 select valueProvider;

            return new ValueProviderCollection(valueProviders.ToList());

        }

clip_image008

  • BindAttribute應用在參數上,包含Include和Exclude屬性,經過逗號分割.只有當指定的屬性名在Include列表(或者爲空)中而且不在Exclude列表中,IsPropertyAlowed方法才返回TRUE,此方法用於判斷指定的屬性是否容許綁定,
  • ReflectedParameterDescriptor
    • public ParameterInfo ParameterInfo { get; private set; }綁定用的類
    • _bindingInfo = new ReflectedParameterBindingInfo(parameterInfo);在ReflectedParameterDescriptor的構造函數中初始化
  • ReflectedParameterBindingInfo
    • 有一個方法獲取ModelBinder,經過讀取應用在參數上的ModelBinderAttribute
public override IModelBinder Binder
        {

            get
            {

                IModelBinder binder = ModelBinders.GetBinderFromAttributes(_parameterInfo,

                () => String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedParameterBindingInfo_MultipleConverterAttributes,

                _parameterInfo.Name, _parameterInfo.Member));

                return binder;

            }

        }
  • public override ICollection<string> Exclude
  • public override ICollection<string> Include
  • public override string Prefix
  • 這三個屬性經過讀取應用在參數上的BindAttribute
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;

        }
  • CustomModelBinderAttribute 抽象類, 只有一個方法 public abstract IModelBinder GetBinder();
  • public sealed class ModelBinderAttribute : CustomModelBinderAttribute是CustomModelBinderAttribute的惟一繼承者 public 構造函數ModelBinderAttribute(Type binderType)指定Binder的類型.只有將此特性應用在方法的參數上,才能生效.應用在屬性或者類上是無效的.
  • 若是沒有給參數指定IModelBinder的類型(沒有給參數添加ModelBinderAttribute),系統會經過ModelBinders中的默認的binder
public static class ModelBinders
        {

            private static readonly ModelBinderDictionary _binders = CreateDefaultBinderDictionary();

            public static ModelBinderDictionary Binders
            {

                get { return _binders; }

            }

            private static ModelBinderDictionary CreateDefaultBinderDictionary()
            {

                // We can't add a binder to the HttpPostedFileBase type as an attribute, so we'll just

                // prepopulate the dictionary as a convenience to users.

                ModelBinderDictionary binders = new ModelBinderDictionary()

                {

                //值會放入innerDictionary

                { typeof(HttpPostedFileBase), new HttpPostedFileBaseModelBinder() },

                { typeof(byte[]), new ByteArrayModelBinder() },

                { typeof(Binary), new LinqBinaryModelBinder() },

                { typeof(CancellationToken), new CancellationTokenModelBinder() }

                };

                return binders;

            }

        }

        public class ModelBinderDictionary : IDictionary<Type, IModelBinder>
        {

            private readonly Dictionary<Type, IModelBinder> _innerDictionary = new Dictionary<Type, IModelBinder>();

            private IModelBinder _defaultBinder;

            private ModelBinderProviderCollection _modelBinderProviders;

            public ModelBinderDictionary()

                : this(ModelBinderProviders.BinderProviders)
            {

            }

        }

//查找binder的過程

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

            //1.

            IModelBinder binder = _modelBinderProviders.GetBinder(modelType);

            if (binder != null) {

                return binder;

            }

            //2.

            if (_innerDictionary.TryGetValue(modelType, out binder)) {

                return binder;

            }

            //3.讀取應用在參數類型上的CustomModelBinderAttribute

            binder = ModelBinders.GetBinderFromAttributes(modelType,

            () => String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderDictionary_MultipleAttributes, modelType.FullName));

            return binder ?? fallbackBinder;

        }

根據類型查找對應的默認的ModelBinder.最高優先級是,在參數上定義的ModelBindAttribute,其次是上邊的三個.書中有錯誤

能夠經過註冊對應數據類型的IModelBinder,代碼ModelBinders.Binders.Add(typeof(Baz) , new BazModelBinder());

也能夠添加Provider 代碼ModelBinderProviders.BinderProviders.Add(new MyModelBinderProvider())i

  • ModelBinderProviders

public static ModelBinderProviderCollection BinderProviders

{

get { return _binderProviders; }

}

  • ModelBinderProviderCollection
    • public class ModelBinderProviderCollection : Collection<IModelBinderProvider>
  • clip_image009
  • ModelState 與Model 綁定
  • Model 綁定除了利用ModelBinder 爲目標Action 方法的執行提供參數以外,還會將相關的數據以ModelState 的形式存儲於當前Controller 的ViewData 中.Controller 的基類ControllerBase 中具備一個System. Web.Mvc. ViewDataDictionary 類型的屬'性ViewData。顧名思義, ViewData 就是Controller 在進行View 呈現過程當中傳遞的數據。
  • 字典類型的ViewDataDictionary 具備一個類型爲System.Web.Mvc.ModelStateDictionary的屬性ModelState,這是一個Key 和Value 類型分別爲String 和System.Web.Mvc.ModelState的字典。在這裏有一點須要引發讀者注意: ViewDataDictionary 的ModelState 屬性類型不是ModelState,而是ModelStateDictionary 。
public class ModelState
        {

            private ModelErrorCollection _errors = new ModelErrorCollection();

            public ModelErrorCollection Errors
            {

                get
                {

                    return this._errors;

                }

            }

            public ValueProviderResult Value { get; set; }

        }
  • ModelBindingContext 的建立
  • 建立ModelBindingContext 對象,須要對ModelName,ModelState,ValueProvider,ModelMetadata和FallbackToEmptyPrefix屬性進行初始化.
  • ControllerActionInvoker中的方法:
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;

        }
  • FallbackToEmptyPrefix

若是沒有利用BindAttribute 特性爲參數設置一個前綴,默認狀況下會將參數名稱做爲前綴。經過前面的介紹咱們知道,這個前綴會被ValueProvider 用於數據的匹配。若是ValueProvider 經過此前綴找不到匹配的數據,將剔除前綴再次進行數據獲取。針對以下定義的Action 方法AddContacts ,在請求數據並不包含基於參數名("foo" 和"bar")前綴的狀況下,兩個參數最終將被綁定上相同的值。

public class ContactController

public void AddContacts(Contact foo , Contact bar)

反之,若是咱們應用BindAttribute 特性顯式地設置了一個前綴,這種去除前綴再次實施Model 綁定的後備機制將不會被採用,是否採用後備Model 綁定策略經過ModelBindingContext 的FallbackToEmptyPrefix 屬性來控制。

對於簡單數據類型,應該沒有這個用法.由於前綴置爲空的話,屬性名就好是空的了

  • clip_image010
  • DefaultModelBinder在根據類型建立對象時的代碼:針對泛型有特殊的處理
protected virtual object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
        {

            Type typeToCreate = modelType;

            // we can understand some collection interfaces, e.g. IList<>, IDictionary<,>

            if (modelType.IsGenericType) {

                Type genericTypeDefinition = modelType.GetGenericTypeDefinition();

                if (genericTypeDefinition == typeof(IDictionary<,>)) {

                    typeToCreate = typeof(Dictionary<,>).MakeGenericType(modelType.GetGenericArguments());

                } else if (genericTypeDefinition == typeof(IEnumerable<>) || genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IList<>)) {

                    typeToCreate = typeof(List<>).MakeGenericType(modelType.GetGenericArguments());

                }

            }

            // fallback to the type's default constructor

            return Activator.CreateInstance(typeToCreate);

        }
  • Array array = Array.CreateInstance(elementType, list.Count);

list.CopyTo(array, 0);

  • TypeDescriptor.GetProperties()返回指定組件屬性的集合
  • PropertyDescriptor提供類上的屬性的抽象化。
  • 數據綁定的過程:
    • 若是指定了BindAttribute屬性,FallbackToEmptyPrefix 會被設置爲False,即不會用去除前綴的後背策略.
    • 若是制定了BindAttribute屬性,則採用指定的Prefix做爲前綴,不然使用參數名稱最爲前綴.
    • 使用這個前綴從ValueProvider中查找是否包含前綴
    • 若是不包含前綴,且FallbackToEmptyPrefix 爲FALSE,則返回Null.
    • 若是不包含前綴,且FallbackToEmptyPrefix 爲true,則使用後背策略,使用去除前綴的方式搜索
    • 若是包含前綴,則繼續進行
    • 簡單類型不可以使用後備策略查找
  • 對於數組,在使用數字做爲索引時,不能中斷,不然中斷後的會丟棄.
  • 數組綁定數據時,會先查找和數組參數同名不包含索引的部分數據.不存在的話會添加索引再次查找.如Action(String[] foo),會先查找foo做爲Key的值,而後依次添加0.1.2.的索引foo[0.,1]進行查找.若是自定義了索引,那麼數字索引就不會使用了.定義索引用prefix.index.直接匹配參數名稱、使用自定義索引、使用數字索引三種方式是互斥的
  • IEnumerable<>類型的,同數組相似,先獲取數據列表,而後就行轉換.規則同數組同樣.支持索引和與參數同名的數據
  • 字典:
    • 字典是-個KeyValuePair<TKey , TValue>對象的集合,因此在字典元素這一級能夠採用基於索引的匹配機制。同數組和IEnumerable
    • KeyValuePair<TKey, TValue>是一個複雜類型,能夠按照屬性名稱(Key 和Value) 進行匹配。後綴分別添加Key和Value進行匹配
相關文章
相關標籤/搜索