You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,19 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System;
|
||||
|
||||
public sealed class ArrayModelBinderProvider : ModelBinderProvider {
|
||||
|
||||
public override IModelBinder GetBinder(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) {
|
||||
ModelBinderUtil.ValidateBindingContext(bindingContext);
|
||||
|
||||
if (!bindingContext.ModelMetadata.IsReadOnly && bindingContext.ModelType.IsArray &&
|
||||
bindingContext.UnvalidatedValueProvider.ContainsPrefix(bindingContext.ModelName)) {
|
||||
Type elementType = bindingContext.ModelType.GetElementType();
|
||||
return (IModelBinder)Activator.CreateInstance(typeof(ArrayModelBinder<>).MakeGenericType(elementType));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public class ArrayModelBinder<TElement> : CollectionModelBinder<TElement> {
|
||||
|
||||
protected override bool CreateOrReplaceCollection(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext, IList<TElement> newCollection) {
|
||||
bindingContext.Model = newCollection.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
// This class provides a good implementation of ModelMetadataProvider for people who will be
|
||||
// using traditional classes with properties. It uses the buddy class support from
|
||||
// DataAnnotations, and consolidates the three operations down to a single override
|
||||
// for reading the attribute values and creating the metadata class.
|
||||
public abstract class AssociatedMetadataProvider : ModelMetadataProvider {
|
||||
private static void ApplyMetadataAwareAttributes(IEnumerable<Attribute> attributes, ModelMetadata result) {
|
||||
foreach (IMetadataAware awareAttribute in attributes.OfType<IMetadataAware>()) {
|
||||
awareAttribute.OnMetadataCreated(result);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName);
|
||||
|
||||
protected virtual IEnumerable<Attribute> FilterAttributes(Type containerType, PropertyDescriptor propertyDescriptor, IEnumerable<Attribute> attributes) {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType) {
|
||||
if (containerType == null) {
|
||||
throw new ArgumentNullException("containerType");
|
||||
}
|
||||
|
||||
return GetMetadataForPropertiesImpl(container, containerType);
|
||||
}
|
||||
|
||||
private IEnumerable<ModelMetadata> GetMetadataForPropertiesImpl(object container, Type containerType) {
|
||||
foreach (PropertyDescriptor property in GetTypeDescriptor(containerType).GetProperties()) {
|
||||
Func<object> modelAccessor = container == null ? null : GetPropertyValueAccessor(container, property);
|
||||
yield return GetMetadataForProperty(modelAccessor, containerType, property);
|
||||
}
|
||||
}
|
||||
|
||||
public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName) {
|
||||
if (containerType == null) {
|
||||
throw new ArgumentNullException("containerType");
|
||||
}
|
||||
if (String.IsNullOrEmpty(propertyName)) {
|
||||
throw new ArgumentException(SR.GetString(SR.Common_NullOrEmpty), "propertyName");
|
||||
}
|
||||
|
||||
ICustomTypeDescriptor typeDescriptor = GetTypeDescriptor(containerType);
|
||||
PropertyDescriptor property = typeDescriptor.GetProperties().Find(propertyName, true);
|
||||
if (property == null) {
|
||||
throw new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.GetString(SR.Common_PropertyNotFound),
|
||||
containerType.FullName, propertyName));
|
||||
}
|
||||
|
||||
return GetMetadataForProperty(modelAccessor, containerType, property);
|
||||
}
|
||||
|
||||
protected virtual ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, PropertyDescriptor propertyDescriptor) {
|
||||
IEnumerable<Attribute> attributes = FilterAttributes(containerType, propertyDescriptor, propertyDescriptor.Attributes.Cast<Attribute>());
|
||||
ModelMetadata result = CreateMetadata(attributes, containerType, modelAccessor, propertyDescriptor.PropertyType, propertyDescriptor.Name);
|
||||
ApplyMetadataAwareAttributes(attributes, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType) {
|
||||
if (modelType == null) {
|
||||
throw new ArgumentNullException("modelType");
|
||||
}
|
||||
|
||||
IEnumerable<Attribute> attributes = GetTypeDescriptor(modelType).GetAttributes().Cast<Attribute>();
|
||||
ModelMetadata result = CreateMetadata(attributes, null /* containerType */, modelAccessor, modelType, null /* propertyName */);
|
||||
ApplyMetadataAwareAttributes(attributes, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Func<object> GetPropertyValueAccessor(object container, PropertyDescriptor property) {
|
||||
return () => property.GetValue(container);
|
||||
}
|
||||
|
||||
protected virtual ICustomTypeDescriptor GetTypeDescriptor(Type type) {
|
||||
return TypeDescriptorHelper.Get(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
public abstract class AssociatedValidatorProvider : ModelValidatorProvider {
|
||||
protected virtual ICustomTypeDescriptor GetTypeDescriptor(Type type) {
|
||||
return TypeDescriptorHelper.Get(type);
|
||||
}
|
||||
|
||||
public override sealed IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ModelBindingExecutionContext context) {
|
||||
if (metadata == null) {
|
||||
throw new ArgumentNullException("metadata");
|
||||
}
|
||||
if (context == null) {
|
||||
throw new ArgumentNullException("context");
|
||||
}
|
||||
|
||||
if (metadata.ContainerType != null && !String.IsNullOrEmpty(metadata.PropertyName)) {
|
||||
return GetValidatorsForProperty(metadata, context);
|
||||
}
|
||||
|
||||
return GetValidatorsForType(metadata, context);
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ModelBindingExecutionContext context, IEnumerable<Attribute> attributes);
|
||||
|
||||
private IEnumerable<ModelValidator> GetValidatorsForProperty(ModelMetadata metadata, ModelBindingExecutionContext context) {
|
||||
ICustomTypeDescriptor typeDescriptor = GetTypeDescriptor(metadata.ContainerType);
|
||||
PropertyDescriptor property = typeDescriptor.GetProperties().Find(metadata.PropertyName, true);
|
||||
if (property == null) {
|
||||
throw new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.GetString(SR.Common_PropertyNotFound),
|
||||
metadata.ContainerType.FullName, metadata.PropertyName),
|
||||
"metadata");
|
||||
}
|
||||
|
||||
return GetValidators(metadata, context, property.Attributes.OfType<Attribute>());
|
||||
}
|
||||
|
||||
private IEnumerable<ModelValidator> GetValidatorsForType(ModelMetadata metadata, ModelBindingExecutionContext context) {
|
||||
return GetValidators(metadata, context, GetTypeDescriptor(metadata.ModelType).GetAttributes().Cast<Attribute>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
// This is a single provider that can work with both byte[] and Binary models.
|
||||
public sealed class BinaryDataModelBinderProvider : ModelBinderProvider {
|
||||
|
||||
private static readonly ModelBinderProvider[] _providers = new ModelBinderProvider[] {
|
||||
new SimpleModelBinderProvider(typeof(byte[]), new ByteArrayExtensibleModelBinder()),
|
||||
#if UNDEF
|
||||
new SimpleModelBinderProvider(typeof(Binary), new LinqBinaryExtensibleModelBinder())
|
||||
#endif
|
||||
};
|
||||
|
||||
public override IModelBinder GetBinder(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) {
|
||||
return (from provider in _providers
|
||||
let binder = provider.GetBinder(modelBindingExecutionContext, bindingContext)
|
||||
where binder != null
|
||||
select binder).FirstOrDefault();
|
||||
}
|
||||
|
||||
// This is essentially a clone of the ByteArrayModelBinder from core
|
||||
private class ByteArrayExtensibleModelBinder : IModelBinder {
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to ignore when the data is corrupted")]
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.Web.ModelBinding.ValueProviderResult.ConvertTo(System.Type)", Justification = @"The default CultureInfo used by ValueProvider is fine.")]
|
||||
public bool BindModel(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) {
|
||||
ModelBinderUtil.ValidateBindingContext(bindingContext);
|
||||
ValueProviderResult vpResult = bindingContext.UnvalidatedValueProvider.GetValue(bindingContext.ModelName);
|
||||
|
||||
// case 1: there was no <input ... /> element containing this data
|
||||
if (vpResult == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string base64string = (string)vpResult.ConvertTo(typeof(string));
|
||||
|
||||
// case 2: there was an <input ... /> element but it was left blank
|
||||
if (String.IsNullOrEmpty(base64string)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Future proofing. If the byte array is actually an instance of System.Data.Linq.Binary
|
||||
// then we need to remove these quotes put in place by the ToString() method.
|
||||
string realValue = base64string.Replace("\"", String.Empty);
|
||||
try {
|
||||
bindingContext.Model = ConvertByteArray(Convert.FromBase64String(realValue));
|
||||
return true;
|
||||
}
|
||||
catch {
|
||||
// corrupt data - just ignore
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual object ConvertByteArray(byte[] originalModel) {
|
||||
return originalModel;
|
||||
}
|
||||
}
|
||||
#if UNDEF
|
||||
// This is essentially a clone of the LinqBinaryModelBinder from core
|
||||
private class LinqBinaryExtensibleModelBinder : ByteArrayExtensibleModelBinder {
|
||||
protected override object ConvertByteArray(byte[] originalModel) {
|
||||
return new Binary(originalModel);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class BindNeverAttribute : BindingBehaviorAttribute {
|
||||
|
||||
public BindNeverAttribute()
|
||||
: base(BindingBehavior.Never) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class BindRequiredAttribute : BindingBehaviorAttribute {
|
||||
|
||||
public BindRequiredAttribute()
|
||||
: base(BindingBehavior.Required) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
|
||||
public enum BindingBehavior {
|
||||
Optional = 0,
|
||||
Never,
|
||||
Required
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "This class is designed to be overridden")]
|
||||
public class BindingBehaviorAttribute : Attribute {
|
||||
|
||||
private static readonly object _typeId = new object();
|
||||
|
||||
public BindingBehaviorAttribute(BindingBehavior behavior) {
|
||||
Behavior = behavior;
|
||||
}
|
||||
|
||||
public BindingBehavior Behavior {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public override object TypeId {
|
||||
get {
|
||||
return _typeId;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System.Collections.Generic;
|
||||
|
||||
public sealed class CollectionModelBinderProvider : ModelBinderProvider {
|
||||
|
||||
public override IModelBinder GetBinder(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) {
|
||||
ModelBinderUtil.ValidateBindingContext(bindingContext);
|
||||
|
||||
if (bindingContext.UnvalidatedValueProvider.ContainsPrefix(bindingContext.ModelName)) {
|
||||
return CollectionModelBinderUtil.GetGenericBinder(typeof(ICollection<>), typeof(List<>), typeof(CollectionModelBinder<>), bindingContext.ModelMetadata);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
|
||||
internal static class CollectionModelBinderUtil {
|
||||
|
||||
public static void CreateOrReplaceCollection<TElement>(ModelBindingContext bindingContext, IEnumerable<TElement> incomingElements, Func<ICollection<TElement>> creator) {
|
||||
ICollection<TElement> collection = bindingContext.Model as ICollection<TElement>;
|
||||
if (collection == null || collection.IsReadOnly) {
|
||||
collection = creator();
|
||||
bindingContext.Model = collection;
|
||||
}
|
||||
|
||||
collection.Clear();
|
||||
foreach (TElement element in incomingElements) {
|
||||
collection.Add(element);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CreateOrReplaceDictionary<TKey, TValue>(ModelBindingContext bindingContext, IEnumerable<KeyValuePair<TKey, TValue>> incomingElements, Func<IDictionary<TKey, TValue>> creator) {
|
||||
IDictionary<TKey, TValue> dictionary = bindingContext.Model as IDictionary<TKey, TValue>;
|
||||
if (dictionary == null || dictionary.IsReadOnly) {
|
||||
dictionary = creator();
|
||||
bindingContext.Model = dictionary;
|
||||
}
|
||||
|
||||
dictionary.Clear();
|
||||
foreach (var element in incomingElements) {
|
||||
if (element.Key != null) {
|
||||
dictionary[element.Key] = element.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// supportedInterfaceType: type that is updatable by this binder
|
||||
// newInstanceType: type that will be created by the binder if necessary
|
||||
// openBinderType: model binder type
|
||||
// modelMetadata: metadata for the model to bind
|
||||
//
|
||||
// example: GetGenericBinder(typeof(IList<>), typeof(List<>), typeof(ListBinder<>), ...) means that the ListBinder<T>
|
||||
// type can update models that implement IList<T>, and if for some reason the existing model instance is not
|
||||
// updatable the binder will create a List<T> object and bind to that instead. This method will return a ListBinder<T>
|
||||
// or null, depending on whether the type and updatability checks succeed.
|
||||
public static IModelBinder GetGenericBinder(Type supportedInterfaceType, Type newInstanceType, Type openBinderType, ModelMetadata modelMetadata) {
|
||||
Type[] typeArguments = GetTypeArgumentsForUpdatableGenericCollection(supportedInterfaceType, newInstanceType, modelMetadata);
|
||||
return (typeArguments != null) ? (IModelBinder)Activator.CreateInstance(openBinderType.MakeGenericType(typeArguments)) : null;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.Web.ModelBinding.ValueProviderResult.ConvertTo(System.Type)", Justification = @"The default CultureInfo used by ValueProvider is fine.")]
|
||||
public static IEnumerable<string> GetIndexNamesFromValueProviderResult(ValueProviderResult vpResultIndex) {
|
||||
IEnumerable<string> indexNames = null;
|
||||
if (vpResultIndex != null) {
|
||||
string[] indexes = (string[])(vpResultIndex.ConvertTo(typeof(string[])));
|
||||
if (indexes != null && indexes.Length > 0) {
|
||||
indexNames = indexes;
|
||||
}
|
||||
}
|
||||
return indexNames;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetZeroBasedIndexes() {
|
||||
for (int i = 0; ; i++) {
|
||||
yield return i.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
public class CollectionModelBinder<TElement> : IModelBinder {
|
||||
|
||||
// Used when the ValueProvider contains the collection to be bound as multiple elements, e.g. foo[0], foo[1].
|
||||
private static List<TElement> BindComplexCollection(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) {
|
||||
string indexPropertyName = ModelBinderUtil.CreatePropertyModelName(bindingContext.ModelName, "index");
|
||||
ValueProviderResult vpResultIndex = bindingContext.UnvalidatedValueProvider.GetValue(indexPropertyName);
|
||||
IEnumerable<string> indexNames = CollectionModelBinderUtil.GetIndexNamesFromValueProviderResult(vpResultIndex);
|
||||
return BindComplexCollectionFromIndexes(modelBindingExecutionContext, bindingContext, indexNames);
|
||||
}
|
||||
|
||||
internal static List<TElement> BindComplexCollectionFromIndexes(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext, IEnumerable<string> indexNames) {
|
||||
bool indexNamesIsFinite;
|
||||
if (indexNames != null) {
|
||||
indexNamesIsFinite = true;
|
||||
}
|
||||
else {
|
||||
indexNamesIsFinite = false;
|
||||
indexNames = CollectionModelBinderUtil.GetZeroBasedIndexes();
|
||||
}
|
||||
|
||||
List<TElement> boundCollection = new List<TElement>();
|
||||
foreach (string indexName in indexNames) {
|
||||
string fullChildName = ModelBinderUtil.CreateIndexModelName(bindingContext.ModelName, indexName);
|
||||
ModelBindingContext childBindingContext = new ModelBindingContext(bindingContext) {
|
||||
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TElement)),
|
||||
ModelName = fullChildName
|
||||
};
|
||||
|
||||
object boundValue = null;
|
||||
IModelBinder childBinder = bindingContext.ModelBinderProviders.GetBinder(modelBindingExecutionContext, childBindingContext);
|
||||
if (childBinder != null) {
|
||||
if (childBinder.BindModel(modelBindingExecutionContext, childBindingContext)) {
|
||||
boundValue = childBindingContext.Model;
|
||||
|
||||
// merge validation up
|
||||
bindingContext.ValidationNode.ChildNodes.Add(childBindingContext.ValidationNode);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// should we even bother continuing?
|
||||
if (!indexNamesIsFinite) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
boundCollection.Add(ModelBinderUtil.CastOrDefault<TElement>(boundValue));
|
||||
}
|
||||
|
||||
return boundCollection;
|
||||
}
|
||||
|
||||
public virtual bool BindModel(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) {
|
||||
ModelBinderUtil.ValidateBindingContext(bindingContext);
|
||||
|
||||
ValueProviderResult vpResult = bindingContext.UnvalidatedValueProvider.GetValue(bindingContext.ModelName, skipValidation: !bindingContext.ValidateRequest);
|
||||
List<TElement> boundCollection = (vpResult != null)
|
||||
? BindSimpleCollection(modelBindingExecutionContext, bindingContext, vpResult.RawValue, vpResult.Culture)
|
||||
: BindComplexCollection(modelBindingExecutionContext, bindingContext);
|
||||
|
||||
bool retVal = CreateOrReplaceCollection(modelBindingExecutionContext, bindingContext, boundCollection);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
// Used when the ValueProvider contains the collection to be bound as a single element, e.g. the raw value
|
||||
// is [ "1", "2" ] and needs to be converted to an int[].
|
||||
internal static List<TElement> BindSimpleCollection(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext, object rawValue, CultureInfo culture) {
|
||||
if (rawValue == null) {
|
||||
return null; // nothing to do
|
||||
}
|
||||
|
||||
List<TElement> boundCollection = new List<TElement>();
|
||||
|
||||
object[] rawValueArray = ModelBinderUtil.RawValueToObjectArray(rawValue);
|
||||
foreach (object rawValueElement in rawValueArray) {
|
||||
ModelBindingContext innerBindingContext = new ModelBindingContext(bindingContext) {
|
||||
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TElement)),
|
||||
ModelName = bindingContext.ModelName,
|
||||
ValueProvider = new ValueProviderCollection() { // aggregate value provider
|
||||
new ElementalValueProvider(bindingContext.ModelName, rawValueElement, culture), // our temporary provider goes at the front of the list
|
||||
bindingContext.ValueProvider
|
||||
}
|
||||
};
|
||||
|
||||
object boundValue = null;
|
||||
IModelBinder childBinder = bindingContext.ModelBinderProviders.GetBinder(modelBindingExecutionContext, innerBindingContext);
|
||||
if (childBinder != null) {
|
||||
if (childBinder.BindModel(modelBindingExecutionContext, innerBindingContext)) {
|
||||
boundValue = innerBindingContext.Model;
|
||||
bindingContext.ValidationNode.ChildNodes.Add(innerBindingContext.ValidationNode);
|
||||
}
|
||||
}
|
||||
boundCollection.Add(ModelBinderUtil.CastOrDefault<TElement>(boundValue));
|
||||
}
|
||||
|
||||
return boundCollection;
|
||||
}
|
||||
|
||||
// Extensibility point that allows the bound collection to be manipulated or transformed before
|
||||
// being returned from the binder.
|
||||
protected virtual bool CreateOrReplaceCollection(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext, IList<TElement> newCollection) {
|
||||
CollectionModelBinderUtil.CreateOrReplaceCollection(bindingContext, newCollection, () => new List<TElement>());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
// Describes a complex model, but uses a collection rather than individual properties as the data store.
|
||||
public class ComplexModel {
|
||||
|
||||
public ComplexModel(ModelMetadata modelMetadata, IEnumerable<ModelMetadata> propertyMetadata) {
|
||||
if (modelMetadata == null) {
|
||||
throw new ArgumentNullException("modelMetadata");
|
||||
}
|
||||
if (propertyMetadata == null) {
|
||||
throw new ArgumentNullException("propertyMetadata");
|
||||
}
|
||||
|
||||
ModelMetadata = modelMetadata;
|
||||
PropertyMetadata = new ReadOnlyCollection<ModelMetadata>(propertyMetadata.ToList());
|
||||
Results = new Dictionary<ModelMetadata, ComplexModelResult>();
|
||||
}
|
||||
|
||||
public ModelMetadata ModelMetadata {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<ModelMetadata> PropertyMetadata {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
// Contains entries corresponding to each property against which binding was
|
||||
// attempted. If binding failed, the entry's value will be null. If binding
|
||||
// was never attempted, this dictionary will not contain a corresponding
|
||||
// entry.
|
||||
public IDictionary<ModelMetadata, ComplexModelResult> Results {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
|
||||
public sealed class ComplexModelBinder : IModelBinder {
|
||||
|
||||
public bool BindModel(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) {
|
||||
ModelBinderUtil.ValidateBindingContext(bindingContext, typeof(ComplexModel), false /* allowNullModel */);
|
||||
|
||||
ComplexModel complexModel = (ComplexModel)bindingContext.Model;
|
||||
foreach (ModelMetadata propertyMetadata in complexModel.PropertyMetadata) {
|
||||
ModelBindingContext propertyBindingContext = new ModelBindingContext(bindingContext) {
|
||||
ModelMetadata = propertyMetadata,
|
||||
ModelName = ModelBinderUtil.CreatePropertyModelName(bindingContext.ModelName, propertyMetadata.PropertyName)
|
||||
};
|
||||
|
||||
// bind and propagate the values
|
||||
IModelBinder propertyBinder = bindingContext.ModelBinderProviders.GetBinder(modelBindingExecutionContext, propertyBindingContext);
|
||||
if (propertyBinder != null) {
|
||||
if (propertyBinder.BindModel(modelBindingExecutionContext, propertyBindingContext)) {
|
||||
complexModel.Results[propertyMetadata] = new ComplexModelResult(propertyBindingContext.Model, propertyBindingContext.ValidationNode);
|
||||
}
|
||||
else {
|
||||
complexModel.Results[propertyMetadata] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
|
||||
// Returns a binder that can bind ComplexModel objects.
|
||||
public sealed class ComplexModelBinderProvider : ModelBinderProvider {
|
||||
|
||||
// This is really just a simple binder.
|
||||
private static readonly SimpleModelBinderProvider _underlyingProvider = GetUnderlyingProvider();
|
||||
|
||||
public override IModelBinder GetBinder(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext) {
|
||||
return _underlyingProvider.GetBinder(modelBindingExecutionContext, bindingContext);
|
||||
}
|
||||
|
||||
private static SimpleModelBinderProvider GetUnderlyingProvider() {
|
||||
return new SimpleModelBinderProvider(typeof(ComplexModel), new ComplexModelBinder()) {
|
||||
SuppressPrefixCheck = true
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System;
|
||||
|
||||
public sealed class ComplexModelResult {
|
||||
|
||||
public ComplexModelResult(object model, ModelValidationNode validationNode) {
|
||||
if (validationNode == null) {
|
||||
throw new ArgumentNullException("validationNode");
|
||||
}
|
||||
|
||||
Model = model;
|
||||
ValidationNode = validationNode;
|
||||
}
|
||||
|
||||
public object Model {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public ModelValidationNode ValidationNode {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
namespace System.Web.ModelBinding {
|
||||
|
||||
[AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class ControlAttribute : ValueProviderSourceAttribute {
|
||||
|
||||
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID", Justification = "Legacy way of referring to ControlID.")]
|
||||
public string ControlID {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string PropertyName {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public ControlAttribute()
|
||||
: this(null, null) {
|
||||
}
|
||||
|
||||
|
||||
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID", Justification = "Legacy way of referring to ControlID.")]
|
||||
public ControlAttribute(string controlID)
|
||||
: this(controlID, null) {
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID", Justification = "Legacy way of referring to ControlID.")]
|
||||
public ControlAttribute(string controlID, string propertyName) {
|
||||
ControlID = controlID;
|
||||
PropertyName = propertyName;
|
||||
}
|
||||
|
||||
public override IValueProvider GetValueProvider(ModelBindingExecutionContext modelBindingExecutionContext) {
|
||||
if (modelBindingExecutionContext == null) {
|
||||
throw new ArgumentNullException("modelBindingExecutionContext");
|
||||
}
|
||||
|
||||
return new ControlValueProvider(modelBindingExecutionContext, PropertyName);
|
||||
}
|
||||
|
||||
public override string GetModelName() {
|
||||
return ControlID;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace System.Web.ModelBinding {
|
||||
|
||||
public sealed class ControlValueProvider : SimpleValueProvider {
|
||||
|
||||
public string PropertyName {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.Web.ModelBinding.SimpleValueProvider.#ctor(System.Web.ModelBinding.ModelBindingExecutionContext)",
|
||||
Justification = "SimpleValueProvider Constructor specifies the CultureInfo")]
|
||||
public ControlValueProvider(ModelBindingExecutionContext modelBindingExecutionContext, string propertyName)
|
||||
: base(modelBindingExecutionContext) {
|
||||
PropertyName = propertyName;
|
||||
}
|
||||
|
||||
protected override object FetchValue(string controlId) {
|
||||
if (String.IsNullOrEmpty(controlId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Control dataControl = ModelBindingExecutionContext.GetService<Control>();
|
||||
|
||||
//Following code taken from ControlParameter - code duplicated because ControlPrameter throws exceptions whereas we do not.
|
||||
string propertyName = PropertyName;
|
||||
|
||||
//Bug Fix # 280051 : First try to find it on dataControl as DataBoundControlHelper.FindControl only walks up starting from dataControl's NamingContainer.
|
||||
Control foundControl = dataControl.FindControl(controlId) ?? DataBoundControlHelper.FindControl(dataControl, controlId);
|
||||
|
||||
if (foundControl == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ControlValuePropertyAttribute controlValueProp = (ControlValuePropertyAttribute)TypeDescriptor.GetAttributes(foundControl)[typeof(ControlValuePropertyAttribute)];
|
||||
|
||||
// If no property name is specified, use the ControlValuePropertyAttribute to determine which property to use.
|
||||
if (String.IsNullOrEmpty(propertyName)) {
|
||||
if ((controlValueProp != null) && (!String.IsNullOrEmpty(controlValueProp.Name))) {
|
||||
propertyName = controlValueProp.Name;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the value of the property
|
||||
object value = DataBinder.Eval(foundControl, propertyName);
|
||||
|
||||
// Convert the value to null if this is the default property and the value is the property's default value
|
||||
if (controlValueProp != null &&
|
||||
controlValueProp.DefaultValue != null &&
|
||||
controlValueProp.DefaultValue.Equals(value)) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Web;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class CookieAttribute : ValueProviderSourceAttribute, IUnvalidatedValueProviderSource {
|
||||
|
||||
private bool _validateInput = true;
|
||||
|
||||
public string Name {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public CookieAttribute()
|
||||
: this(null) {
|
||||
}
|
||||
|
||||
public CookieAttribute(string name) {
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public override IValueProvider GetValueProvider(ModelBindingExecutionContext modelBindingExecutionContext) {
|
||||
|
||||
if (modelBindingExecutionContext == null) {
|
||||
throw new ArgumentNullException("modelBindingExecutionContext");
|
||||
}
|
||||
|
||||
return new CookieValueProvider(modelBindingExecutionContext);
|
||||
}
|
||||
|
||||
public override string GetModelName() {
|
||||
return Name;
|
||||
}
|
||||
|
||||
public bool ValidateInput {
|
||||
get {
|
||||
return _validateInput;
|
||||
}
|
||||
set {
|
||||
_validateInput = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
namespace System.Web.ModelBinding {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
public sealed class CookieValueProvider : IValueProvider, IUnvalidatedValueProvider {
|
||||
|
||||
private readonly CultureInfo _culture;
|
||||
private readonly PrefixContainer _prefixes;
|
||||
private readonly Dictionary<string, ValueProviderResultPlaceholder> _values = new Dictionary<string, ValueProviderResultPlaceholder>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly HttpCookieCollection _validatedCollection;
|
||||
private readonly HttpCookieCollection _unvalidatedCollection;
|
||||
|
||||
public CookieValueProvider(ModelBindingExecutionContext modelBindingExecutionContext)
|
||||
: this(modelBindingExecutionContext, modelBindingExecutionContext.HttpContext.Request.Unvalidated) {
|
||||
}
|
||||
|
||||
internal CookieValueProvider(ModelBindingExecutionContext modelBindingExecutionContext, UnvalidatedRequestValuesBase unvalidatedValues)
|
||||
: this(modelBindingExecutionContext.HttpContext.Request.Cookies, unvalidatedValues.Cookies, CultureInfo.CurrentCulture) {
|
||||
}
|
||||
|
||||
internal CookieValueProvider(HttpCookieCollection collection, HttpCookieCollection unvalidatedCollection, CultureInfo culture) {
|
||||
if (collection == null) {
|
||||
throw new ArgumentNullException("collection");
|
||||
}
|
||||
|
||||
_culture = culture;
|
||||
_prefixes = new PrefixContainer(collection.Keys.Cast<string>());
|
||||
_validatedCollection = collection;
|
||||
_unvalidatedCollection = unvalidatedCollection ?? collection;
|
||||
|
||||
foreach (string key in collection) {
|
||||
if (key != null) {
|
||||
_values[key] = new ValueProviderResultPlaceholder(key, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsPrefix(string prefix) {
|
||||
if (prefix == null) {
|
||||
throw new ArgumentNullException("prefix");
|
||||
}
|
||||
|
||||
return _prefixes.ContainsPrefix(prefix);
|
||||
}
|
||||
|
||||
public ValueProviderResult GetValue(string key) {
|
||||
return GetValue(key, skipValidation: false);
|
||||
}
|
||||
|
||||
public ValueProviderResult GetValue(string key, bool skipValidation) {
|
||||
if (key == null) {
|
||||
throw new ArgumentNullException("key");
|
||||
}
|
||||
|
||||
ValueProviderResultPlaceholder placeholder;
|
||||
_values.TryGetValue(key, out placeholder);
|
||||
if (placeholder == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return (skipValidation) ? placeholder.UnvalidatedResult : placeholder.ValidatedResult;
|
||||
}
|
||||
}
|
||||
|
||||
// Placeholder that can store a validated (in relation to request validation) or unvalidated
|
||||
// ValueProviderResult for a given key.
|
||||
private sealed class ValueProviderResultPlaceholder {
|
||||
private readonly Func<ValueProviderResult> _validatedResultAccessor;
|
||||
private readonly Func<ValueProviderResult> _unvalidatedResultAccessor;
|
||||
private ValueProviderResult _validatedResult;
|
||||
private ValueProviderResult _unvalidatedResult;
|
||||
|
||||
public ValueProviderResultPlaceholder(string key, CookieValueProvider valueProvider) {
|
||||
_validatedResultAccessor = () => GetResultFromCollection(key, valueProvider, useValidatedCollection: true);
|
||||
_unvalidatedResultAccessor = () => GetResultFromCollection(key, valueProvider, useValidatedCollection: false);
|
||||
}
|
||||
|
||||
private static ValueProviderResult GetResultFromCollection(string key, CookieValueProvider valueProvider, bool useValidatedCollection) {
|
||||
HttpCookieCollection collection = (useValidatedCollection) ? valueProvider._validatedCollection : valueProvider._unvalidatedCollection;
|
||||
string value = collection[key].Value;
|
||||
return new ValueProviderResult(value, value, valueProvider._culture);
|
||||
|
||||
}
|
||||
|
||||
public ValueProviderResult ValidatedResult {
|
||||
get { return LazyInitializer.EnsureInitialized(ref _validatedResult, _validatedResultAccessor); }
|
||||
}
|
||||
|
||||
public ValueProviderResult UnvalidatedResult {
|
||||
get { return LazyInitializer.EnsureInitialized(ref _unvalidatedResult, _unvalidatedResultAccessor); }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user