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); }
// Returns the generic type arguments for the model type if updatable, else null. // supportedInterfaceType: open type (like IList<>) of supported interface, must implement ICollection<> // newInstanceType: open type (like List<>) of object that will be created, must implement supportedInterfaceType public static Type[] GetTypeArgumentsForUpdatableGenericCollection(Type supportedInterfaceType, Type newInstanceType, ModelMetadata modelMetadata) { /* * Check that we can extract proper type arguments from the model. */ if (!modelMetadata.ModelType.IsGenericType || modelMetadata.ModelType.IsGenericTypeDefinition) { // not a closed generic type return(null); } Type[] modelTypeArguments = modelMetadata.ModelType.GetGenericArguments(); if (modelTypeArguments.Length != supportedInterfaceType.GetGenericArguments().Length) { // wrong number of generic type arguments return(null); } /* * Is it possible just to change the reference rather than update the collection in-place? */ if (!modelMetadata.IsReadOnly) { Type closedNewInstanceType = newInstanceType.MakeGenericType(modelTypeArguments); if (modelMetadata.ModelType.IsAssignableFrom(closedNewInstanceType)) { return(modelTypeArguments); } } /* * At this point, we know we can't change the reference, so we need to verify that * the model instance can be updated in-place. */ Type closedSupportedInterfaceType = supportedInterfaceType.MakeGenericType(modelTypeArguments); if (!closedSupportedInterfaceType.IsInstanceOfType(modelMetadata.Model)) { return(null); // not instance of correct interface } Type closedCollectionType = TypeHelpers.ExtractGenericInterface(closedSupportedInterfaceType, typeof(ICollection <>)); bool collectionInstanceIsReadOnly = (bool)closedCollectionType.GetProperty("IsReadOnly").GetValue(modelMetadata.Model, null); if (collectionInstanceIsReadOnly) { return(null); } else { return(modelTypeArguments); } }