using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; namespace System.ComponentModel.DataAnnotations { /// /// Describes the context in which a validation is being performed. /// /// /// This class contains information describing the instance on which /// validation is being performed. /// /// It supports so that custom validation /// code can acquire additional services to help it perform its validation. /// /// /// An property bag is available for additional contextual /// information about the validation. Values stored in /// will be available to validation methods that use this /// /// [SuppressMessage("Microsoft.Usage", "CA2302:FlagServiceProviders", Justification = "The actual IServiceProvider implementation lies with the underlying service provider.")] public sealed class ValidationContext : IServiceProvider { #region Member Fields private Func _serviceProvider; private object _objectInstance; private string _memberName; private string _displayName; private Dictionary _items; #endregion #region Constructors /// /// Construct a for a given object instance being validated. /// /// The object instance being validated. It cannot be null. /// When is null public ValidationContext(object instance) : this(instance, null, null) { } /// /// Construct a for a given object instance and an optional /// property bag of . /// /// The object instance being validated. It cannot be null. /// Optional set of key/value pairs to make available to consumers via . /// If null, an empty dictionary will be created. If not null, the set of key/value pairs will be copied into a /// new dictionary, preventing consumers from modifying the original dictionary. /// /// When is null public ValidationContext(object instance, IDictionary items) : this(instance, null, items) { } /// /// Construct a for a given object instance, an optional , and an optional /// property bag of . /// /// The object instance being validated. It cannot be null. /// /// Optional to use when is called. /// If it is null, will always return null. /// /// Optional set of key/value pairs to make available to consumers via . /// If null, an empty dictionary will be created. If not null, the set of key/value pairs will be copied into a /// new dictionary, preventing consumers from modifying the original dictionary. /// /// When is null public ValidationContext(object instance, IServiceProvider serviceProvider, IDictionary items) { if (instance == null) { throw new ArgumentNullException("instance"); } if (serviceProvider != null) { this.InitializeServiceProvider(serviceType => serviceProvider.GetService(serviceType)); } if (items != null) { this._items = new Dictionary(items); } else { this._items = new Dictionary(); } this._objectInstance = instance; } #endregion #region Properties /// /// Gets the object instance being validated. While it will not be null, the state of the instance is indeterminate /// as it might only be partially initialized during validation. /// Consume this instance with caution! /// /// /// During validation, especially property-level validation, the object instance might be in an indeterminate state. /// For example, the property being validated, as well as other properties on the instance might not have been /// updated to their new values. /// public object ObjectInstance { get { return this._objectInstance; } } /// /// Gets the type of the object being validated. It will not be null. /// public Type ObjectType { get { return this.ObjectInstance.GetType(); } } /// /// Gets or sets the user-visible name of the type or property being validated. /// /// If this name was not explicitly set, this property will consult an associated /// to see if can use that instead. Lacking that, it returns . The /// type name will be used if MemberName has not been set. /// public string DisplayName { get { if (string.IsNullOrEmpty(this._displayName)) { this._displayName = this.GetDisplayName(); if (string.IsNullOrEmpty(this._displayName)) { this._displayName = this.MemberName; if (string.IsNullOrEmpty(this._displayName)) { this._displayName = this.ObjectType.Name; } } } return this._displayName; } set { if (string.IsNullOrEmpty(value)) { throw new ArgumentNullException("value"); } this._displayName = value; } } /// /// Gets or sets the name of the type or property being validated. /// /// This name reflects the API name of the member being validated, not a localized name. It should be set /// only for property or parameter contexts. public string MemberName { get { return this._memberName; } set { this._memberName = value; } } /// /// Gets the dictionary of key/value pairs associated with this context. /// /// This property will never be null, but the dictionary may be empty. Changes made /// to items in this dictionary will never affect the original dictionary specified in the constructor. public IDictionary Items { get { return this._items; } } #endregion #region Methods /// /// Looks up the display name using the DisplayAttribute attached to the respective type or property. /// /// A display-friendly name of the member represented by the . private string GetDisplayName() { string displayName = null; ValidationAttributeStore store = ValidationAttributeStore.Instance; DisplayAttribute displayAttribute = null; if (string.IsNullOrEmpty(this._memberName)) { displayAttribute = store.GetTypeDisplayAttribute(this); } else if (store.IsPropertyContext(this)) { displayAttribute = store.GetPropertyDisplayAttribute(this); } if (displayAttribute != null) { displayName = displayAttribute.GetName(); } return displayName ?? this.MemberName; } /// /// Initializes the with a service provider that can return /// service instances by when is called. /// /// /// A that can return service instances given the /// desired when is called. /// If it is null, will always return null. /// public void InitializeServiceProvider(Func serviceProvider) { this._serviceProvider = serviceProvider; } #endregion #region IServiceProvider Members /// /// See . /// /// The type of the service needed. /// An instance of that service or null if it is not available. public object GetService(Type serviceType) { object service = null; if (service == null && this._serviceProvider != null) { service = this._serviceProvider(serviceType); } return service; } #endregion } }