/// <summary> /// This method recursively validates an object, first validating all properties, then /// validating the type. This method implements the classic Try pattern. However it serves /// as code sharing for the validation pattern where an exception is thrown on first error. /// </summary> /// <param name="instance">The object to validate.</param> /// <param name="memberPath">The dotted path of the member.</param> /// <param name="validationContext">The validation context.</param> /// <param name="validationResults">The collection in which the validation results will be /// stored. The collection can be <c>null</c>.</param> /// <returns><c>True</c> if the object was successfully validated with no errors.</returns> /// <exception cref="ValidationException">When <paramref name="validationResults"/> is /// <c>null</c> and the object has a validation error.</exception> private static bool ValidateObjectRecursive(object instance, string memberPath, ValidationContext validationContext, List <ValidationResult> validationResults) { MetaType metaType = MetaType.GetMetaType(instance.GetType()); if (!metaType.RequiresValidation) { return(true); } // First validate all properties bool hasValidationErrors = false; foreach (MetaMember metaMember in metaType.Members.Where(m => m.RequiresValidation || m.IsComplex)) { ValidationContext propertyValidationContext = ValidationUtilities.CreateValidationContext(instance, validationContext); propertyValidationContext.MemberName = metaMember.Member.Name; // Form the current member path, appending the current // member name if it is complex. string currMemberPath = memberPath; if (metaMember.IsComplex) { if (currMemberPath.Length > 0) { currMemberPath += "."; } currMemberPath += metaMember.Member.Name; } object value = metaMember.GetValue(instance); // first validate the property itself if (metaMember.RequiresValidation) { hasValidationErrors |= !ValidationUtilities.ValidateProperty(value, propertyValidationContext, validationResults, currMemberPath); } // for complex members, in addition to property level validation we need to // do deep validation recursively if (value != null && metaMember.IsComplex) { if (!metaMember.IsCollection) { hasValidationErrors |= !ValidateObjectRecursive(value, currMemberPath, validationContext, validationResults); } else { hasValidationErrors |= !ValidateComplexCollection((IEnumerable)value, currMemberPath, validationContext, validationResults); } } } // Only proceed to Type level validation if there are no property validation errors if (hasValidationErrors) { return(false); } // Next perform Type level validation without validating properties, since we've already validated all properties. // Note that we can't use Validator.ValidateObject specifying 'validateAllProperties' since even when specifying false, // that API will validate RequiredAttribute. ValidationContext context = ValidationUtilities.CreateValidationContext(instance, validationContext); if (metaType.ValidationAttributes.Any()) { hasValidationErrors |= !ValidationUtilities.ValidateValue(instance, context, validationResults, metaType.ValidationAttributes, memberPath); } #if !SILVERLIGHT // Only proceed to IValidatableObject validation if there are no errors if (hasValidationErrors) { return(false); } // Test for IValidatableObject implementation and run the validation if applicable // Note : this interface doesn't exist in Silverlight IValidatableObject validatable = instance as IValidatableObject; if (validatable != null) { IEnumerable <ValidationResult> results = validatable.Validate(context); if (!string.IsNullOrEmpty(memberPath)) { results = ValidationUtilities.ApplyMemberPath(results, memberPath); } foreach (ValidationResult result in results.Where(r => r != ValidationResult.Success)) { validationResults.Add(result); hasValidationErrors = true; } } #endif return(!hasValidationErrors); }