namespace System.Web.ModelBinding { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; public sealed class ModelBinderProviderCollection : Collection { public ModelBinderProviderCollection() { } public ModelBinderProviderCollection(IList list) : base(list) { } public IModelBinder GetBinder(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) { if (modelBindingExecutionContext == null) { throw new ArgumentNullException("modelBindingExecutionContext"); } if (bindingContext == null) { throw new ArgumentNullException("bindingContext"); } ModelBinderProvider providerFromAttr; if (TryGetProviderFromAttributes(bindingContext.ModelType, out providerFromAttr)) { return providerFromAttr.GetBinder(modelBindingExecutionContext, bindingContext); } return (from provider in this let binder = provider.GetBinder(modelBindingExecutionContext, bindingContext) where binder != null select binder).FirstOrDefault(); } internal IModelBinder GetRequiredBinder(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) { IModelBinder binder = GetBinder(modelBindingExecutionContext, bindingContext); if (binder == null) { throw Error.ModelBinderProviderCollection_BinderForTypeNotFound(bindingContext.ModelType); } return binder; } protected override void InsertItem(int index, ModelBinderProvider item) { if (item == null) { throw new ArgumentNullException("item"); } base.InsertItem(index, item); } private void InsertSimpleProviderAtFront(ModelBinderProvider provider) { // Don't want to insert simple providers before any that are marked as "should go first," // as that might throw off other providers like the exact type match provider. int i = 0; for (; i < Count; i++) { if (!ShouldProviderGoFirst(this[i])) { break; } } base.InsertItem(i, provider); } public void RegisterBinderForGenericType(Type modelType, IModelBinder modelBinder) { InsertSimpleProviderAtFront(new GenericModelBinderProvider(modelType, modelBinder)); } public void RegisterBinderForGenericType(Type modelType, Func modelBinderFactory) { InsertSimpleProviderAtFront(new GenericModelBinderProvider(modelType, modelBinderFactory)); } public void RegisterBinderForGenericType(Type modelType, Type modelBinderType) { InsertSimpleProviderAtFront(new GenericModelBinderProvider(modelType, modelBinderType)); } public void RegisterBinderForType(Type modelType, IModelBinder modelBinder) { RegisterBinderForType(modelType, modelBinder, false /* suppressPrefixCheck */); } internal void RegisterBinderForType(Type modelType, IModelBinder modelBinder, bool suppressPrefixCheck) { SimpleModelBinderProvider provider = new SimpleModelBinderProvider(modelType, modelBinder) { SuppressPrefixCheck = suppressPrefixCheck }; InsertSimpleProviderAtFront(provider); } public void RegisterBinderForType(Type modelType, Func modelBinderFactory) { InsertSimpleProviderAtFront(new SimpleModelBinderProvider(modelType, modelBinderFactory)); } protected override void SetItem(int index, ModelBinderProvider item) { if (item == null) { throw new ArgumentNullException("item"); } base.SetItem(index, item); } private static bool ShouldProviderGoFirst(ModelBinderProvider provider) { ModelBinderProviderOptionsAttribute options = provider.GetType() .GetCustomAttributes(typeof(ModelBinderProviderOptionsAttribute), true /* inherit */) .OfType() .FirstOrDefault(); return (options != null) ? options.FrontOfList : false; } private static bool TryGetProviderFromAttributes(Type modelType, out ModelBinderProvider provider) { ExtensibleModelBinderAttribute attr = TypeDescriptorHelper.Get(modelType).GetAttributes().OfType().FirstOrDefault(); if (attr == null) { provider = null; return false; } if (typeof(ModelBinderProvider).IsAssignableFrom(attr.BinderType)) { provider = (ModelBinderProvider)SecurityUtils.SecureCreateInstance(attr.BinderType); } else if (typeof(IModelBinder).IsAssignableFrom(attr.BinderType)) { Type closedBinderType = (attr.BinderType.IsGenericTypeDefinition) ? attr.BinderType.MakeGenericType(modelType.GetGenericArguments()) : attr.BinderType; IModelBinder binderInstance = (IModelBinder)SecurityUtils.SecureCreateInstance(closedBinderType); provider = new SimpleModelBinderProvider(modelType, binderInstance) { SuppressPrefixCheck = attr.SuppressPrefixCheck }; } else { string errorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.ModelBinderProviderCollection_InvalidBinderType), attr.BinderType, typeof(ModelBinderProvider), typeof(IModelBinder)); throw new InvalidOperationException(errorMessage); } return true; } } }