namespace System.Web.ModelBinding { using System; // Returns a user-specified binder for a given open generic type. public sealed class GenericModelBinderProvider : ModelBinderProvider { private readonly Func _modelBinderFactory; private readonly Type _modelType; public GenericModelBinderProvider(Type modelType, IModelBinder modelBinder) { if (modelType == null) { throw new ArgumentNullException("modelType"); } if (modelBinder == null) { throw new ArgumentNullException("modelBinder"); } ValidateParameters(modelType, null /* modelBinderType */); _modelType = modelType; _modelBinderFactory = _ => modelBinder; } public GenericModelBinderProvider(Type modelType, Type modelBinderType) { // The binder can be a closed type, in which case it will be instantiated directly. If the binder // is an open type, the type arguments will be determined at runtime and the corresponding closed // type instantiated. if (modelType == null) { throw new ArgumentNullException("modelType"); } if (modelBinderType == null) { throw new ArgumentNullException("modelBinderType"); } ValidateParameters(modelType, modelBinderType); bool modelBinderTypeIsOpenGeneric = modelBinderType.IsGenericTypeDefinition; _modelType = modelType; _modelBinderFactory = typeArguments => { Type closedModelBinderType = (modelBinderTypeIsOpenGeneric) ? modelBinderType.MakeGenericType(typeArguments) : modelBinderType; return (IModelBinder)Activator.CreateInstance(closedModelBinderType); }; } public GenericModelBinderProvider(Type modelType, Func modelBinderFactory) { if (modelType == null) { throw new ArgumentNullException("modelType"); } if (modelBinderFactory == null) { throw new ArgumentNullException("modelBinderFactory"); } ValidateParameters(modelType, null /* modelBinderType */); _modelType = modelType; _modelBinderFactory = modelBinderFactory; } public Type ModelType { get { return _modelType; } } public bool SuppressPrefixCheck { get; set; } public override IModelBinder GetBinder(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); Type[] typeArguments = null; if (ModelType.IsInterface) { Type matchingClosedInterface = TypeHelpers.ExtractGenericInterface(bindingContext.ModelType, ModelType); if (matchingClosedInterface != null) { typeArguments = matchingClosedInterface.GetGenericArguments(); } } else { typeArguments = TypeHelpers.GetTypeArgumentsIfMatch(bindingContext.ModelType, ModelType); } if (typeArguments != null) { if (SuppressPrefixCheck || bindingContext.UnvalidatedValueProvider.ContainsPrefix(bindingContext.ModelName)) { return _modelBinderFactory(typeArguments); } } return null; } private static void ValidateParameters(Type modelType, Type modelBinderType) { if (!modelType.IsGenericTypeDefinition) { throw Error.GenericModelBinderProvider_ParameterMustSpecifyOpenGenericType(modelType, "modelType"); } if (modelBinderType != null) { if (!typeof(IModelBinder).IsAssignableFrom(modelBinderType)) { throw Error.Common_TypeMustImplementInterface(modelBinderType, typeof(IModelBinder), "modelBinderType"); } if (modelBinderType.IsGenericTypeDefinition) { if (modelType.GetGenericArguments().Length != modelBinderType.GetGenericArguments().Length) { throw Error.GenericModelBinderProvider_TypeArgumentCountMismatch(modelType, modelBinderType); } } } } } }