Imported Upstream version 4.0.0~alpha1

Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
This commit is contained in:
Jo Shields
2015-04-07 09:35:12 +01:00
parent 283343f570
commit 3c1f479b9d
22469 changed files with 2931443 additions and 869343 deletions

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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>());
}
}
}

View File

@@ -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
}
}

View File

@@ -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) {
}
}
}

View File

@@ -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) {
}
}
}

View File

@@ -0,0 +1,8 @@
namespace System.Web.ModelBinding {
public enum BindingBehavior {
Optional = 0,
Never,
Required
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
};
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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