INotifyDataErrorInfo doesn't have anything to do with trips to the DB (or calls over a service boundary, which is what I assume you meant). INotifyDataErrorInfo is an interface that you view model can implement in order to report to your view that your view model has validation errors. Hooking up the validation of your view model to use that interface is still up to you, unless that's something WCF RIA Services gives you for free, which I'm not positive about.
I use the [Required] attribute in my view models just to give a hint to the UI that my fields are required. I also implement INotifyDataErrorInfo in my view model and make sure to call my validation method whenever any properties in the view model change. I also manually call my validation method when the user executes a save command.
In my case I'm using the Fluent Validation library to implement my validation logic. I also built a new base class for any view models that need validation logic.
public class ValidatingViewModelBase<T> : ViewModelBase, IValidatingViewModel, INotifyDataErrorInfo
{
private readonly IValidator<T> _validator;
private readonly Dictionary<string, List<ValidationInfo>> _errors;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public ValidatingViewModelBase() : this(null, null)
{
}
public ValidatingViewModelBase(IValidator<T> validator) : this(validator, null)
{
}
public ValidatingViewModelBase(IValidator<T> validator, IMessenger messenger) : base(messenger)
{
_validator = validator;
_errors = new Dictionary<string, List<ValidationInfo>>();
}
public IEnumerable GetErrors(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
return _errors.Values;
CreateValidationErrorInfoListForProperty(propertyName);
return _errors[propertyName];
}
public bool HasErrors
{
get { return _errors.Count > 0; }
}
protected virtual void AddValidationErrorForProperty(string propertyName, ValidationInfo validationInfo)
{
CreateValidationErrorInfoListForProperty(propertyName);
if (!_errors[propertyName].Contains(validationInfo))
{
_errors[propertyName].Add(validationInfo);
RaiseErrorsChanged(propertyName);
}
}
protected virtual void ClearValidationErrorsForProperty(string propertyName)
{
CreateValidationErrorInfoListForProperty(propertyName);
if (_errors[propertyName].Count > 0)
{
_errors[propertyName].Clear();
RaiseErrorsChanged(propertyName);
}
}
protected virtual void ClearAllValidationErrors()
{
foreach (var propertyName in _errors.Keys)
ClearValidationErrorsForProperty(propertyName);
_errors.Clear();
}
private void CreateValidationErrorInfoListForProperty(string propertyName)
{
if (!_errors.ContainsKey(propertyName))
_errors[propertyName] = new List<ValidationInfo>();
}
protected void RaiseErrorsChanged(string propertyName)
{
var handler = ErrorsChanged;
if (handler != null)
{
handler.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
}
protected override void RaisePropertyChanged(string propertyName)
{
Validate();
base.RaisePropertyChanged(propertyName);
}
public bool Validate()
{
if (_validator == null)
return true;
if (this is ILoadAndSaveData && !((ILoadAndSaveData)this).HasLoadedData)
return true;
ClearAllValidationErrors();
var results = _validator.Validate(this);
if (!results.IsValid)
{
foreach (var failure in results.Errors)
{
AddValidationErrorForProperty(failure.PropertyName,
new ValidationInfo(failure.ErrorMessage, ValidationType.Error));
}
}
return results.IsValid;
}
public void SendValidationMessage()
{
var message = _errors.Values.SelectMany(propertyValidations => propertyValidations)
.Aggregate("Please correct validation errors before saving.\r\n",
(current, validationInfo) => current + ("\r\n· " + validationInfo.Message));
MessengerInstance.Send(new ErrorMessage(new ErrorInfo { Message = message, Type = "Validation Error" }));
}
public bool ValidateAndSendValidationMessage()
{
var isValid = Validate();
if (!isValid)
{
SendValidationMessage();
}
return isValid;
}
}
public interface IValidatingViewModel
{
bool Validate();
void SendValidationMessage();
bool ValidateAndSendValidationMessage();
}
public enum ValidationType { Error, Warning }
public class ValidationInfo
{
public string Message { get; set; }
public ValidationType Type { get; set; }
public ValidationInfo(string message, ValidationType validationType)
{
Message = message;
Type = validationType;
}
public override string ToString()
{
var result = Message;
if (Type == ValidationType.Warning)
result = "Warning: " + result;
return result;
}
}
So my view models inherit from this new base class.
public class ExampleViewModel : ValidatingViewModelBase<IExampleViewModel>, IExampleViewModel
{
public ExampleViewModel(IValidator<IExampleViewModel> validator,
IMessenger messenger)
: base(validator, messenger)
{
SaveCommand = new RelayCommand(SaveCommandExecute);
}
public RelayCommand SaveCommand { get; private set; }
private void SaveCommandExecute()
{
if (!ValidateAndSendValidationMessage())
return;
// save stuff here
}
}
And I make sure to create a validator class for each view model.
public class ExampleValidator : AbstractValidator<IExampleViewModel>
{
public TripInformationValidator()
{
// validation logic here
}
}
Hope this helps!