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