/// <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;
        }
Exemple #3
0
        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;
        }
Exemple #16
0
 /// <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);