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