1 namespace System.Web.ModelBinding {
2     using System;
3     using System.Collections.Generic;
4     using System.Collections.ObjectModel;
5     using System.Globalization;
6     using System.Linq;
7 
8     public sealed class ModelBinderProviderCollection : Collection<ModelBinderProvider> {
9 
ModelBinderProviderCollection()10         public ModelBinderProviderCollection() {
11         }
12 
ModelBinderProviderCollection(IList<ModelBinderProvider> list)13         public ModelBinderProviderCollection(IList<ModelBinderProvider> list)
14             : base(list) {
15         }
16 
GetBinder(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext)17         public IModelBinder GetBinder(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) {
18             if (modelBindingExecutionContext == null) {
19                 throw new ArgumentNullException("modelBindingExecutionContext");
20             }
21             if (bindingContext == null) {
22                 throw new ArgumentNullException("bindingContext");
23             }
24 
25             ModelBinderProvider providerFromAttr;
26             if (TryGetProviderFromAttributes(bindingContext.ModelType, out providerFromAttr)) {
27                 return providerFromAttr.GetBinder(modelBindingExecutionContext, bindingContext);
28             }
29 
30             return (from provider in this
31                     let binder = provider.GetBinder(modelBindingExecutionContext, bindingContext)
32                     where binder != null
33                     select binder).FirstOrDefault();
34         }
35 
GetRequiredBinder(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext)36         internal IModelBinder GetRequiredBinder(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) {
37             IModelBinder binder = GetBinder(modelBindingExecutionContext, bindingContext);
38             if (binder == null) {
39                 throw Error.ModelBinderProviderCollection_BinderForTypeNotFound(bindingContext.ModelType);
40             }
41             return binder;
42         }
43 
InsertItem(int index, ModelBinderProvider item)44         protected override void InsertItem(int index, ModelBinderProvider item) {
45             if (item == null) {
46                 throw new ArgumentNullException("item");
47             }
48 
49             base.InsertItem(index, item);
50         }
51 
InsertSimpleProviderAtFront(ModelBinderProvider provider)52         private void InsertSimpleProviderAtFront(ModelBinderProvider provider) {
53             // Don't want to insert simple providers before any that are marked as "should go first,"
54             // as that might throw off other providers like the exact type match provider.
55 
56             int i = 0;
57             for (; i < Count; i++) {
58                 if (!ShouldProviderGoFirst(this[i])) {
59                     break;
60                 }
61             }
62 
63             base.InsertItem(i, provider);
64         }
65 
RegisterBinderForGenericType(Type modelType, IModelBinder modelBinder)66         public void RegisterBinderForGenericType(Type modelType, IModelBinder modelBinder) {
67             InsertSimpleProviderAtFront(new GenericModelBinderProvider(modelType, modelBinder));
68         }
69 
RegisterBinderForGenericType(Type modelType, Func<Type[], IModelBinder> modelBinderFactory)70         public void RegisterBinderForGenericType(Type modelType, Func<Type[], IModelBinder> modelBinderFactory) {
71             InsertSimpleProviderAtFront(new GenericModelBinderProvider(modelType, modelBinderFactory));
72         }
73 
RegisterBinderForGenericType(Type modelType, Type modelBinderType)74         public void RegisterBinderForGenericType(Type modelType, Type modelBinderType) {
75             InsertSimpleProviderAtFront(new GenericModelBinderProvider(modelType, modelBinderType));
76         }
77 
RegisterBinderForType(Type modelType, IModelBinder modelBinder)78         public void RegisterBinderForType(Type modelType, IModelBinder modelBinder) {
79             RegisterBinderForType(modelType, modelBinder, false /* suppressPrefixCheck */);
80         }
81 
RegisterBinderForType(Type modelType, IModelBinder modelBinder, bool suppressPrefixCheck)82         internal void RegisterBinderForType(Type modelType, IModelBinder modelBinder, bool suppressPrefixCheck) {
83             SimpleModelBinderProvider provider = new SimpleModelBinderProvider(modelType, modelBinder) {
84                 SuppressPrefixCheck = suppressPrefixCheck
85             };
86             InsertSimpleProviderAtFront(provider);
87         }
88 
RegisterBinderForType(Type modelType, Func<IModelBinder> modelBinderFactory)89         public void RegisterBinderForType(Type modelType, Func<IModelBinder> modelBinderFactory) {
90             InsertSimpleProviderAtFront(new SimpleModelBinderProvider(modelType, modelBinderFactory));
91         }
92 
SetItem(int index, ModelBinderProvider item)93         protected override void SetItem(int index, ModelBinderProvider item) {
94             if (item == null) {
95                 throw new ArgumentNullException("item");
96             }
97 
98             base.SetItem(index, item);
99         }
100 
ShouldProviderGoFirst(ModelBinderProvider provider)101         private static bool ShouldProviderGoFirst(ModelBinderProvider provider) {
102             ModelBinderProviderOptionsAttribute options = provider.GetType()
103                 .GetCustomAttributes(typeof(ModelBinderProviderOptionsAttribute), true /* inherit */)
104                 .OfType<ModelBinderProviderOptionsAttribute>()
105                 .FirstOrDefault();
106 
107             return (options != null) ? options.FrontOfList : false;
108         }
109 
TryGetProviderFromAttributes(Type modelType, out ModelBinderProvider provider)110         private static bool TryGetProviderFromAttributes(Type modelType, out ModelBinderProvider provider) {
111             ExtensibleModelBinderAttribute attr = TypeDescriptorHelper.Get(modelType).GetAttributes().OfType<ExtensibleModelBinderAttribute>().FirstOrDefault();
112             if (attr == null) {
113                 provider = null;
114                 return false;
115             }
116 
117             if (typeof(ModelBinderProvider).IsAssignableFrom(attr.BinderType)) {
118                 provider = (ModelBinderProvider)SecurityUtils.SecureCreateInstance(attr.BinderType);
119             }
120             else if (typeof(IModelBinder).IsAssignableFrom(attr.BinderType)) {
121                 Type closedBinderType = (attr.BinderType.IsGenericTypeDefinition) ? attr.BinderType.MakeGenericType(modelType.GetGenericArguments()) : attr.BinderType;
122                 IModelBinder binderInstance = (IModelBinder)SecurityUtils.SecureCreateInstance(closedBinderType);
123                 provider = new SimpleModelBinderProvider(modelType, binderInstance) { SuppressPrefixCheck = attr.SuppressPrefixCheck };
124             }
125             else {
126                 string errorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.ModelBinderProviderCollection_InvalidBinderType),
127                     attr.BinderType, typeof(ModelBinderProvider), typeof(IModelBinder));
128                 throw new InvalidOperationException(errorMessage);
129             }
130 
131             return true;
132         }
133 
134     }
135 }
136