You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@ -0,0 +1,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
@ -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
|
@ -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(',');
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
Reference in New Issue
Block a user