/// <summary> /// Sets the business rule validation result. /// </summary> /// <param name="validationResult">The business rule validation result.</param> /// <exception cref="ArgumentNullException">The <paramref name="validationResult"/> is <c>null</c>.</exception> protected void SetBusinessRuleValidationResult(IBusinessRuleValidationResult validationResult) { Argument.IsNotNull("validationResult", validationResult); if (string.IsNullOrEmpty(validationResult.Message)) { return; } var previousValidations = _internalValidationContext.GetBusinessRuleValidations(); // First, check if the same error already exists bool alreadyExists = (from previousFieldValidation in previousValidations where string.Compare(previousFieldValidation.Message, validationResult.Message) == 0 select previousFieldValidation).Any(); if (alreadyExists) { return; } _internalValidationContext.AddBusinessRuleValidationResult(validationResult); if (!IsValidating) { switch (validationResult.ValidationResultType) { case ValidationResultType.Warning: NotifyWarningsChanged(string.Empty, true); break; case ValidationResultType.Error: NotifyErrorsChanged(string.Empty, true); break; } } }
/// <summary> /// Validates the current object for field and business rule errors. /// </summary> /// <param name="force">If set to <c>true</c>, a validation is forced (even if the object knows it is already validated).</param> /// <param name="validateDataAnnotations">If set to <c>true</c>, the data annotations will be checked. This value is only used if <paramref name="force"/> is set to <c>true</c>.</param> /// <remarks> /// To check whether this object contains any errors, use the <see cref="INotifyDataErrorInfo.HasErrors"/> property. /// </remarks> internal void Validate(bool force, bool validateDataAnnotations) { if (SuspendValidation) { return; } if (IsValidating) { return; } IsValidating = true; var existingValidationContext = (ValidationContext)_validationContext; bool hasErrors = existingValidationContext.HasErrors; bool hasWarnings = existingValidationContext.HasWarnings; var validationContext = new ValidationContext(); var changes = new List<ValidationContextChange>(); var validator = GetValidator(); if (validator != null) { validator.BeforeValidation(this, existingValidationContext.GetFieldValidations(), existingValidationContext.GetBusinessRuleValidations()); } OnValidating(validationContext); CatchUpWithSuspendedAnnotationsValidation(); if (force && validateDataAnnotations) { var type = GetType(); var ignoredOrFailedPropertyValidations = _propertyValuesIgnoredOrFailedForValidation[type]; // In forced mode, validate all registered properties for annotations var catelTypeInfo = PropertyDataManager.GetCatelTypeInfo(type); foreach (var propertyData in catelTypeInfo.GetCatelProperties()) { var propertyInfo = propertyData.Value.GetPropertyInfo(type); if (propertyInfo == null || !propertyInfo.HasPublicGetter) { // Note: non-public getter, do not validate ignoredOrFailedPropertyValidations.Add(propertyData.Key); continue; } var propertyValue = GetValue(propertyData.Value); ValidatePropertyUsingAnnotations(propertyData.Key, propertyValue, propertyData.Value); } #if !WINDOWS_PHONE && !NETFX_CORE && !PCL && !NET35 // Validate non-catel properties as well for attribute validation foreach (var propertyInfo in catelTypeInfo.GetNonCatelProperties()) { if (_firstAnnotationValidation) { if (propertyInfo.Value.IsDecoratedWithAttribute(typeof(ExcludeFromValidationAttribute))) { ignoredOrFailedPropertyValidations.Add(propertyInfo.Key); } } if (!propertyInfo.Value.HasPublicGetter) { // Note: non-public getter, do not validate ignoredOrFailedPropertyValidations.Add(propertyInfo.Key); continue; } // TODO: Should we check for annotations attributes? if (ignoredOrFailedPropertyValidations.Contains(propertyInfo.Key)) { continue; } try { var propertyValue = propertyInfo.Value.PropertyInfo.GetValue(this, null); ValidatePropertyUsingAnnotations(propertyInfo.Key, propertyValue, null); } catch (Exception ex) { Log.Warning(ex, "Failed to validate property '{0}.{1}', adding it to the ignore list", type.Name, propertyInfo.Key); ignoredOrFailedPropertyValidations.Add(propertyInfo.Key); } } _firstAnnotationValidation = false; #endif } if (!IsValidated || force) { lock (_validationLock) { var fieldValidationResults = new List<IFieldValidationResult>(); var businessRuleValidationResults = new List<IBusinessRuleValidationResult>(); #region Fields if (validator != null) { validator.BeforeValidateFields(this, validationContext.GetFieldValidations()); } OnValidatingFields(validationContext); if (validator != null) { validator.ValidateFields(this, fieldValidationResults); } #if !WINDOWS_PHONE && !NETFX_CORE && !PCL && !NET35 // Support annotation validation fieldValidationResults.AddRange(from fieldAnnotationValidation in _dataAnnotationValidationResults where !string.IsNullOrEmpty(fieldAnnotationValidation.Value) select (IFieldValidationResult)FieldValidationResult.CreateError(fieldAnnotationValidation.Key, fieldAnnotationValidation.Value)); #endif ValidateFields(fieldValidationResults); OnValidatedFields(validationContext); if (validator != null) { validator.AfterValidateFields(this, fieldValidationResults); } // As the last step, sync the field validation results with the context foreach (var fieldValidationResult in fieldValidationResults) { validationContext.AddFieldValidationResult(fieldValidationResult); } #endregion #region Business rules if (validator != null) { validator.BeforeValidateBusinessRules(this, validationContext.GetBusinessRuleValidations()); } OnValidatingBusinessRules(validationContext); if (validator != null) { validator.ValidateBusinessRules(this, businessRuleValidationResults); } ValidateBusinessRules(businessRuleValidationResults); OnValidatedBusinessRules(validationContext); if (validator != null) { validator.AfterValidateBusinessRules(this, businessRuleValidationResults); } // As the last step, sync the field validation results with the context foreach (var businessRuleValidationResult in businessRuleValidationResults) { validationContext.AddBusinessRuleValidationResult(businessRuleValidationResult); } #endregion if (validator != null) { validator.Validate(this, validationContext); } IsValidated = true; // Manual sync to get the changes changes = existingValidationContext.SynchronizeWithContext(validationContext); } } OnValidated(validationContext); if (validator != null) { validator.AfterValidation(this, validationContext.GetFieldValidations(), validationContext.GetBusinessRuleValidations()); } #region Notify changes bool hasNotifiedBusinessWarningsChanged = false; bool hasNotifiedBusinessErrorsChanged = false; foreach (var change in changes) { var changeAsFieldValidationResult = change.ValidationResult as IFieldValidationResult; var changeAsBusinessRuleValidationResult = change.ValidationResult as IBusinessRuleValidationResult; if (changeAsFieldValidationResult != null) { switch (change.ValidationResult.ValidationResultType) { case ValidationResultType.Warning: NotifyWarningsChanged(changeAsFieldValidationResult.PropertyName, false); break; case ValidationResultType.Error: NotifyErrorsChanged(changeAsFieldValidationResult.PropertyName, false); break; default: throw new ArgumentOutOfRangeException(); } } else if (changeAsBusinessRuleValidationResult != null) { switch (change.ValidationResult.ValidationResultType) { case ValidationResultType.Warning: if (!hasNotifiedBusinessWarningsChanged) { hasNotifiedBusinessWarningsChanged = true; NotifyWarningsChanged(string.Empty, false); } break; case ValidationResultType.Error: if (!hasNotifiedBusinessErrorsChanged) { hasNotifiedBusinessErrorsChanged = true; NotifyErrorsChanged(string.Empty, false); } break; default: throw new ArgumentOutOfRangeException(); } } } if (_validationContext.HasWarnings != hasWarnings) { RaisePropertyChanged(HasWarningsMessageProperty); } if (_validationContext.HasErrors != hasErrors) { RaisePropertyChanged(HasErrorsMessageProperty); } #endregion IsValidating = false; }
public void CorrectlySynchronizesWithRemovedBusinessRuleError() { var fieldValidationResults1 = new List<IFieldValidationResult>(); fieldValidationResults1.Add(FieldValidationResult.CreateWarning("MyProperty", "FieldWarning")); fieldValidationResults1.Add(FieldValidationResult.CreateError("MyProperty", "FieldError")); var businessRuleValidationResults1 = new List<IBusinessRuleValidationResult>(); businessRuleValidationResults1.Add(BusinessRuleValidationResult.CreateWarning("BusinessRuleWarning")); businessRuleValidationResults1.Add(BusinessRuleValidationResult.CreateError("BusinessRuleError")); var context1 = new ValidationContext(fieldValidationResults1, businessRuleValidationResults1); var fieldValidationResults2 = new List<IFieldValidationResult>(); fieldValidationResults2.Add(FieldValidationResult.CreateWarning("MyProperty", "FieldWarning")); fieldValidationResults2.Add(FieldValidationResult.CreateError("MyProperty", "FieldError")); var businessRuleValidationResults2 = new List<IBusinessRuleValidationResult>(); businessRuleValidationResults2.Add(BusinessRuleValidationResult.CreateWarning("BusinessRuleWarning")); var context2 = new ValidationContext(fieldValidationResults2, businessRuleValidationResults2); context1.SynchronizeWithContext(context2); // First context should now equal second context int counter; var fieldValidations = context1.GetFieldValidations(); Assert.AreEqual(2, fieldValidations.Count); counter = -1; counter++; Assert.AreEqual("MyProperty", fieldValidations[counter].PropertyName); Assert.AreEqual("FieldWarning", fieldValidations[counter].Message); Assert.AreEqual(ValidationResultType.Warning, fieldValidations[counter].ValidationResultType); counter++; Assert.AreEqual("MyProperty", fieldValidations[counter].PropertyName); Assert.AreEqual("FieldError", fieldValidations[counter].Message); Assert.AreEqual(ValidationResultType.Error, fieldValidations[counter].ValidationResultType); var businessRuleValidations = context1.GetBusinessRuleValidations(); Assert.AreEqual(1, businessRuleValidations.Count); counter = -1; counter++; Assert.AreEqual("BusinessRuleWarning", businessRuleValidations[counter].Message); Assert.AreEqual(ValidationResultType.Warning, businessRuleValidations[counter].ValidationResultType); }
public void ReturnsRightValidationsForEmptyContextWithTag() { var context = new ValidationContext(); var businessRuleValidations = context.GetBusinessRuleValidations("tag"); Assert.AreEqual(0, businessRuleValidations.Count); }
/// <summary> /// Validates the current object for field and business rule errors. /// </summary> /// <param name="force">If set to <c>true</c>, a validation is forced (even if the object knows it is already validated).</param> /// <param name="validateDataAnnotations">If set to <c>true</c>, the data annotations will be checked. This value is only used if <paramref name="force"/> is set to <c>true</c>.</param> /// <remarks> /// To check whether this object contains any errors, use the ValidationContext property. /// </remarks> internal void Validate(bool force, bool validateDataAnnotations) { if (IsValidating) { return; } if (IsValidationSuspended) { return; } IsValidating = true; var existingValidationContext = _validationContext; if (existingValidationContext == null) { existingValidationContext = new ValidationContext(); } var hasErrors = existingValidationContext.HasErrors; var hasWarnings = existingValidationContext.HasWarnings; var validationContext = new ValidationContext(); var changes = new List <ValidationContextChange>(); var validator = GetValidator(); if (validator != null) { validator.BeforeValidation(this, existingValidationContext.GetFieldValidations(), existingValidationContext.GetBusinessRuleValidations()); } OnValidating(validationContext); if (force && validateDataAnnotations) { var type = GetType(); var ignoredOrFailedPropertyValidations = PropertiesNotCausingValidation[type]; // In forced mode, validate all registered properties for annotations var catelTypeInfo = PropertyDataManager.GetCatelTypeInfo(type); foreach (var propertyData in catelTypeInfo.GetCatelProperties()) { if (propertyData.Value.IsModelBaseProperty) { continue; } var propertyInfo = propertyData.Value.GetPropertyInfo(type); if (propertyInfo == null || !propertyInfo.HasPublicGetter) { // Note: non-public getter, do not validate ignoredOrFailedPropertyValidations.Add(propertyData.Key); continue; } ValidatePropertyUsingAnnotations(propertyData.Key); } #if !NETFX_CORE && !PCL // Validate non-catel properties as well for attribute validation foreach (var propertyInfo in catelTypeInfo.GetNonCatelProperties()) { if (_firstAnnotationValidation) { if (propertyInfo.Value.IsDecoratedWithAttribute <ExcludeFromValidationAttribute>()) { ignoredOrFailedPropertyValidations.Add(propertyInfo.Key); } } if (!propertyInfo.Value.HasPublicGetter) { // Note: non-public getter, do not validate ignoredOrFailedPropertyValidations.Add(propertyInfo.Key); continue; } // TODO: Should we check for annotations attributes? if (ignoredOrFailedPropertyValidations.Contains(propertyInfo.Key)) { continue; } try { ValidatePropertyUsingAnnotations(propertyInfo.Key); } catch (Exception ex) { Log.Warning(ex, "Failed to validate property '{0}.{1}', adding it to the ignore list", type.Name, propertyInfo.Key); ignoredOrFailedPropertyValidations.Add(propertyInfo.Key); } } _firstAnnotationValidation = false; #endif } if (!_isValidated || force) { lock (_validationContext) { var fieldValidationResults = new List <IFieldValidationResult>(); var businessRuleValidationResults = new List <IBusinessRuleValidationResult>(); #region Fields if (validator != null) { validator.BeforeValidateFields(this, validationContext.GetFieldValidations()); } OnValidatingFields(validationContext); if (validator != null) { validator.ValidateFields(this, fieldValidationResults); } #if !NETFX_CORE && !PCL // Support annotation validation fieldValidationResults.AddRange(from fieldAnnotationValidation in _dataAnnotationValidationResults where !string.IsNullOrEmpty(fieldAnnotationValidation.Value) select(IFieldValidationResult) FieldValidationResult.CreateError(fieldAnnotationValidation.Key, fieldAnnotationValidation.Value)); #endif ValidateFields(fieldValidationResults); OnValidatedFields(validationContext); if (validator != null) { validator.AfterValidateFields(this, fieldValidationResults); } // As the last step, sync the field validation results with the context foreach (var fieldValidationResult in fieldValidationResults) { validationContext.AddFieldValidationResult(fieldValidationResult); } #endregion #region Business rules if (validator != null) { validator.BeforeValidateBusinessRules(this, validationContext.GetBusinessRuleValidations()); } OnValidatingBusinessRules(validationContext); if (validator != null) { validator.ValidateBusinessRules(this, businessRuleValidationResults); } ValidateBusinessRules(businessRuleValidationResults); OnValidatedBusinessRules(validationContext); if (validator != null) { validator.AfterValidateBusinessRules(this, businessRuleValidationResults); } // As the last step, sync the field validation results with the context foreach (var businessRuleValidationResult in businessRuleValidationResults) { validationContext.AddBusinessRuleValidationResult(businessRuleValidationResult); } #endregion if (validator != null) { validator.Validate(this, validationContext); } _isValidated = true; // Manual sync to get the changes changes = existingValidationContext.SynchronizeWithContext(validationContext); } } OnValidated(validationContext); if (validator != null) { validator.AfterValidation(this, validationContext.GetFieldValidations(), validationContext.GetBusinessRuleValidations()); } #region Notify changes var hasNotifiedBusinessWarningsChanged = false; var hasNotifiedBusinessErrorsChanged = false; foreach (var change in changes) { var changeAsFieldValidationResult = change.ValidationResult as IFieldValidationResult; var changeAsBusinessRuleValidationResult = change.ValidationResult as IBusinessRuleValidationResult; if (changeAsFieldValidationResult != null) { switch (change.ValidationResult.ValidationResultType) { case ValidationResultType.Warning: NotifyWarningsChanged(changeAsFieldValidationResult.PropertyName, false); break; case ValidationResultType.Error: NotifyErrorsChanged(changeAsFieldValidationResult.PropertyName, false); break; default: throw new ArgumentOutOfRangeException(); } } else if (changeAsBusinessRuleValidationResult != null) { switch (change.ValidationResult.ValidationResultType) { case ValidationResultType.Warning: if (!hasNotifiedBusinessWarningsChanged) { hasNotifiedBusinessWarningsChanged = true; NotifyWarningsChanged(string.Empty, false); } break; case ValidationResultType.Error: if (!hasNotifiedBusinessErrorsChanged) { hasNotifiedBusinessErrorsChanged = true; NotifyErrorsChanged(string.Empty, false); } break; default: throw new ArgumentOutOfRangeException(); } } } if (_validationContext.HasWarnings != hasWarnings) { RaisePropertyChanged(this, new PropertyChangedEventArgs(nameof(HasWarnings)), false, false); RaisePropertyChanged(this, new PropertyChangedEventArgs(HasWarningsMessageProperty), false, false); } if (_validationContext.HasErrors != hasErrors) { RaisePropertyChanged(this, new PropertyChangedEventArgs(nameof(HasErrors)), false, false); RaisePropertyChanged(this, new PropertyChangedEventArgs(HasErrorsMessageProperty), false, false); } #endregion IsValidating = false; }