我通过WCF RIA Services Metadata类使用Required属性,其下面是Entity Framework。
我创建了一个新实体,让视图绑定到视图模型。用户查看它一段时间,点击,然后尝试保存它。
此处的方案是用户没有标签或点击具有必填字段属性的字段。
如何在提交数据之前确保所有必填字段都有数据?
Winforms有同样的问题,我用来循环表单中的所有字段以确保所有验证器都已通过。
我是否必须为页面重写该基类 - AGAIN?
在将其发送回服务器之前,是否有办法确保触发所有属性验证?我是否必须使用反射并使用Required属性选取所有字段?
我正在使用实体框架,并且我研究了INotifyDataErrorInfo - 但这是在数据库之旅后使用的(据我理解)。
我知道我不是第一个遇到这种情况的人 - 但在我的研究中,我找不到这种情况的好例子。
答案 0 :(得分:1)
你看过System.ComponentModel.DataAnnotations中的Validator.ValidateObject吗?我很确定它完全符合你的要求。
答案 1 :(得分:0)
INotifyDataErrorInfo与访问数据库没有任何关系(或通过服务边界调用,这是我认为你的意思)。 INotifyDataErrorInfo是您查看模型可以实现的接口,以便向您的视图报告您的视图模型存在验证错误。连接您的视图模型的验证以使用该界面仍由您自己决定,除非WCF RIA服务免费为您提供,我并不乐观。
我在视图模型中使用[Required]属性只是为UI提供了我的字段所需的提示。我还在我的视图模型中实现了INotifyDataErrorInfo,并确保在视图模型中的任何属性发生更改时调用我的验证方法。当用户执行保存命令时,我还手动调用我的验证方法。
在我的情况下,我正在使用Fluent Validation库来实现我的验证逻辑。我还为需要验证逻辑的任何视图模型构建了一个新的基类。
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;
}
}
所以我的视图模型继承自这个新的基类。
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
}
}
我确保为每个视图模型创建一个验证器类。
public class ExampleValidator : AbstractValidator<IExampleViewModel>
{
public TripInformationValidator()
{
// validation logic here
}
}
希望这有帮助!