/// <summary> /// Gets the validators for the model using the metadata, the validator providers, and a list of attributes. /// </summary> /// <param name="metadata">The metadata.</param> /// <param name="validatorProviders">An enumeration of validator providers.</param> /// <param name="attributes">The list of attributes.</param> /// <returns>The validators for the model.</returns> protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<ModelValidatorProvider> validatorProviders, IEnumerable<Attribute> attributes) { if (metadata == null) { throw Error.ArgumentNull("metadata"); } List<ModelValidator> results = new List<ModelValidator>(); // Produce a validator for each validation attribute we find foreach (ValidationAttribute attribute in attributes.OfType<ValidationAttribute>()) { DataAnnotationsModelValidationFactory factory; if (!this.attributeFactories.TryGetValue(attribute.GetType(), out factory)) { factory = this.defaultAttributeFactory; } results.Add(factory(validatorProviders, attribute)); } // Produce a validator if the type supports IValidatableObject if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType)) { DataAnnotationsValidatableObjectAdapterFactory factory; if (!this.validatableFactories.TryGetValue(metadata.ModelType, out factory)) { factory = this.defaultValidatableFactory; } results.Add(factory(validatorProviders)); } return results; }
/// <summary> /// Validates a specified object. /// </summary> /// <param name="metadata">The metadata.</param> /// <param name="container">The container.</param> /// <returns>A list of validation results.</returns> public override IEnumerable<ModelValidationResult> Validate(ModelMetadata metadata, object container) { if (metadata == null) { throw Error.ArgumentNull("metadata"); } string memberName = metadata.GetDisplayName(); ValidationContext context = new ValidationContext(container ?? metadata.Model) { DisplayName = memberName, MemberName = memberName }; ValidationResult result = this.Attribute.GetValidationResult(metadata.Model, context); if (result != ValidationResult.Success) { string errorMemberName = result.MemberNames.FirstOrDefault(); if (string.Equals(errorMemberName, memberName, StringComparison.Ordinal)) { errorMemberName = null; } var validationResult = new ModelValidationResult { Message = result.ErrorMessage, MemberName = errorMemberName }; return new[] { validationResult }; } return EmptyResult; }
public IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata) { // If metadata is for a property then containerType != null && propertyName != null // If metadata is for a type then containerType == null && propertyName == null, so we have to use modelType for the cache key. Type typeForCache = metadata.ContainerType ?? metadata.ModelType; Tuple<Type, string> cacheKey = Tuple.Create(typeForCache, metadata.PropertyName); ModelValidator[] validators; // Try to find validator into fast cache first if (!this.fastCache.TryGetValue(cacheKey, out validators)) { if (!this.validatorCache.TryGetValue(cacheKey, out validators)) { // Compute validators // There are no side-effects if the same validators are created more than once validators = metadata.GetValidators(this.validatorProviders.Value).ToArray(); this.validatorCache.TryAdd(cacheKey, validators); } // Fastcache is out of sync. Resync it. // Resync could miss some addition to the validatorCache but it will be OK after a few iterations. // Works well if there is a lot of read operations Interlocked.Exchange(ref this.fastCache, new Dictionary<Tuple<Type, string>, ModelValidator[]>(this.validatorCache)); } return validators; }
private bool ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, object container) { Contract.Requires(metadata != null); Contract.Requires(validationContext != null); object model; try { model = metadata.Model; } catch { // Retrieving the model failed - typically caused by a property getter throwing // Being unable to retrieve a property is not a validation error - many properties can only be retrieved if certain conditions are met // For example, Uri.AbsoluteUri throws for relative URIs but it shouldn't be considered a validation error return true; } bool isValid; // Optimization: we don't need to recursively traverse the graph for null and primitive types if (model == null || TypeHelper.IsSimpleType(model.GetType())) { return ShallowValidate(metadata, validationContext, container); } // Check to avoid infinite recursion. This can happen with cycles in an object graph. if (validationContext.Visited.Contains(model)) { return true; } validationContext.Visited.Add(model); // Validate the children first - depth-first traversal IEnumerable enumerableModel = model as IEnumerable; if (enumerableModel == null) { isValid = this.ValidateProperties(metadata, validationContext); } else { isValid = this.ValidateElements(enumerableModel, validationContext); } if (isValid) { // Don't bother to validate this node if children failed. isValid = ShallowValidate(metadata, validationContext, container); } // Pop the object so that it can be validated again in a different path validationContext.Visited.Remove(model); return isValid; }
private void VisitNodeAndChildren(ModelMetadata metadata, VisitContext visitContext) { Contract.Requires(metadata != null); Contract.Requires(visitContext != null); // Do not traverse the model if caching must be ignored if (metadata.IgnoreCaching) { return; } object model; try { model = metadata.Model; } catch { // Retrieving the model failed - typically caused by a property getter throwing // Being unable to retrieve a property is not an error - many properties can only be retrieved if certain conditions are met // For example, Uri.AbsoluteUri throws for relative URIs but it shouldn't be considered a validation error return; } // Optimization: we don't need to recursively traverse the graph for null and primitive types if (model == null || TypeHelper.IsSimpleType(model.GetType())) { ShallowVisit(metadata, visitContext); return; } // Check to avoid infinite recursion. This can happen with cycles in an object graph. if (visitContext.Visited.Contains(model)) { return; } visitContext.Visited.Add(model); // Visit the children first - depth-first traversal IEnumerable enumerableModel = model as IEnumerable; if (enumerableModel == null) { this.VisitProperties(metadata, visitContext); } else { this.VisitElements(enumerableModel, visitContext); } ShallowVisit(metadata, visitContext); // Pop the object so that it can be visited again in a different path visitContext.Visited.Remove(model); }
private IEnumerable<ModelValidator> GetValidatorsForProperty(ModelMetadata metadata, IEnumerable<ModelValidatorProvider> validatorProviders) { Contract.Requires(metadata != null); ICustomTypeDescriptor typeDescriptor = this.GetTypeDescriptor(metadata.ContainerType); PropertyDescriptor property = typeDescriptor.GetProperties().Find(metadata.PropertyName, true); if (property == null) { throw Error.Argument("metadata", Resources.Common_PropertyNotFound, metadata.ContainerType, metadata.PropertyName); } return this.GetValidators(metadata, validatorProviders, property.Attributes.OfType<Attribute>()); }
/// <summary> /// Gets the validators for the model using the metadata and validator providers. /// </summary> /// <param name="metadata">The metadata.</param> /// <param name="validatorProviders">An enumeration of validator providers.</param> /// <returns>The validators for the model.</returns> public sealed override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<ModelValidatorProvider> validatorProviders) { if (metadata == null) { throw Error.ArgumentNull("metadata"); } if (validatorProviders == null) { throw Error.ArgumentNull("validatorProviders"); } if (metadata.ContainerType != null && !string.IsNullOrEmpty(metadata.PropertyName)) { return this.GetValidatorsForProperty(metadata, validatorProviders); } return this.GetValidatorsForType(metadata, validatorProviders); }
/// <summary> /// Validates a specified object. /// </summary> /// <param name="metadata">The metadata.</param> /// <param name="container">The container.</param> /// <returns>A list of validation results.</returns> public override IEnumerable<ModelValidationResult> Validate(ModelMetadata metadata, object container) { if (metadata == null) { throw Error.ArgumentNull("metadata"); } // Container is never used here, because IValidatableObject doesn't give you // any way to get access to your container. object model = metadata.Model; if (model == null) { return Enumerable.Empty<ModelValidationResult>(); } IValidatableObject validatable = model as IValidatableObject; if (validatable == null) { throw Error.InvalidOperation(Resources.ValidatableObjectAdapter_IncompatibleType, model.GetType()); } ValidationContext validationContext = new ValidationContext(validatable, null, null); return this.ConvertResults(validatable.Validate(validationContext)); }
// Visits a single node (not including children) private static void ShallowVisit(ModelMetadata metadata, VisitContext visitContext) { Contract.Requires(metadata != null); Contract.Requires(visitContext != null); Contract.Requires(visitContext.KeyBuilders != null); string key = visitContext.RootPrefix; foreach (IKeyBuilder keyBuilder in visitContext.KeyBuilders.Reverse()) { key = keyBuilder.AppendTo(key); } if (!metadata.IsComplexType) { visitContext.FlatCommand.Add(key, metadata.Model); } }
private void VisitProperties(ModelMetadata metadata, VisitContext visitContext) { Contract.Requires(metadata != null); Contract.Requires(visitContext != null); PropertyScope propertyScope = new PropertyScope(); visitContext.KeyBuilders.Push(propertyScope); foreach (ModelMetadata childMetadata in metadata.Properties) { propertyScope.PropertyName = childMetadata.PropertyName; this.VisitNodeAndChildren(childMetadata, visitContext); } visitContext.KeyBuilders.Pop(); }
private IEnumerable<ModelValidator> GetValidatorsForType(ModelMetadata metadata, IEnumerable<ModelValidatorProvider> validatorProviders) { Contract.Requires(metadata != null); return this.GetValidators(metadata, validatorProviders, this.GetTypeDescriptor(metadata.ModelType).GetAttributes().Cast<Attribute>()); }
/// <summary> /// Gets the validators for the model using the metadata, the validator providers, and a list of attributes. /// </summary> /// <param name="metadata">The metadata.</param> /// <param name="validatorProviders">An enumeration of validator providers.</param> /// <param name="attributes">The list of attributes.</param> /// <returns>The validators for the model.</returns> protected abstract IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<ModelValidatorProvider> validatorProviders, IEnumerable<Attribute> attributes);
/// <summary> /// Gets a list of validators associated with this ModelValidatorProvider. /// </summary> /// <param name="metadata">The metadata.</param> /// <param name="validatorProviders">The validator providers.</param> /// <returns>The list of validators.</returns> public abstract IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<ModelValidatorProvider> validatorProviders);
// Validates a single node (not including children) // Returns true if validation passes successfully private static bool ShallowValidate(ModelMetadata metadata, ValidationContext validationContext, object container) { Contract.Requires(validationContext != null); bool isValid = true; string key = null; IModelValidatorCache validatorCache = validationContext.ValidatorCache; ModelValidator[] validators; if (validatorCache == null) { // slow path: there is no validator cache on the configuration validators = metadata.GetValidators(validationContext.ValidatorProviders).AsArray(); } else { validators = validatorCache.GetValidators(metadata).AsArray(); } for (int index = 0; index < validators.Length; index++) { ModelValidator validator = validators[index]; foreach (ModelValidationResult error in validator.Validate(metadata, container)) { if (key == null) { key = validationContext.RootPrefix; foreach (IKeyBuilder keyBuilder in validationContext.KeyBuilders.Reverse()) { key = keyBuilder.AppendTo(key); } } validationContext.ModelState.AddModelError(key, error.Message); isValid = false; } } return isValid; }
private bool ValidateProperties(ModelMetadata metadata, ValidationContext validationContext) { Contract.Requires(validationContext != null); Contract.Requires(metadata != null); bool isValid = true; PropertyScope propertyScope = new PropertyScope(); validationContext.KeyBuilders.Push(propertyScope); foreach (ModelMetadata childMetadata in validationContext.MetadataProvider.GetMetadataForProperties(metadata.Model, metadata.RealModelType)) { propertyScope.PropertyName = childMetadata.PropertyName; if (!this.ValidateNodeAndChildren(childMetadata, validationContext, metadata.Model)) { isValid = false; } } validationContext.KeyBuilders.Pop(); return isValid; }
/// <summary> /// Validates a specified object. /// </summary> /// <param name="metadata">The metadata.</param> /// <param name="container">The container.</param> /// <returns>A list of validation results.</returns> public abstract IEnumerable<ModelValidationResult> Validate(ModelMetadata metadata, object container);