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