/// <summary> /// Sets a validation message for a whole entity. /// </summary> /// <param name="message">Validation message.</param> /// <returns><c>true</c>, if the message has been added. <c>false</c>, if it was already there.</returns> private bool SetEntityMessage(ValidationMessage message) { Guard.AssertNotNull(message, "message"); return this.SetPropertyGroupMessage(GenericEntityValidationKey, message); }
/// <summary> /// Adds validation message for a property to a certain dictionary. /// </summary> /// <param name="dictionary">The dictionary, where the message should be added.</param> /// <param name="propertyName">The property name.</param> /// <param name="message">The validation message to add.</param> /// <param name="messageKind">The validation message kind (property, entity).</param> private void AddPropertyValidationMessage( Dictionary<string, List<ValidationMessage>> dictionary, string propertyName, ValidationMessage message, ValidationMessageKind messageKind) { Guard.AssertNotNull(dictionary, "dictionary"); Guard.AssertNotNull(message, "message"); // if the property is already in the dictionary, just add the new message; else, add a new key/value pair if (dictionary.ContainsKey(propertyName)) { dictionary[propertyName].Add(new ValidationMessage(message, messageKind)); } else { dictionary.Add(propertyName, new List<ValidationMessage> { new ValidationMessage(message, messageKind) }); } }
/// <summary> /// Updates the messages collection of the property. /// </summary> /// <param name="propertyName"> The name of the property. </param> /// <param name="newPropertyMessage"> The new property message. </param> /// <param name="replaceMessages"> Indicates if old messages will be replaced. </param> /// <returns> True if the property messages have changed. Otherwise, false. </returns> private bool SetPropertyMessage(string propertyName, ValidationMessage newPropertyMessage, bool replaceMessages) { Guard.AssertNotNull(newPropertyMessage, "newPropertyMessage"); return this.SetPropertyMessages(propertyName, new List<ValidationMessage> { newPropertyMessage }, replaceMessages); }
/// <summary> /// Updates the messages collection of the property group. /// </summary> /// <param name="propertyGroup"> The property group validation attribute instance. </param> /// <param name="newMessage"> The new message to set for this group. </param> /// <returns> True if the property group message has changed. Otherwise, false. </returns> private bool SetPropertyGroupMessage(IGroupValidationAttribute propertyGroup, ValidationMessage newMessage) { Guard.AssertNotNull(propertyGroup, "propertyGroup"); lock (this.groupPropertyMessages) { var messageChanged = false; // If the property does not have a message, simply add them if (!this.groupPropertyMessages.ContainsKey(propertyGroup)) { if (newMessage != null) { this.groupPropertyMessages.Add(propertyGroup, newMessage); messageChanged = true; } } else { // If the group has a message, check if the message differs. if (this.groupPropertyMessages[propertyGroup] != newMessage) { if (newMessage != null) { this.groupPropertyMessages[propertyGroup] = newMessage; } else { this.groupPropertyMessages.Remove(propertyGroup); } messageChanged = true; } } return messageChanged; } }
/// <summary> /// Updates the messages collection of the property. /// </summary> /// <param name="propertyName"> The name of the property. </param> /// <param name="newPropertyMessage"> The new property message. </param> /// <returns> True if the property messages have changed. Otherwise, false. </returns> private bool SetPropertyMessage(string propertyName, ValidationMessage newPropertyMessage) { return this.SetPropertyMessage(propertyName, newPropertyMessage, true); }
/// <summary> /// Core logic for validating a property, adding the results to the <paramref name="singlePropertyMatchMessages"/> /// and <paramref name="groupPropertyMatchMessages"/> lists. /// </summary> /// <param name="propertyInfo"> The <see cref="PropertyInfo"/> of the property to validate. </param> /// <param name="singlePropertyMatchMessages"> A list containing the current messages of the property. </param> /// <param name="groupPropertyMatchMessages"> A dictionary containing the current messages of the properties groups. </param> /// <param name="validationContextDictionary"> The validation context dictionary which is used to keep state between different validation rules. </param> /// <param name="includeAllValidationAttributes"> Indicates whether attributes that should not have been used in setter validation will be validated as well. </param> /// <returns><c>true</c> if the property is valid. Otherwise, <c>false</c>.</returns> private async Task<bool> TryValidatePropertyAsync( PropertyInfo propertyInfo, List<ValidationMessage> singlePropertyMatchMessages, IDictionary<IGroupValidationAttribute, ValidationMessage> groupPropertyMatchMessages, Dictionary<object, object> validationContextDictionary, bool includeAllValidationAttributes) { Guard.AssertNotNull(propertyInfo, "propertyInfo"); Guard.AssertNotNull(singlePropertyMatchMessages, "singlePropertyMatchMessages"); Guard.AssertNotNull(groupPropertyMatchMessages, "groupPropertyMatchMessages"); var context = new ValidationContext(this.entityToValidate, validationContextDictionary ?? new Dictionary<object, object>()); context.MemberName = propertyInfo.Name; // gather all validation attributes that have to be evaluated var propertyValue = propertyInfo.GetValue(this.entityToValidate); var validationAttributes = propertyInfo.GetCustomAttributes(typeof(ValidationAttribute)) .Cast<ValidationAttribute>() .ToArray(); var asyncValidationAttributes = propertyInfo.GetCustomAttributes(typeof(AsyncValidationAttribute)) .Cast<AsyncValidationAttribute>() .Where(att => includeAllValidationAttributes || att.UseInImplicitValidation) .ToArray(); var groupValidationAttributes = this.entityToValidate.GetType() .GetTypeInfo() .GetCustomAttributes() .OfType<IGroupValidationAttribute>() .Where(group => group.CausativePropertyNames.Contains(propertyInfo.Name)) .OfType<GroupValidationAttribute>() .Where(att => includeAllValidationAttributes || att.UseInImplicitValidation) .Distinct() .ToArray(); var asyncGroupValidationAttributes = this.entityToValidate.GetType() .GetTypeInfo() .GetCustomAttributes() .OfType<IGroupValidationAttribute>() .Where(group => group.CausativePropertyNames.Contains(propertyInfo.Name)) .OfType<AsyncValidationAttribute>() .Where(att => includeAllValidationAttributes || att.UseInImplicitValidation) .Distinct() .ToArray(); // Validate the property var isValid = true; foreach (var validationAttribute in validationAttributes) { var validationResult = validationAttribute.GetValidationResult(propertyValue, context); if (validationResult != ValidationResult.Success) { ValidationMessage validationMessage = null; var extValidationAttribute = validationAttribute as ExtendedValidationAttribute; if (extValidationAttribute != null) { validationMessage = new ValidationMessage( extValidationAttribute.ValidationLevel, validationResult.ErrorMessage, extValidationAttribute.ShowMessageOnProperty, extValidationAttribute.ShowMessageInSummary); } else { validationMessage = new ValidationMessage(ValidationLevel.Error, validationResult.ErrorMessage); } singlePropertyMatchMessages.Add(validationMessage); isValid = false; } } var isAsyncValid = true; foreach (var asyncValidationAttribute in asyncValidationAttributes) { var validationResult = await asyncValidationAttribute.GetValidationResultAsync(propertyValue, context); if (validationResult != ValidationResult.Success) { singlePropertyMatchMessages.Add(new ValidationMessage(asyncValidationAttribute.ValidationLevel, validationResult.ErrorMessage, asyncValidationAttribute.ShowMessageOnProperty, asyncValidationAttribute.ShowMessageInSummary)); isAsyncValid = false; } } // Validate the property groups var isGroupValid = true; foreach (var groupValidationAttribute in groupValidationAttributes) { // Skip attribute if already checked in higher level validation if (!groupPropertyMatchMessages.ContainsKey(groupValidationAttribute)) { var validationResult = groupValidationAttribute.GetValidationResult(this.entityToValidate, context); if (validationResult != ValidationResult.Success) { groupPropertyMatchMessages[groupValidationAttribute] = new ValidationMessage( groupValidationAttribute.ValidationLevel, validationResult.ErrorMessage, groupValidationAttribute.ShowMessageOnProperty, groupValidationAttribute.ShowMessageInSummary); isGroupValid = false; } else { // must be always set to be reset if required groupPropertyMatchMessages[groupValidationAttribute] = null; } } } var isAsyncGroupValid = true; foreach (var asyncGroupValidationAttribute in asyncGroupValidationAttributes) { var castedGroupAttribute = asyncGroupValidationAttribute as IGroupValidationAttribute; // Skip attribute if already checked in higher level validation if (!groupPropertyMatchMessages.ContainsKey(castedGroupAttribute)) { var validationResult = await asyncGroupValidationAttribute.GetValidationResultAsync(this.entityToValidate, context); if (validationResult != ValidationResult.Success) { groupPropertyMatchMessages[castedGroupAttribute] = new ValidationMessage( asyncGroupValidationAttribute.ValidationLevel, validationResult.ErrorMessage, asyncGroupValidationAttribute.ShowMessageOnProperty, asyncGroupValidationAttribute.ShowMessageInSummary); isAsyncGroupValid = false; } else { // must be always set to be reset if required groupPropertyMatchMessages[castedGroupAttribute] = null; } } } return isValid && isAsyncValid && isGroupValid && isAsyncGroupValid; }
/// <summary> /// Manually adds a validation message for a given property. /// </summary> /// <param name="propertyName">Property, for which the validation message should be added.</param> /// <param name="validationMessage">Validation message that should be added.</param> /// <returns><c>true</c>, if the message has been added. <c>false</c>, if it was already there.</returns> public bool AddMessage(string propertyName, ValidationMessage validationMessage) { Guard.AssertNotNull(propertyName, "propertyName"); Guard.AssertNotNull(validationMessage, "validationMessage"); bool messagesChanged = this.SetPropertyMessage( propertyName, validationMessage, false); if (messagesChanged) { this.OnPropertyChanged("AllMessages"); this.OnPropertyChanged("Item[" + propertyName + "]"); this.NotifyPropertyMessagesChanged(propertyName); } return messagesChanged; }
/// <summary> /// Manually adds a validation message to the entire entity. /// </summary> /// <param name="validationMessage">Validation message that should be added.</param> /// <returns><c>true</c>, if the message has been added. <c>false</c>, if it was already there.</returns> public bool AddMessage(ValidationMessage validationMessage) { Guard.AssertNotNull(validationMessage, "validationMessage"); bool messagesChanged = this.SetEntityMessage(validationMessage); if (messagesChanged) { this.OnPropertyChanged("AllMessages"); } return messagesChanged; }
/// <summary> /// Manually adds a validation message for a given property with a certain level. /// </summary> /// <param name="propertySelector">Selector of the validated property.</param> /// <param name="validationLevel">Level of the validation message.</param> /// <param name="messageText">Validation message text.</param> /// <param name="showMessageOnProperty">Indicates if the message should be shown on the property at the UI.</param> /// <param name="showMessageInSummary">Indicates if the message should be shown in the validation summary at the UI.</param> /// <returns><c>true</c>, if the message has been added. <c>false</c>, if it was already there.</returns> public bool AddMessage( Expression<Func<object>> propertySelector, ValidationLevel validationLevel, string messageText, bool showMessageOnProperty = true, bool showMessageInSummary = true) { Guard.AssertNotNull(propertySelector, "propertySelector"); string propertyName = propertySelector.ExtractPropertyName(); var validationMessage = new ValidationMessage(validationLevel, messageText, showMessageOnProperty, showMessageInSummary, ValidationMessageKind.Property); return this.AddMessage(propertyName, validationMessage); }