Quantcast
Channel: Fluent Validation for .NET
Viewing all articles
Browse latest Browse all 1917

New Post: Non-property oriented validation

$
0
0
2) I have implemented an adapter for DataErrorInfo that could be used for WPF and Silverlight. Note: propertyName = string.Empty stands for an error for the object not directly connected with a property
    public class DataErrorInfoAdapter<TError> : INotifyDataErrorInfo, IDataErrorInfo
    {
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        private const string ObjectPropertyName = "";
        private static readonly TError[] NoErrors = new TError[0];
        
        private readonly Dictionary<string, List<TError>> validationResults;
        private readonly Func<string, IEnumerable<TError>> propertyValidation;
        private readonly Func<IEnumerable<TError>, string> propertyErrorsToMessage;

        public DataErrorInfoAdapter(Func<string, IEnumerable<TError>> propertyValidation, Func<IEnumerable<TError>, string> propertyErrorsToMessage)
        {
            this.propertyValidation = propertyValidation;
            this.propertyErrorsToMessage = propertyErrorsToMessage;

            validationResults = new Dictionary<string, List<TError>>();
        }

        public IEnumerable<TError> CheckErrors(string propertyName)
        {
            return validationResults[propertyName];
        }

        public string Error
        {
            get
            {
                IEnumerable<TError> errors = GetSetErrors(ObjectPropertyName);
                return propertyErrorsToMessage(errors);
            }
        }

        public string this[string propertyName]
        {
            get
            {
                // if property validation then validate also the object
                if (propertyName != ObjectPropertyName)
                {
                    Validate(ObjectPropertyName);
                }

                IEnumerable<TError> errors = Validate(propertyName);
                return propertyErrorsToMessage(errors);
            }
        }

        public IEnumerable GetErrors(string propertyName)
        {
            return Validate(propertyName);
        }

        public bool HasErrors
        {
            get { return validationResults.Any(); }
        }

        private void RaiseErrorsChanged(string propertyName)
        {
            var handler = ErrorsChanged;
            if (handler != null)
                handler(this, new DataErrorsChangedEventArgs(propertyName));
        }

        private IEnumerable<TError> Validate(string propertyName)
        {
            IEnumerable<TError> errors = propertyValidation(propertyName);
            SetErrors(propertyName, errors);
            return errors;
        }

        private void SetErrors(string propertyName, IEnumerable<TError> newValidationResults)
        {
            var localPropertyName = propertyName ?? string.Empty;
            var hasCurrentValidationResults = validationResults.ContainsKey(localPropertyName);
            var hasNewValidationResults = newValidationResults != null && newValidationResults.Any();

            if (hasCurrentValidationResults || hasNewValidationResults)
            {
                if (hasNewValidationResults)
                {
                    validationResults[localPropertyName] = new List<TError>(newValidationResults);
                }
                else
                {
                    validationResults.Remove(localPropertyName);
                }

                RaiseErrorsChanged(localPropertyName);
            }
        }

        private IEnumerable<TError> GetSetErrors(string propertyName)
        {
            var localPropertyName = propertyName ?? string.Empty;
            List<TError> currentValidationResults;
            if (validationResults.TryGetValue(localPropertyName, out currentValidationResults))
                return currentValidationResults;
            else
                return NoErrors;
        }
    }
A sample usage to create a ValidatableViewModel (here an implenetation on top of MVVM Light):
public abstract class ValidatableViewModel<TError> : ViewModelBase, INotifyDataErrorInfo, IDataErrorInfo
    {
        private DataErrorInfoAdapter<TError> dataErrorInfoAdapter;

        protected ValidatableViewModel()
        {
            Initialize();
        }

        protected ValidatableViewModel(IMessenger messenger)
            : base(messenger)
        {
            Initialize();
        }

        public void Validate()
        {
            RaisePropertyChanged(string.Empty);
        }

        private void Initialize()
        {
            dataErrorInfoAdapter = new DataErrorInfoAdapter<TError>(Validate, GetFirstErrorString);
            dataErrorInfoAdapter.ErrorsChanged += OnErrorsChanged;
        }

        protected abstract IEnumerable<TError> Validate(string propertyName);

        private string GetFirstErrorString(IEnumerable<TError> errors)
        {
            TError error = errors.FirstOrDefault();
            return error.EqualsDefault() ? null : error.ToString();
        } 

        private void OnErrorsChanged(object sender, DataErrorsChangedEventArgs e)
        {
            RaisePropertyChanged(() => HasErrors);
            RaisePropertyChanged(() => Error);
        }

        #region Interface adaptation

        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged
        {
            add { dataErrorInfoAdapter.ErrorsChanged += value; }
            remove { dataErrorInfoAdapter.ErrorsChanged -= value; }
        }

        public virtual string Error
        {
            get { return dataErrorInfoAdapter.Error; }
        }

        public string this[string columnName]
        {
            get { return dataErrorInfoAdapter[columnName]; }
        }

        public IEnumerable GetErrors(string propertyName)
        {
            return dataErrorInfoAdapter.GetErrors(propertyName);
        }

        public bool HasErrors
        {
            get { return dataErrorInfoAdapter.HasErrors; }
        }

        #endregion
    }
So now I created a generic viewmodel that supports custom validation.
And in some inherting class:
public class SampleViewModel : ValidatableViewModel<ValidationResult> { ... }
we just override the Validate method where you can use FluentValidation, example:
protected override IEnumerable<ValidationError> Validate(string propertyName)
{
   var result = someFluentValidator.Validate(this, propertyName);
   return result.Errors;
}
Please leave me a comment if it is clear and I would be thankful for any comments.

Viewing all articles
Browse latest Browse all 1917

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>