Imported Upstream version 4.6.0.125

Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2016-08-03 10:59:49 +00:00
parent a569aebcfd
commit e79aa3c0ed
17047 changed files with 3137615 additions and 392334 deletions

View File

@ -0,0 +1,35 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System
{
using System;
using System.ComponentModel.DataAnnotations;
internal static partial class AppContextDefaultValues
{
static partial void PopulateDefaultValuesPartial(string platformIdentifier, string profile, int version)
{
// When defining a new switch you should add it to the last known version.
// For instance, if you are adding a switch in .NET 4.6 (the release after 4.5.2) you should defined your switch
// like this:
// if (version <= 40502) ...
// This ensures that all previous versions of that platform (up-to 4.5.2) will get the old behavior by default
// NOTE: When adding a default value for a switch please make sure that the default value is added to ALL of the existing platforms!
// NOTE: When adding a new if statement for the version please ensure that ALL previous switches are enabled (ie. don't use else if)
switch (platformIdentifier)
{
case ".NETCore":
case ".NETFramework":
{
if (version <= 40600)
{
LocalAppContextSwitches.SetDefaultsLessOrEqual_46();
}
break;
}
}
}
}
}

View File

@ -0,0 +1,52 @@
//------------------------------------------------------------------------------
// <copyright file="AppSettings.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
// AppSettings.cs
//
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
namespace System.ComponentModel.DataAnnotations {
internal static class AppSettings {
#if MONO
internal static readonly bool DisableRegEx = false;
#else
private static volatile bool _settingsInitialized = false;
private static object _appSettingsLock = new object();
private static void EnsureSettingsLoaded() {
if (!_settingsInitialized) {
lock (_appSettingsLock) {
if (!_settingsInitialized) {
NameValueCollection settings = null;
try {
settings = ConfigurationManager.AppSettings;
}
catch (ConfigurationErrorsException) { }
finally {
if (settings == null || !Boolean.TryParse(settings["dataAnnotations:dataTypeAttribute:disableRegEx"], out _disableRegEx))
_disableRegEx = false;
_settingsInitialized = true;
}
}
}
}
}
private static bool _disableRegEx;
internal static bool DisableRegEx {
get {
EnsureSettingsLoaded();
return _disableRegEx;
}
}
#endif
}
}

View File

@ -0,0 +1,26 @@

#if !SILVERLIGHT
namespace System.ComponentModel.DataAnnotations {
public class AssociatedMetadataTypeTypeDescriptionProvider : TypeDescriptionProvider {
private Type _associatedMetadataType;
public AssociatedMetadataTypeTypeDescriptionProvider(Type type)
: base(TypeDescriptor.GetProvider(type)) {
}
public AssociatedMetadataTypeTypeDescriptionProvider(Type type, Type associatedMetadataType)
: this(type) {
if (associatedMetadataType == null) {
throw new ArgumentNullException("associatedMetadataType");
}
_associatedMetadataType = associatedMetadataType;
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) {
ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(objectType, instance);
return new AssociatedMetadataTypeTypeDescriptor(baseDescriptor, objectType, _associatedMetadataType);
}
}
}
#endif

View File

@ -0,0 +1,162 @@

#if !SILVERLIGHT
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Resources;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Collections.Concurrent;
namespace System.ComponentModel.DataAnnotations {
internal class AssociatedMetadataTypeTypeDescriptor : CustomTypeDescriptor {
private Type AssociatedMetadataType {
get;
set;
}
private bool IsSelfAssociated {
get;
set;
}
public AssociatedMetadataTypeTypeDescriptor(ICustomTypeDescriptor parent, Type type, Type associatedMetadataType)
: base(parent) {
AssociatedMetadataType = associatedMetadataType ?? TypeDescriptorCache.GetAssociatedMetadataType(type);
IsSelfAssociated = (type == AssociatedMetadataType);
if (AssociatedMetadataType != null) {
TypeDescriptorCache.ValidateMetadataType(type, AssociatedMetadataType);
}
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) {
return GetPropertiesWithMetadata(base.GetProperties(attributes));
}
public override PropertyDescriptorCollection GetProperties() {
return GetPropertiesWithMetadata(base.GetProperties());
}
private PropertyDescriptorCollection GetPropertiesWithMetadata(PropertyDescriptorCollection originalCollection) {
if (AssociatedMetadataType == null) {
return originalCollection;
}
bool customDescriptorsCreated = false;
List<PropertyDescriptor> tempPropertyDescriptors = new List<PropertyDescriptor>();
foreach (PropertyDescriptor propDescriptor in originalCollection) {
Attribute[] newMetadata = TypeDescriptorCache.GetAssociatedMetadata(AssociatedMetadataType, propDescriptor.Name);
PropertyDescriptor descriptor = propDescriptor;
if (newMetadata.Length > 0) {
// Create a metadata descriptor that wraps the property descriptor
descriptor = new MetadataPropertyDescriptorWrapper(propDescriptor, newMetadata);
customDescriptorsCreated = true;
}
tempPropertyDescriptors.Add(descriptor);
}
if (customDescriptorsCreated) {
return new PropertyDescriptorCollection(tempPropertyDescriptors.ToArray(), true);
}
return originalCollection;
}
public override AttributeCollection GetAttributes() {
// Since normal TD behavior is to return cached attribute instances on subsequent
// calls to GetAttributes, we must be sure below to use the TD APIs to get both
// the base and associated attributes
AttributeCollection attributes = base.GetAttributes();
if (AssociatedMetadataType != null && !IsSelfAssociated) {
// Note that the use of TypeDescriptor.GetAttributes here opens up the possibility of
// infinite recursion, in the corner case of two Types referencing each other as
// metadata types (or a longer cycle), though the second condition above saves an immediate such
// case where a Type refers to itself.
Attribute[] newAttributes = TypeDescriptor.GetAttributes(AssociatedMetadataType).OfType<Attribute>().ToArray();
attributes = AttributeCollection.FromExisting(attributes, newAttributes);
}
return attributes;
}
private static class TypeDescriptorCache {
private static readonly Attribute[] emptyAttributes = new Attribute[0];
// Stores the associated metadata type for a type
private static readonly ConcurrentDictionary<Type, Type> _metadataTypeCache = new ConcurrentDictionary<Type, Type>();
// Stores the attributes for a member info
private static readonly ConcurrentDictionary<Tuple<Type, string>, Attribute[]> _typeMemberCache = new ConcurrentDictionary<Tuple<Type, string>, Attribute[]>();
// Stores whether or not a type and associated metadata type has been checked for validity
private static readonly ConcurrentDictionary<Tuple<Type, Type>, bool> _validatedMetadataTypeCache = new ConcurrentDictionary<Tuple<Type, Type>, bool>();
public static void ValidateMetadataType(Type type, Type associatedType) {
Tuple<Type, Type> typeTuple = new Tuple<Type, Type>(type, associatedType);
if (!_validatedMetadataTypeCache.ContainsKey(typeTuple)) {
CheckAssociatedMetadataType(type, associatedType);
_validatedMetadataTypeCache.TryAdd(typeTuple, true);
}
}
public static Type GetAssociatedMetadataType(Type type) {
Type associatedMetadataType = null;
if (_metadataTypeCache.TryGetValue(type, out associatedMetadataType)) {
return associatedMetadataType;
}
// Try association attribute
MetadataTypeAttribute attribute = (MetadataTypeAttribute)Attribute.GetCustomAttribute(type, typeof(MetadataTypeAttribute));
if (attribute != null) {
associatedMetadataType = attribute.MetadataClassType;
}
_metadataTypeCache.TryAdd(type, associatedMetadataType);
return associatedMetadataType;
}
private static void CheckAssociatedMetadataType(Type mainType, Type associatedMetadataType) {
// Only properties from main type
HashSet<string> mainTypeMemberNames = new HashSet<string>(mainType.GetProperties().Select(p => p.Name));
// Properties and fields from buddy type
var buddyFields = associatedMetadataType.GetFields().Select(f => f.Name);
var buddyProperties = associatedMetadataType.GetProperties().Select(p => p.Name);
HashSet<string> buddyTypeMembers = new HashSet<string>(buddyFields.Concat(buddyProperties), StringComparer.Ordinal);
// Buddy members should be a subset of the main type's members
if (!buddyTypeMembers.IsSubsetOf(mainTypeMemberNames)) {
// Reduce the buddy members to the set not contained in the main members
buddyTypeMembers.ExceptWith(mainTypeMemberNames);
throw new InvalidOperationException(String.Format(
CultureInfo.CurrentCulture,
DataAnnotationsResources.AssociatedMetadataTypeTypeDescriptor_MetadataTypeContainsUnknownProperties,
mainType.FullName,
String.Join(", ", buddyTypeMembers.ToArray())));
}
}
public static Attribute[] GetAssociatedMetadata(Type type, string memberName) {
var memberTuple = new Tuple<Type, string>(type, memberName);
Attribute[] attributes;
if (_typeMemberCache.TryGetValue(memberTuple, out attributes)) {
return attributes;
}
// Allow fields and properties
MemberTypes allowedMemberTypes = MemberTypes.Property | MemberTypes.Field;
// Only public static/instance members
BindingFlags searchFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
// Try to find a matching member on type
MemberInfo matchingMember = type.GetMember(memberName, allowedMemberTypes, searchFlags).FirstOrDefault();
if (matchingMember != null) {
attributes = Attribute.GetCustomAttributes(matchingMember, true /* inherit */);
}
else {
attributes = emptyAttributes;
}
_typeMemberCache.TryAdd(memberTuple, attributes);
return attributes;
}
}
}
}
#endif

View File

@ -0,0 +1,97 @@
using System.Collections.Generic;
namespace System.ComponentModel.DataAnnotations {
/// <summary>
/// Used to mark an Entity member as an association
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class AssociationAttribute : Attribute {
private string name;
private string thisKey;
private string otherKey;
private bool isForeignKey;
/// <summary>
/// Full form of constructor
/// </summary>
/// <param name="name">The name of the association. For bi-directional associations, the name must
/// be the same on both sides of the association</param>
/// <param name="thisKey">Comma separated list of the property names of the key values
/// on this side of the association</param>
/// <param name="otherKey">Comma separated list of the property names of the key values
/// on the other side of the association</param>
public AssociationAttribute(string name, string thisKey, string otherKey) {
this.name = name;
this.thisKey = thisKey;
this.otherKey = otherKey;
}
/// <summary>
/// Gets the name of the association. For bi-directional associations, the name must
/// be the same on both sides of the association
/// </summary>
public string Name {
get { return this.name; }
}
/// <summary>
/// Gets a comma separated list of the property names of the key values
/// on this side of the association
/// </summary>
public string ThisKey {
get {
return this.thisKey;
}
}
/// <summary>
/// Gets a comma separated list of the property names of the key values
/// on the other side of the association
/// </summary>
public string OtherKey {
get {
return this.otherKey;
}
}
/// <summary>
/// Gets or sets a value indicating whether this association member represents the foreign key
/// side of an association
/// </summary>
public bool IsForeignKey {
get {
return this.isForeignKey;
}
set {
this.isForeignKey = value;
}
}
/// <summary>
/// Gets the collection of individual key members specified in the ThisKey string.
/// </summary>
public IEnumerable<string> ThisKeyMembers {
get {
return GetKeyMembers(this.ThisKey);
}
}
/// <summary>
/// Gets the collection of individual key members specified in the OtherKey string.
/// </summary>
public IEnumerable<string> OtherKeyMembers {
get {
return GetKeyMembers(this.OtherKey);
}
}
/// <summary>
/// Parses the comma delimited key specified
/// </summary>
/// <param name="key">The key to parse</param>
/// <returns>Array of individual key members</returns>
private static string[] GetKeyMembers(string key) {
return key.Replace(" ", string.Empty).Split(',');
}
}
}

View File

@ -0,0 +1,20 @@
namespace System.ComponentModel.DataAnnotations {
/// <summary>
/// Specifies that a type is considered a bindable type for automatic fields generation by controls like GridView / DetailsView.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Struct, AllowMultiple = false, Inherited=true)]
public sealed class BindableTypeAttribute : Attribute {
public BindableTypeAttribute() {
IsBindable = true;
}
/// <summary>
/// Indicates whether the type should be considered Bindable or not. The default value is true when the attribute is specified.
/// </summary>
public bool IsBindable {
get;
set;
}
}
}

View File

@ -0,0 +1,78 @@
namespace System.ComponentModel.DataAnnotations {
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Resources;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "This attribute is designed to be a base class for other attributes.")]
public class CompareAttribute : ValidationAttribute {
public CompareAttribute(string otherProperty)
: base(DataAnnotationsResources.CompareAttribute_MustMatch) {
if (otherProperty == null) {
throw new ArgumentNullException("otherProperty");
}
OtherProperty = otherProperty;
}
public string OtherProperty { get; private set; }
public string OtherPropertyDisplayName { get; internal set; }
public override string FormatErrorMessage(string name) {
return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, OtherPropertyDisplayName ?? OtherProperty);
}
public override bool RequiresValidationContext {
get {
return true;
}
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
PropertyInfo otherPropertyInfo = validationContext.ObjectType.GetProperty(OtherProperty);
if (otherPropertyInfo == null) {
return new ValidationResult(String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.CompareAttribute_UnknownProperty, OtherProperty));
}
object otherPropertyValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
if (!Equals(value, otherPropertyValue)) {
if (OtherPropertyDisplayName == null) {
OtherPropertyDisplayName = GetDisplayNameForProperty(validationContext.ObjectType, OtherProperty);
}
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
private static string GetDisplayNameForProperty(Type containerType, string propertyName) {
ICustomTypeDescriptor typeDescriptor = GetTypeDescriptor(containerType);
PropertyDescriptor property = typeDescriptor.GetProperties().Find(propertyName, true);
if (property == null) {
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
DataAnnotationsResources.Common_PropertyNotFound, containerType.FullName, propertyName));
}
IEnumerable<Attribute> attributes = property.Attributes.Cast<Attribute>();
DisplayAttribute display = attributes.OfType<DisplayAttribute>().FirstOrDefault();
if (display != null) {
return display.GetName();
}
DisplayNameAttribute displayName = attributes.OfType<DisplayNameAttribute>().FirstOrDefault();
if (displayName != null) {
return displayName.DisplayName;
}
return propertyName;
}
private static ICustomTypeDescriptor GetTypeDescriptor(Type type) {
return new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type);
}
}
}

View File

@ -0,0 +1,10 @@

namespace System.ComponentModel.DataAnnotations {
/// <summary>
/// This attribute is used to mark the members of a Type that participate in
/// optimistic concurrency checks.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public sealed class ConcurrencyCheckAttribute : Attribute {
}
}

View File

@ -0,0 +1,51 @@
namespace System.ComponentModel.DataAnnotations {
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Resources;
using System.Linq;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class CreditCardAttribute : DataTypeAttribute {
public CreditCardAttribute()
: base(DataType.CreditCard) {
// DevDiv 468241: set DefaultErrorMessage not ErrorMessage, allowing user to set
// ErrorMessageResourceType and ErrorMessageResourceName to use localized messages.
DefaultErrorMessage = DataAnnotationsResources.CreditCardAttribute_Invalid;
}
public override bool IsValid(object value) {
if (value == null) {
return true;
}
string ccValue = value as string;
if (ccValue == null) {
return false;
}
ccValue = ccValue.Replace("-", "");
ccValue = ccValue.Replace(" ", "");
int checksum = 0;
bool evenDigit = false;
// http://www.beachnet.com/~hstiles/cardtype.html
foreach (char digit in ccValue.Reverse()) {
if (digit < '0' || digit > '9') {
return false;
}
int digitValue = (digit - '0') * (evenDigit ? 2 : 1);
evenDigit = !evenDigit;
while (digitValue > 0) {
checksum += digitValue % 10;
digitValue /= 10;
}
}
return (checksum % 10) == 0;
}
}
}

View File

@ -0,0 +1,307 @@
using System.ComponentModel.DataAnnotations.Resources;
using System.Globalization;
using System.Reflection;
namespace System.ComponentModel.DataAnnotations {
/// <summary>
/// Validation attribute that executes a user-supplied method at runtime, using one of these signatures:
/// <para>
/// public static <see cref="ValidationResult"/> Method(object value) { ... }
/// </para>
/// <para>
/// public static <see cref="ValidationResult"/> Method(object value, <see cref="ValidationContext"/> context) { ... }
/// </para>
/// <para>
/// The value can be strongly typed as type conversion will be attempted.
/// </para>
/// </summary>
/// <remarks>
/// This validation attribute is used to invoke custom logic to perform validation at runtime.
/// Like any other <see cref="ValidationAttribute"/>, its <see cref="IsValid(object, ValidationContext)"/>
/// method is invoked to perform validation. This implementation simply redirects that call to the method
/// identified by <see cref="Method"/> on a type identified by <see cref="ValidatorType"/>
/// <para>
/// The supplied <see cref="ValidatorType"/> cannot be null, and it must be a public type.
/// </para>
/// <para>
/// The named <see cref="Method"/> must be public, static, return <see cref="ValidationResult"/> and take at
/// least one input parameter for the value to be validated. This value parameter may be strongly typed.
/// Type conversion will be attempted if clients pass in a value of a different type.
/// </para>
/// <para>
/// The <see cref="Method"/> may also declare an additional parameter of type <see cref="ValidationContext"/>.
/// The <see cref="ValidationContext"/> parameter provides additional context the method may use to determine
/// the context in which it is being used.
/// </para>
/// <para>
/// If the method returns <see cref="ValidationResult"/>.<see cref="ValidationResult.Success"/>, that indicates the given value is acceptable and validation passed.
/// Returning an instance of <see cref="ValidationResult"/> indicates that the value is not acceptable
/// and validation failed.
/// </para>
/// <para>
/// If the method returns a <see cref="ValidationResult"/> with a <c>null</c> <see cref="ValidationResult.ErrorMessage"/>
/// then the normal <see cref="ValidationAttribute.FormatErrorMessage"/> method will be called to compose the error message.
/// </para>
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter, AllowMultiple = true)]
public sealed class CustomValidationAttribute : ValidationAttribute {
#region Member Fields
private Type _validatorType;
private string _method;
private MethodInfo _methodInfo;
private bool _isSingleArgumentMethod;
private string _lastMessage;
private Type _valuesType;
private Lazy<string> _malformedErrorMessage;
#if !SILVERLIGHT
private Tuple<string, Type> _typeId;
#endif
#endregion
#region All Constructors
/// <summary>
/// Instantiates a custom validation attribute that will invoke a method in the
/// specified type.
/// </summary>
/// <remarks>An invalid <paramref name="validatorType"/> or <paramref name="Method"/> will be cause
/// <see cref="IsValid(object, ValidationContext)"/>> to return a <see cref="ValidationResult"/>
/// and <see cref="ValidationAttribute.FormatErrorMessage"/> to return a summary error message.
/// </remarks>
/// <param name="validatorType">The type that will contain the method to invoke. It cannot be null. See <see cref="Method"/>.</param>
/// <param name="method">The name of the method to invoke in <paramref name="validatorType"/>.</param>
public CustomValidationAttribute(Type validatorType, string method)
: base(() => DataAnnotationsResources.CustomValidationAttribute_ValidationError) {
this._validatorType = validatorType;
this._method = method;
_malformedErrorMessage = new Lazy<string>(CheckAttributeWellFormed);
}
#endregion
#region Properties
/// <summary>
/// Gets the type that contains the validation method identified by <see cref="Method"/>.
/// </summary>
public Type ValidatorType {
get {
return this._validatorType;
}
}
/// <summary>
/// Gets the name of the method in <see cref="ValidatorType"/> to invoke to perform validation.
/// </summary>
public string Method {
get {
return this._method;
}
}
#if !SILVERLIGHT
/// <summary>
/// Gets a unique identifier for this attribute.
/// </summary>
public override object TypeId {
get {
if (_typeId == null) {
_typeId = new Tuple<string, Type>(this._method, this._validatorType);
}
return _typeId;
}
}
#endif
#endregion
/// <summary>
/// Override of validation method. See <see cref="ValidationAttribute.IsValid(object, ValidationContext)"/>.
/// </summary>
/// <param name="value">The value to validate.</param>
/// <param name="validationContext">A <see cref="ValidationContext"/> instance that provides
/// context about the validation operation, such as the object and member being validated.</param>
/// <returns>Whatever the <see cref="Method"/> in <see cref="ValidatorType"/> returns.</returns>
/// <exception cref="InvalidOperationException"> is thrown if the current attribute is malformed.</exception>
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
// If attribute is not valid, throw an exeption right away to inform the developer
this.ThrowIfAttributeNotWellFormed();
MethodInfo methodInfo = this._methodInfo;
// If the value is not of the correct type and cannot be converted, fail
// to indicate it is not acceptable. The convention is that IsValid is merely a probe,
// and clients are not expecting exceptions.
object convertedValue;
if (!this.TryConvertValue(value, out convertedValue)) {
return new ValidationResult(String.Format(CultureInfo.CurrentCulture, Resources.DataAnnotationsResources.CustomValidationAttribute_Type_Conversion_Failed,
(value != null ? value.GetType().ToString() : "null"), this._valuesType, this._validatorType, this._method));
}
// Invoke the method. Catch TargetInvocationException merely to unwrap it.
// Callers don't know Reflection is being used and will not typically see
// the real exception
try {
// 1-parameter form is ValidationResult Method(object value)
// 2-parameter form is ValidationResult Method(object value, ValidationContext context),
object[] methodParams = this._isSingleArgumentMethod
? new object[] { convertedValue }
: new object[] { convertedValue, validationContext };
ValidationResult result = (ValidationResult)methodInfo.Invoke(null, methodParams);
// We capture the message they provide us only in the event of failure,
// otherwise we use the normal message supplied via the ctor
this._lastMessage = null;
if (result != null) {
this._lastMessage = result.ErrorMessage;
}
return result;
} catch (TargetInvocationException tie) {
if (tie.InnerException != null) {
throw tie.InnerException;
}
throw;
}
}
/// <summary>
/// Override of <see cref="ValidationAttribute.FormatErrorMessage"/>
/// </summary>
/// <param name="name">The name to include in the formatted string</param>
/// <returns>A localized string to describe the problem.</returns>
/// <exception cref="InvalidOperationException"> is thrown if the current attribute is malformed.</exception>
public override string FormatErrorMessage(string name) {
// If attribute is not valid, throw an exeption right away to inform the developer
this.ThrowIfAttributeNotWellFormed();
if (!string.IsNullOrEmpty(this._lastMessage)) {
return String.Format(CultureInfo.CurrentCulture, this._lastMessage, name);
}
// If success or they supplied no custom message, use normal base class behavior
return base.FormatErrorMessage(name);
}
/// <summary>
/// Checks whether the current attribute instance itself is valid for use.
/// </summary>
/// <returns>The error message why it is not well-formed, null if it is well-formed.</returns>
private string CheckAttributeWellFormed() {
return this.ValidateValidatorTypeParameter() ?? this.ValidateMethodParameter();
}
/// <summary>
/// Internal helper to determine whether <see cref="ValidatorType"/> is legal for use.
/// </summary>
/// <returns><c>null</c> or the appropriate error message.</returns>
private string ValidateValidatorTypeParameter() {
if (this._validatorType == null) {
return DataAnnotationsResources.CustomValidationAttribute_ValidatorType_Required;
}
if (!this._validatorType.IsVisible) {
return String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.CustomValidationAttribute_Type_Must_Be_Public, this._validatorType.Name);
}
return null;
}
/// <summary>
/// Internal helper to determine whether <see cref="Method"/> is legal for use.
/// </summary>
/// <returns><c>null</c> or the appropriate error message.</returns>
private string ValidateMethodParameter() {
if (String.IsNullOrEmpty(this._method)) {
return DataAnnotationsResources.CustomValidationAttribute_Method_Required;
}
// Named method must be public and static
MethodInfo methodInfo = this._validatorType.GetMethod(this._method, BindingFlags.Public | BindingFlags.Static);
if (methodInfo == null) {
return String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.CustomValidationAttribute_Method_Not_Found, this._method, this._validatorType.Name);
}
// Method must return a ValidationResult
if (methodInfo.ReturnType != typeof(ValidationResult)) {
return String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.CustomValidationAttribute_Method_Must_Return_ValidationResult, this._method, this._validatorType.Name);
}
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
// Must declare at least one input parameter for the value and it cannot be ByRef
if (parameterInfos.Length == 0 || parameterInfos[0].ParameterType.IsByRef) {
return String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.CustomValidationAttribute_Method_Signature, this._method, this._validatorType.Name);
}
// We accept 2 forms:
// 1-parameter form is ValidationResult Method(object value)
// 2-parameter form is ValidationResult Method(object value, ValidationContext context),
this._isSingleArgumentMethod = (parameterInfos.Length == 1);
if (!this._isSingleArgumentMethod) {
if ((parameterInfos.Length != 2) || (parameterInfos[1].ParameterType != typeof(ValidationContext))) {
return String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.CustomValidationAttribute_Method_Signature, this._method, this._validatorType.Name);
}
}
this._methodInfo = methodInfo;
this._valuesType = parameterInfos[0].ParameterType;
return null;
}
/// <summary>
/// Throws InvalidOperationException if the attribute is not valid.
/// </summary>
private void ThrowIfAttributeNotWellFormed() {
string errorMessage = _malformedErrorMessage.Value;
if (errorMessage != null) {
throw new InvalidOperationException(errorMessage);
}
}
/// <summary>
/// Attempts to convert the given value to the type needed to invoke the method for the current
/// CustomValidationAttribute.
/// </summary>
/// <param name="value">The value to check/convert.</param>
/// <param name="convertedValue">If successful, the converted (or copied) value.</param>
/// <returns><c>true</c> if type value was already correct or was successfully converted.</returns>
private bool TryConvertValue(object value, out object convertedValue) {
convertedValue = null;
Type t = this._valuesType;
// Null is permitted for reference types or for Nullable<>'s only
if (value == null) {
if (t.IsValueType && (!t.IsGenericType || t.GetGenericTypeDefinition() != typeof(Nullable<>))) {
return false;
}
return true; // convertedValue already null, which is correct for this case
}
// If the type is already legally assignable, we're good
if (t.IsAssignableFrom(value.GetType())) {
convertedValue = value;
return true;
}
// Value is not the right type -- attempt a convert.
// Any expected exception returns a false
try {
convertedValue = Convert.ChangeType(value, t, CultureInfo.CurrentCulture);
return true;
} catch (FormatException) {
return false;
} catch (InvalidCastException) {
return false;
} catch (NotSupportedException) {
return false;
}
}
}
}

View File

@ -0,0 +1,92 @@

namespace System.ComponentModel.DataAnnotations {
/// <summary>
/// Enumeration of logical data types that may appear in <see cref="DataTypeAttribute"/>
/// </summary>
public enum DataType {
/// <summary>
/// Custom data type, not one of the static data types we know
/// </summary>
Custom,
/// <summary>
/// DateTime data type
/// </summary>
DateTime,
/// <summary>
/// Date data type
/// </summary>
Date,
/// <summary>
/// Time data type
/// </summary>
Time,
/// <summary>
/// Duration data type
/// </summary>
Duration,
/// <summary>
/// Phone number data type
/// </summary>
PhoneNumber,
/// <summary>
/// Currency data type
/// </summary>
Currency,
/// <summary>
/// Plain text data type
/// </summary>
Text,
/// <summary>
/// Html data type
/// </summary>
Html,
/// <summary>
/// Multiline text data type
/// </summary>
MultilineText,
/// <summary>
/// Email address data type
/// </summary>
EmailAddress,
/// <summary>
/// Password data type -- do not echo in UI
/// </summary>
Password,
/// <summary>
/// URL data type
/// </summary>
Url,
/// <summary>
/// URL to an Image -- to be displayed as an image instead of text
/// </summary>
ImageUrl,
/// <summary>
/// Credit card data type
/// </summary>
CreditCard,
/// <summary>
/// Postal code data type
/// </summary>
PostalCode,
/// <summary>
/// File upload data type
/// </summary>
Upload
}
}

View File

@ -0,0 +1,116 @@
using System.ComponentModel.DataAnnotations.Resources;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace System.ComponentModel.DataAnnotations {
/// <summary>
/// Allows for clarification of the <see cref="DataType"/> represented by a given
/// property (such as <see cref="System.ComponentModel.DataAnnotations.DataType.PhoneNumber"/>
/// or <see cref="System.ComponentModel.DataAnnotations.DataType.Url"/>)
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter, AllowMultiple = false)]
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "We want users to be able to extend this class")]
public class DataTypeAttribute : ValidationAttribute {
/// <summary>
/// Gets the DataType. If it equals DataType.Custom, <see cref="CustomDataType"/> should also be retrieved.
/// </summary>
public DataType DataType { get; private set; }
/// <summary>
/// Gets the string representing a custom data type. Returns a non-null value only if <see cref="DataType"/> is DataType.Custom.
/// </summary>
public string CustomDataType { get; private set; }
/// <summary>
/// Return the name of the data type, either using the <see cref="DataType"/> enum or <see cref="CustomDataType"/> string
/// </summary>
/// <returns>The name of the data type enum</returns>
/// <exception cref="InvalidOperationException"> is thrown if the current attribute is ill-formed.</exception>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method throws an exception if the properties have not been configured correctly")]
public virtual string GetDataTypeName() {
this.EnsureValidDataType();
if (DataType == DataType.Custom) {
// If it's a custom type string, use it as the template name
return this.CustomDataType;
} else {
// If it's an enum, turn it into a string
// Use the cached array with enum string values instead of ToString() as the latter is too slow
return _dataTypeStrings[(int)DataType];
}
}
/// <summary>
/// Gets the default display format that gets used along with this DataType.
/// </summary>
public DisplayFormatAttribute DisplayFormat { get; protected set; }
/// <summary>
/// Constructor that accepts a data type enumeration
/// </summary>
/// <param name="dataType">The <see cref="DataType"/> enum value indicating the type to apply.</param>
public DataTypeAttribute(DataType dataType) {
DataType = dataType;
// Set some DisplayFormat for a few specific data types
switch (dataType) {
case DataType.Date:
this.DisplayFormat = new DisplayFormatAttribute();
this.DisplayFormat.DataFormatString = "{0:d}";
this.DisplayFormat.ApplyFormatInEditMode = true;
break;
case DataType.Time:
this.DisplayFormat = new DisplayFormatAttribute();
this.DisplayFormat.DataFormatString = "{0:t}";
this.DisplayFormat.ApplyFormatInEditMode = true;
break;
case DataType.Currency:
this.DisplayFormat = new DisplayFormatAttribute();
this.DisplayFormat.DataFormatString = "{0:C}";
// Don't set ApplyFormatInEditMode for currencies because the currency
// symbol can't be parsed
break;
}
}
/// <summary>
/// Constructor that accepts the string name of a custom data type
/// </summary>
/// <param name="customDataType">The string name of the custom data type.</param>
public DataTypeAttribute(string customDataType)
: this(DataType.Custom) {
this.CustomDataType = customDataType;
}
/// <summary>
/// Override of <see cref="ValidationAttribute.IsValid(object)"/>
/// </summary>
/// <remarks>This override always returns <c>true</c>. Subclasses should override this to provide the correct result.</remarks>
/// <param name="value">The value to validate</param>
/// <returns>Unconditionally returns <c>true</c></returns>
/// <exception cref="InvalidOperationException"> is thrown if the current attribute is ill-formed.</exception>
#if !SILVERLIGHT
public
#else
internal
#endif
override bool IsValid(object value) {
this.EnsureValidDataType();
return true;
}
/// <summary>
/// Throws an exception if this attribute is not correctly formed
/// </summary>
/// <exception cref="InvalidOperationException"> is thrown if the current attribute is ill-formed.</exception>
private void EnsureValidDataType() {
if (this.DataType == DataType.Custom && String.IsNullOrEmpty(this.CustomDataType)) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.DataTypeAttribute_EmptyDataTypeString));
}
}
private static string[] _dataTypeStrings = Enum.GetNames(typeof(DataType));
}
}

View File

@ -0,0 +1,486 @@
using System.ComponentModel.DataAnnotations.Resources;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace System.ComponentModel.DataAnnotations {
/// <summary>
/// DisplayAttribute is a general-purpose attribute to specify user-visible globalizable strings for types and members.
/// The string properties of this class can be used either as literals or as resource identifiers into a specified
/// <see cref="ResourceType"/>
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = false)]
public sealed class DisplayAttribute : Attribute {
#region Member Fields
private Type _resourceType;
private LocalizableString _shortName = new LocalizableString("ShortName");
private LocalizableString _name = new LocalizableString("Name");
private LocalizableString _description = new LocalizableString("Description");
private LocalizableString _prompt = new LocalizableString("Prompt");
private LocalizableString _groupName = new LocalizableString("GroupName");
private bool? _autoGenerateField;
private bool? _autoGenerateFilter;
private int? _order;
#endregion
#region All Constructors
/// <summary>
/// Default constructor for DisplayAttribute. All associated string properties and methods will return <c>null</c>.
/// </summary>
public DisplayAttribute() {
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the ShortName attribute property, which may be a resource key string.
/// <para>
/// Consumers must use the <see cref="GetShortName"/> method to retrieve the UI display string.
/// </para>
/// </summary>
/// <remarks>
/// The property contains either the literal, non-localized string or the resource key
/// to be used in conjunction with <see cref="ResourceType"/> to configure a localized
/// short name for display.
/// <para>
/// The <see cref="GetShortName"/> method will return either the literal, non-localized
/// string or the localized string when <see cref="ResourceType"/> has been specified.
/// </para>
/// </remarks>
/// <value>
/// The short name is generally used as the grid column label for a UI element bound to the member
/// bearing this attribute. A <c>null</c> or empty string is legal, and consumers must allow for that.
/// </value>
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "The property and method are a matched pair")]
public string ShortName {
get {
return this._shortName.Value;
}
set {
if (this._shortName.Value != value) {
this._shortName.Value = value;
}
}
}
/// <summary>
/// Gets or sets the Name attribute property, which may be a resource key string.
/// <para>
/// Consumers must use the <see cref="GetName"/> method to retrieve the UI display string.
/// </para>
/// </summary>
/// <remarks>
/// The property contains either the literal, non-localized string or the resource key
/// to be used in conjunction with <see cref="ResourceType"/> to configure a localized
/// name for display.
/// <para>
/// The <see cref="GetName"/> method will return either the literal, non-localized
/// string or the localized string when <see cref="ResourceType"/> has been specified.
/// </para>
/// </remarks>
/// <value>
/// The name is generally used as the field label for a UI element bound to the member
/// bearing this attribute. A <c>null</c> or empty string is legal, and consumers must allow for that.
/// </value>
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "The property and method are a matched pair")]
public string Name {
get {
return this._name.Value;
}
set {
if (this._name.Value != value) {
this._name.Value = value;
}
}
}
/// <summary>
/// Gets or sets the Description attribute property, which may be a resource key string.
/// <para>
/// Consumers must use the <see cref="GetDescription"/> method to retrieve the UI display string.
/// </para>
/// </summary>
/// <remarks>
/// The property contains either the literal, non-localized string or the resource key
/// to be used in conjunction with <see cref="ResourceType"/> to configure a localized
/// description for display.
/// <para>
/// The <see cref="GetDescription"/> method will return either the literal, non-localized
/// string or the localized string when <see cref="ResourceType"/> has been specified.
/// </para>
/// </remarks>
/// <value>
/// Description is generally used as a tool tip or description a UI element bound to the member
/// bearing this attribute. A <c>null</c> or empty string is legal, and consumers must allow for that.
/// </value>
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "The property and method are a matched pair")]
public string Description {
get {
return this._description.Value;
}
set {
if (this._description.Value != value) {
this._description.Value = value;
}
}
}
/// <summary>
/// Gets or sets the Prompt attribute property, which may be a resource key string.
/// <para>
/// Consumers must use the <see cref="GetPrompt"/> method to retrieve the UI display string.
/// </para>
/// </summary>
/// <remarks>
/// The property contains either the literal, non-localized string or the resource key
/// to be used in conjunction with <see cref="ResourceType"/> to configure a localized
/// prompt for display.
/// <para>
/// The <see cref="GetPrompt"/> method will return either the literal, non-localized
/// string or the localized string when <see cref="ResourceType"/> has been specified.
/// </para>
/// </remarks>
/// <value>
/// A prompt is generally used as a prompt or watermark for a UI element bound to the member
/// bearing this attribute. A <c>null</c> or empty string is legal, and consumers must allow for that.
/// </value>
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "The property and method are a matched pair")]
public string Prompt {
get {
return this._prompt.Value;
}
set {
if (this._prompt.Value != value) {
this._prompt.Value = value;
}
}
}
/// <summary>
/// Gets or sets the GroupName attribute property, which may be a resource key string.
/// <para>
/// Consumers must use the <see cref="GetGroupName"/> method to retrieve the UI display string.
/// </para>
/// </summary>
/// <remarks>
/// The property contains either the literal, non-localized string or the resource key
/// to be used in conjunction with <see cref="ResourceType"/> to configure a localized
/// group name for display.
/// <para>
/// The <see cref="GetGroupName"/> method will return either the literal, non-localized
/// string or the localized string when <see cref="ResourceType"/> has been specified.
/// </para>
/// </remarks>
/// <value>
/// A group name is used for grouping fields into the UI. A <c>null</c> or empty string is legal,
/// and consumers must allow for that.
/// </value>
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "The property and method are a matched pair")]
public string GroupName {
get {
return this._groupName.Value;
}
set {
if (this._groupName.Value != value) {
this._groupName.Value = value;
}
}
}
/// <summary>
/// Gets or sets the <see cref="System.Type"/> that contains the resources for <see cref="ShortName"/>,
/// <see cref="Name"/>, <see cref="Description"/>, <see cref="Prompt"/>, and <see cref="GroupName"/>.
/// Using <see cref="ResourceType"/> along with these Key properties, allows the <see cref="GetShortName"/>,
/// <see cref="GetName"/>, <see cref="GetDescription"/>, <see cref="GetPrompt"/>, and <see cref="GetGroupName"/>
/// methods to return localized values.
/// </summary>
public Type ResourceType {
get {
return this._resourceType;
}
set {
if (this._resourceType != value) {
this._resourceType = value;
this._shortName.ResourceType = value;
this._name.ResourceType = value;
this._description.ResourceType = value;
this._prompt.ResourceType = value;
this._groupName.ResourceType = value;
}
}
}
/// <summary>
/// Gets or sets whether UI should be generated automatically to display this field. If this property is not
/// set then the presentation layer will automatically determine whether UI should be generated. Setting this
/// property allows an override of the default behavior of the presentation layer.
/// <para>
/// Consumers must use the <see cref="GetAutoGenerateField"/> method to retrieve the value, as this property getter will throw
/// an exception if the value has not been set.
/// </para>
/// </summary>
/// <exception cref="System.InvalidOperationException">
/// If the getter of this property is invoked when the value has not been explicitly set using the setter.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "The property and method are a matched pair")]
public bool AutoGenerateField {
get {
if (!this._autoGenerateField.HasValue) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.DisplayAttribute_PropertyNotSet, "AutoGenerateField", "GetAutoGenerateField"));
}
return this._autoGenerateField.Value;
}
set {
this._autoGenerateField = value;
}
}
/// <summary>
/// Gets or sets whether UI should be generated automatically to display filtering for this field. If this property is not
/// set then the presentation layer will automatically determine whether filtering UI should be generated. Setting this
/// property allows an override of the default behavior of the presentation layer.
/// <para>
/// Consumers must use the <see cref="GetAutoGenerateFilter"/> method to retrieve the value, as this property getter will throw
/// an exception if the value has not been set.
/// </para>
/// </summary>
/// <exception cref="System.InvalidOperationException">
/// If the getter of this property is invoked when the value has not been explicitly set using the setter.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "The property and method are a matched pair")]
public bool AutoGenerateFilter {
get {
if (!this._autoGenerateFilter.HasValue) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.DisplayAttribute_PropertyNotSet, "AutoGenerateFilter", "GetAutoGenerateFilter"));
}
return this._autoGenerateFilter.Value;
}
set {
this._autoGenerateFilter = value;
}
}
/// <summary>
/// Gets or sets the order in which this field should be displayed. If this property is not set then
/// the presentation layer will automatically determine the order. Setting this property explicitly
/// allows an override of the default behavior of the presentation layer.
/// <para>
/// Consumers must use the <see cref="GetOrder"/> method to retrieve the value, as this property getter will throw
/// an exception if the value has not been set.
/// </para>
/// </summary>
/// <exception cref="System.InvalidOperationException">
/// If the getter of this property is invoked when the value has not been explicitly set using the setter.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "The property and method are a matched pair")]
public int Order {
get {
if (!this._order.HasValue) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.DisplayAttribute_PropertyNotSet, "Order", "GetOrder"));
}
return this._order.Value;
}
set {
this._order = value;
}
}
#endregion
#region Methods
/// <summary>
/// Gets the UI display string for ShortName.
/// <para>
/// This can be either a literal, non-localized string provided to <see cref="ShortName"/> or the
/// localized string found when <see cref="ResourceType"/> has been specified and <see cref="ShortName"/>
/// represents a resource key within that resource type.
/// </para>
/// </summary>
/// <returns>
/// When <see cref="ResourceType"/> has not been specified, the value of
/// <see cref="ShortName"/> will be returned.
/// <para>
/// When <see cref="ResourceType"/> has been specified and <see cref="ShortName"/>
/// represents a resource key within that resource type, then the localized value will be returned.
/// </para>
/// <para>
/// If <see cref="ShortName"/> is <c>null</c>, the value from <see cref="GetName"/> will be returned.
/// </para>
/// </returns>
/// <exception cref="System.InvalidOperationException">
/// After setting both the <see cref="ResourceType"/> property and the <see cref="ShortName"/> property,
/// but a public static property with a name matching the <see cref="ShortName"/> value couldn't be found
/// on the <see cref="ResourceType"/>.
/// </exception>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method does work using a property of the same name")]
public string GetShortName() {
return this._shortName.GetLocalizableValue() ?? this.GetName();
}
/// <summary>
/// Gets the UI display string for Name.
/// <para>
/// This can be either a literal, non-localized string provided to <see cref="Name"/> or the
/// localized string found when <see cref="ResourceType"/> has been specified and <see cref="Name"/>
/// represents a resource key within that resource type.
/// </para>
/// </summary>
/// <returns>
/// When <see cref="ResourceType"/> has not been specified, the value of
/// <see cref="Name"/> will be returned.
/// <para>
/// When <see cref="ResourceType"/> has been specified and <see cref="Name"/>
/// represents a resource key within that resource type, then the localized value will be returned.
/// </para>
/// <para>
/// Can return <c>null</c> and will not fall back onto other values, as it's more likely for the
/// consumer to want to fall back onto the property name.
/// </para>
/// </returns>
/// <exception cref="System.InvalidOperationException">
/// After setting both the <see cref="ResourceType"/> property and the <see cref="Name"/> property,
/// but a public static property with a name matching the <see cref="Name"/> value couldn't be found
/// on the <see cref="ResourceType"/>.
/// </exception>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method does work using a property of the same name")]
public string GetName() {
return this._name.GetLocalizableValue();
}
/// <summary>
/// Gets the UI display string for Description.
/// <para>
/// This can be either a literal, non-localized string provided to <see cref="Description"/> or the
/// localized string found when <see cref="ResourceType"/> has been specified and <see cref="Description"/>
/// represents a resource key within that resource type.
/// </para>
/// </summary>
/// <returns>
/// When <see cref="ResourceType"/> has not been specified, the value of
/// <see cref="Description"/> will be returned.
/// <para>
/// When <see cref="ResourceType"/> has been specified and <see cref="Description"/>
/// represents a resource key within that resource type, then the localized value will be returned.
/// </para>
/// </returns>
/// <exception cref="System.InvalidOperationException">
/// After setting both the <see cref="ResourceType"/> property and the <see cref="Description"/> property,
/// but a public static property with a name matching the <see cref="Description"/> value couldn't be found
/// on the <see cref="ResourceType"/>.
/// </exception>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method does work using a property of the same name")]
public string GetDescription() {
return this._description.GetLocalizableValue();
}
/// <summary>
/// Gets the UI display string for Prompt.
/// <para>
/// This can be either a literal, non-localized string provided to <see cref="Prompt"/> or the
/// localized string found when <see cref="ResourceType"/> has been specified and <see cref="Prompt"/>
/// represents a resource key within that resource type.
/// </para>
/// </summary>
/// <returns>
/// When <see cref="ResourceType"/> has not been specified, the value of
/// <see cref="Prompt"/> will be returned.
/// <para>
/// When <see cref="ResourceType"/> has been specified and <see cref="Prompt"/>
/// represents a resource key within that resource type, then the localized value will be returned.
/// </para>
/// </returns>
/// <exception cref="System.InvalidOperationException">
/// After setting both the <see cref="ResourceType"/> property and the <see cref="Prompt"/> property,
/// but a public static property with a name matching the <see cref="Prompt"/> value couldn't be found
/// on the <see cref="ResourceType"/>.
/// </exception>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method does work using a property of the same name")]
public string GetPrompt() {
return this._prompt.GetLocalizableValue();
}
/// <summary>
/// Gets the UI display string for GroupName.
/// <para>
/// This can be either a literal, non-localized string provided to <see cref="GroupName"/> or the
/// localized string found when <see cref="ResourceType"/> has been specified and <see cref="GroupName"/>
/// represents a resource key within that resource type.
/// </para>
/// </summary>
/// <returns>
/// When <see cref="ResourceType"/> has not been specified, the value of
/// <see cref="GroupName"/> will be returned.
/// <para>
/// When <see cref="ResourceType"/> has been specified and <see cref="GroupName"/>
/// represents a resource key within that resource type, then the localized value will be returned.
/// </para>
/// </returns>
/// <exception cref="System.InvalidOperationException">
/// After setting both the <see cref="ResourceType"/> property and the <see cref="GroupName"/> property,
/// but a public static property with a name matching the <see cref="GroupName"/> value couldn't be found
/// on the <see cref="ResourceType"/>.
/// </exception>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method does work using a property of the same name")]
public string GetGroupName() {
return this._groupName.GetLocalizableValue();
}
/// <summary>
/// Gets the value of <see cref="AutoGenerateField"/> if it has been set, or <c>null</c>.
/// </summary>
/// <returns>
/// When <see cref="AutoGenerateField"/> has been set returns the value of that property.
/// <para>
/// When <see cref="AutoGenerateField"/> has not been set returns <c>null</c>.
/// </para>
/// </returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method does work using a property of the same name")]
public bool? GetAutoGenerateField() {
return this._autoGenerateField;
}
/// <summary>
/// Gets the value of <see cref="AutoGenerateFilter"/> if it has been set, or <c>null</c>.
/// </summary>
/// <returns>
/// When <see cref="AutoGenerateFilter"/> has been set returns the value of that property.
/// <para>
/// When <see cref="AutoGenerateFilter"/> has not been set returns <c>null</c>.
/// </para>
/// </returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method does work using a property of the same name")]
public bool? GetAutoGenerateFilter() {
return this._autoGenerateFilter;
}
/// <summary>
/// Gets the value of <see cref="Order"/> if it has been set, or <c>null</c>.
/// </summary>
/// <returns>
/// When <see cref="Order"/> has been set returns the value of that property.
/// <para>
/// When <see cref="Order"/> has not been set returns <c>null</c>.
/// </para>
/// </returns>
/// <remarks>
/// When an order is not specified, presentation layers should consider using the value
/// of 10000. This value allows for explicitly-ordered fields to be displayed before
/// and after the fields that don't specify an order.
/// </remarks>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method does work using a property of the same name")]
public int? GetOrder() {
return this._order;
}
#endregion
}
}

View File

@ -0,0 +1,30 @@
using System.Diagnostics.CodeAnalysis;
namespace System.ComponentModel.DataAnnotations {
/// <summary>
/// Sets the display column, the sort column, and the sort order for when a table is used as a parent table in FK relationships.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "We want users to be able to extend this class")]
public class DisplayColumnAttribute : Attribute {
public DisplayColumnAttribute(string displayColumn)
: this(displayColumn, null) {
}
public DisplayColumnAttribute(string displayColumn, string sortColumn)
: this(displayColumn, sortColumn, false) {
}
public DisplayColumnAttribute(string displayColumn, string sortColumn, bool sortDescending) {
this.DisplayColumn = displayColumn;
this.SortColumn = sortColumn;
this.SortDescending = sortDescending;
}
public string DisplayColumn { get; private set; }
public string SortColumn { get; private set; }
public bool SortDescending { get; private set; }
}
}

View File

@ -0,0 +1,48 @@
using System.Diagnostics.CodeAnalysis;
namespace System.ComponentModel.DataAnnotations {
/// <summary>
/// Allows overriding various display-related options for a given field. The options have the same meaning as in BoundField.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "We want users to be able to extend this class")]
public class DisplayFormatAttribute : Attribute {
/// <summary>
/// Gets or sets the format string
/// </summary>
public string DataFormatString { get; set; }
/// <summary>
/// Gets or sets the string to display when the value is null
/// </summary>
public string NullDisplayText { get; set; }
/// <summary>
/// Gets or sets a value indicating whether empty strings should be set to null
/// </summary>
public bool ConvertEmptyStringToNull { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the format string should be used in edit mode
/// </summary>
public bool ApplyFormatInEditMode { get; set; }
#if !SILVERLIGHT
/// <summary>
/// Gets or sets a value indicating whether the field should be html encoded
/// </summary>
public bool HtmlEncode { get; set; }
#endif
/// <summary>
/// Default constructor
/// </summary>
public DisplayFormatAttribute() {
this.ConvertEmptyStringToNull = true; // default to true to match behavior in related components
#if !SILVERLIGHT
this.HtmlEncode = true; // default to true to match behavior in related components
#endif
}
}
}

View File

@ -0,0 +1,61 @@

namespace System.ComponentModel.DataAnnotations {
/// <summary>
/// Indicates whether the consumer of a field or property, such as a client application,
/// should allow editing of the value.
/// </summary>
/// <remarks>
/// This attribute neither enforces nor guarantees editability; the underlying data
/// store might allow changing the data regardless of this attribute. The presence
/// of this attribute signals intent to the consumer of the attribute whethere or not
/// the end user should be allowed to change the value via the client application.
/// </remarks>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class EditableAttribute : Attribute {
/// <summary>
/// Indicates whether or not the field/property allows editing of the
/// value.
/// </summary>
/// <value>
/// When <c>true</c>, the field/property is editable.
/// <para>
/// When <c>false</c>, the field/property is not editable.
/// </para>
/// </value>
public bool AllowEdit { get; private set; }
/// <summary>
/// Indicates whether or not the field/property allows an initial value
/// to be specified.
/// </summary>
/// <remarks>
/// The value of this property defaults to match the <see cref="AllowEdit"/>
/// property value specified in the constructor.
/// </remarks>
/// <value>
/// When <c>true</c>, the field/property can have its value set for
/// newly constructed instances, such as during an insert operation.
/// <para>
/// When <c>false</c>, the field/property cannot have its
/// value provided for newly constructed instances, such as during
/// an insert operation. This will often indicate that the value
/// is auto-generated by the persistence store.
/// </para>
/// </value>
public bool AllowInitialValue { get; set; }
/// <summary>
/// Indicate whether or not a field/property is editable.
/// </summary>
/// <param name="allowEdit">
/// Indicates whether the field/property is editable. The value provided
/// will apply to both <see cref="AllowEdit"/> and
/// <see cref="AllowInitialValue"/> unless the <see cref="AllowInitialValue"/>
/// property is explicitly specified.
/// </param>
public EditableAttribute(bool allowEdit) {
this.AllowEdit = allowEdit;
this.AllowInitialValue = allowEdit;
}
}
}

View File

@ -0,0 +1,74 @@
namespace System.ComponentModel.DataAnnotations {
using System;
using System.ComponentModel.DataAnnotations.Resources;
using System.Text.RegularExpressions;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class EmailAddressAttribute : DataTypeAttribute {
// This attribute provides server-side email validation equivalent to jquery validate,
// and therefore shares the same regular expression. See unit tests for examples.
private static Regex _regex = CreateRegEx();
public EmailAddressAttribute()
: base(DataType.EmailAddress) {
// DevDiv 468241: set DefaultErrorMessage not ErrorMessage, allowing user to set
// ErrorMessageResourceType and ErrorMessageResourceName to use localized messages.
DefaultErrorMessage = DataAnnotationsResources.EmailAddressAttribute_Invalid;
}
public override bool IsValid(object value) {
if (value == null) {
return true;
}
string valueAsString = value as string;
// Use RegEx implementation if it has been created, otherwise use a non RegEx version.
if (_regex != null) {
return valueAsString != null && _regex.Match(valueAsString).Length > 0;
}
else {
int atCount = 0;
foreach (char c in valueAsString) {
if (c == '@') {
atCount++;
}
}
return (valueAsString != null
&& atCount == 1
&& valueAsString[0] != '@'
&& valueAsString[valueAsString.Length - 1] != '@');
}
}
private static Regex CreateRegEx() {
// We only need to create the RegEx if this switch is enabled.
if (AppSettings.DisableRegEx) {
return null;
}
const string pattern = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$";
const RegexOptions options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
// Set explicit regex match timeout, sufficient enough for email parsing
// Unless the global REGEX_DEFAULT_MATCH_TIMEOUT is already set
TimeSpan matchTimeout = TimeSpan.FromSeconds(2);
try {
if (AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") == null) {
return new Regex(pattern, options, matchTimeout);
}
}
catch {
// Fallback on error
}
// Legacy fallback (without explicit match timeout)
return new Regex(pattern, options);
}
}
}

View File

@ -0,0 +1,98 @@
using System.ComponentModel.DataAnnotations.Resources;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace System.ComponentModel.DataAnnotations {
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter, AllowMultiple = false)]
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "We want users to be able to extend this class")]
public sealed class EnumDataTypeAttribute : DataTypeAttribute {
public Type EnumType { get; private set; }
public EnumDataTypeAttribute(Type enumType)
: base("Enumeration") {
this.EnumType = enumType;
}
#if !SILVERLIGHT
public
#else
internal
#endif
override bool IsValid(object value) {
if (this.EnumType == null) {
throw new InvalidOperationException(DataAnnotationsResources.EnumDataTypeAttribute_TypeCannotBeNull);
}
if (!this.EnumType.IsEnum) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.EnumDataTypeAttribute_TypeNeedsToBeAnEnum, this.EnumType.FullName));
}
if (value == null) {
return true;
}
string stringValue = value as string;
if (stringValue != null && String.IsNullOrEmpty(stringValue)) {
return true;
}
Type valueType = value.GetType();
if (valueType.IsEnum && this.EnumType != valueType) {
// don't match a different enum that might map to the same underlying integer
//
return false;
}
if (!valueType.IsValueType && valueType != typeof(string)) {
// non-value types cannot be converted
return false;
}
if (valueType == typeof(bool) ||
valueType == typeof(float) ||
valueType == typeof(double) ||
valueType == typeof(decimal) ||
valueType == typeof(char)) {
// non-integral types cannot be converted
return false;
}
object convertedValue;
if (valueType.IsEnum) {
Debug.Assert(valueType == value.GetType(), "The valueType should equal the Type of the value");
convertedValue = value;
} else {
try {
if (stringValue != null) {
convertedValue = Enum.Parse(this.EnumType, stringValue, false);
} else {
convertedValue = Enum.ToObject(this.EnumType, value);
}
} catch (ArgumentException) {
//
return false;
}
}
if (IsEnumTypeInFlagsMode(this.EnumType)) {
//
string underlying = GetUnderlyingTypeValueString(this.EnumType, convertedValue);
string converted = convertedValue.ToString();
return !underlying.Equals(converted);
} else {
return Enum.IsDefined(this.EnumType, convertedValue);
}
}
private static bool IsEnumTypeInFlagsMode(Type enumType) {
return enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Length != 0;
}
private static string GetUnderlyingTypeValueString(Type enumType, object enumValue) {
return Convert.ChangeType(enumValue, Enum.GetUnderlyingType(enumType), CultureInfo.InvariantCulture).ToString();
}
}
}

View File

@ -0,0 +1,79 @@
using System.Diagnostics.CodeAnalysis;
namespace System.ComponentModel.DataAnnotations {
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Resources;
using System.Globalization;
using System.IO;
using System.Linq;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class FileExtensionsAttribute : DataTypeAttribute {
private string _extensions;
public FileExtensionsAttribute()
: base(DataType.Upload) {
// DevDiv 468241: set DefaultErrorMessage not ErrorMessage, allowing user to set
// ErrorMessageResourceType and ErrorMessageResourceName to use localized messages.
DefaultErrorMessage = DataAnnotationsResources.FileExtensionsAttribute_Invalid;
}
public string Extensions {
get {
// Default file extensions match those from jquery validate.
return String.IsNullOrWhiteSpace(_extensions) ? "png,jpg,jpeg,gif" : _extensions;
}
set {
_extensions = value;
}
}
private string ExtensionsFormatted {
get {
return ExtensionsParsed.Aggregate((left, right) => left + ", " + right);
}
}
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "These strings are normalized to lowercase because they are presented to the user in lowercase format")]
private string ExtensionsNormalized {
get {
return Extensions.Replace(" ", "").Replace(".", "").ToLowerInvariant();
}
}
private IEnumerable<string> ExtensionsParsed {
get {
return ExtensionsNormalized.Split(',').Select(e => "." + e);
}
}
public override string FormatErrorMessage(string name) {
return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, ExtensionsFormatted);
}
public override bool IsValid(object value) {
if (value == null) {
return true;
}
string valueAsString = value as string;
if (valueAsString != null) {
return ValidateExtension(valueAsString);
}
return false;
}
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "These strings are normalized to lowercase because they are presented to the user in lowercase format")]
private bool ValidateExtension(string fileName) {
try {
return ExtensionsParsed.Contains(Path.GetExtension(fileName).ToLowerInvariant());
}
catch (ArgumentException) {
return false;
}
}
}
}

View File

@ -0,0 +1,102 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace System.ComponentModel.DataAnnotations {
/// <summary>
/// An attribute used to specify the filtering behavior for a column.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification = "ControlParameters is exposed, just with a different type")]
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class FilterUIHintAttribute : Attribute {
private UIHintAttribute.UIHintImplementation _implementation;
/// <summary>
/// Gets the name of the control that is most appropriate for this associated property or field
/// </summary>
public string FilterUIHint {
get {
return this._implementation.UIHint;
}
}
/// <summary>
/// Gets the name of the presentation layer that supports the control type in <see cref="FilterUIHint"/>
/// </summary>
public string PresentationLayer {
get {
return this._implementation.PresentationLayer;
}
}
/// <summary>
/// Gets the name-value pairs used as parameters to the control's constructor
/// </summary>
/// <exception cref="InvalidOperationException"> is thrown if the current attribute is ill-formed.</exception>
public IDictionary<string, object> ControlParameters {
get {
return this._implementation.ControlParameters;
}
}
#if !SILVERLIGHT
/// <summary>
/// Gets a unique identifier for this attribute.
/// </summary>
public override object TypeId {
get {
return this;
}
}
#endif
/// <summary>
/// Constructor that accepts the name of the control, without specifying which presentation layer to use
/// </summary>
/// <param name="filterUIHint">The name of the UI control.</param>
public FilterUIHintAttribute(string filterUIHint)
: this(filterUIHint, null, new object[0]) {
}
/// <summary>
/// Constructor that accepts both the name of the control as well as the presentation layer
/// </summary>
/// <param name="filterUIHint">The name of the control to use</param>
/// <param name="presentationLayer">The name of the presentation layer that supports this control</param>
public FilterUIHintAttribute(string filterUIHint, string presentationLayer)
: this(filterUIHint, presentationLayer, new object[0]) {
}
/// <summary>
/// Full constructor that accepts the name of the control, presentation layer, and optional parameters
/// to use when constructing the control
/// </summary>
/// <param name="filterUIHint">The name of the control</param>
/// <param name="presentationLayer">The presentation layer</param>
/// <param name="controlParameters">The list of parameters for the control</param>
public FilterUIHintAttribute(string filterUIHint, string presentationLayer, params object[] controlParameters) {
this._implementation = new UIHintAttribute.UIHintImplementation(filterUIHint, presentationLayer, controlParameters);
}
/// <summary>
/// Returns the hash code for this FilterUIHintAttribute.
/// </summary>
/// <returns>A 32-bit signed integer hash code.</returns>
public override int GetHashCode() {
return this._implementation.GetHashCode();
}
/// <summary>
/// Determines whether this instance of FilterUIHintAttribute and a specified object,
/// which must also be a FilterUIHintAttribute object, have the same value.
/// </summary>
/// <param name="obj">An System.Object.</param>
/// <returns>true if obj is a FilterUIHintAttribute and its value is the same as this instance; otherwise, false.</returns>
public override bool Equals(object obj) {
var otherAttribute = obj as FilterUIHintAttribute;
if (otherAttribute == null) {
return false;
}
return this._implementation.Equals(otherAttribute._implementation);
}
}
}

Some files were not shown because too many files have changed in this diff Show More