MVVM/新实体/必需属性/提交前验证

MVVM/新实体/必需属性/提交前验证,mvvm,mvvm-light,wcf-ria-services,Mvvm,Mvvm Light,Wcf Ria Services,我正在通过WCF RIA Services元数据类在上使用Required属性,该类下面是实体框架 我创建了一个新实体,并让视图绑定到视图模型。用户查看它一段时间,单击周围,然后尝试保存它 这里的场景是,用户没有在具有必填字段属性的字段中单击或制表符 在提交数据之前,我如何确保所有必填字段都有数据 Winforms也有同样的问题,我使用它循环表单中的所有字段,以确保所有验证程序都已通过 我必须重新编写页面的基类吗 是否有办法确保在将属性验证发送回服务器之前触发所有属性验证?我是否必须使用反射并拾

我正在通过WCF RIA Services元数据类在上使用Required属性,该类下面是实体框架

我创建了一个新实体,并让视图绑定到视图模型。用户查看它一段时间,单击周围,然后尝试保存它

这里的场景是,用户没有在具有必填字段属性的字段中单击或制表符

在提交数据之前,我如何确保所有必填字段都有数据

Winforms也有同样的问题,我使用它循环表单中的所有字段,以确保所有验证程序都已通过

我必须重新编写页面的基类吗

是否有办法确保在将属性验证发送回服务器之前触发所有属性验证?我是否必须使用反射并拾取具有必需属性的所有字段

我使用的是实体框架,我研究过INotifyDataErrorInfo——但这是在访问数据库之后使用的(据我所知)

我知道我不是第一个遇到这种情况的人,但在我的研究中,我找不到这种情况的好例子。

与数据库的访问(或通过服务边界的呼叫,我认为这是你的意思)没有任何关系。INotifyDataErrorInfo是视图模型可以实现的接口,用于向视图报告视图模型存在验证错误。连接视图模型的验证以使用该接口仍然取决于您,除非WCF RIA Services免费为您提供了这一点,我对此并不乐观

我在视图模型中使用[Required]属性只是为了向UI提示我的字段是必需的。我还在视图模型中实现INotifyDataErrorInfo,并确保在视图模型中的任何属性发生更改时调用我的验证方法。当用户执行save命令时,我还手动调用我的验证方法

在我的例子中,我使用库来实现我的验证逻辑。我还为任何需要验证逻辑的视图模型构建了一个新的基类

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;
    }
}
公共类验证ViewModelBase:ViewModelBase、IValidingViewModel、INotifyDataErrorInfo
{
专用只读IValidator_验证程序;
私有只读字典_错误;
公共事件事件处理程序错误更改;
public ValidatingViewModelBase():这(null,null)
{
}
公共验证ViewModelBase(IValidator验证程序):此(验证程序,null)
{
}
公共验证ViewModelBase(IValidator验证程序、IMessenger messenger):基本(messenger)
{
_验证器=验证器;
_错误=新字典();
}
公共IEnumerable GetErrors(字符串propertyName)
{
if(string.IsNullOrEmpty(propertyName))
返回_errors.Values;
CreateValidationErrorFolistForProperty(propertyName);
返回_错误[propertyName];
}
公共布尔错误
{
获取{return\u errors.Count>0;}
}
受保护的虚拟void AddValidationErrorForProperty(字符串propertyName,ValidationInfo ValidationInfo)
{
CreateValidationErrorFolistForProperty(propertyName);
如果(!\u错误[propertyName]。包含(validationInfo))
{
_错误[propertyName]。添加(validationInfo);
RaiserRorschanged(propertyName);
}
}
受保护的虚拟void ClearValidationErrorForProperty(字符串propertyName)
{
CreateValidationErrorFolistForProperty(propertyName);
如果(\u错误[propertyName].Count>0)
{
_错误[propertyName]。清除();
RaiserRorschanged(propertyName);
}
}
受保护的虚拟空间ClearAllValidationErrors()
{
foreach(在_errors.Keys中的var propertyName)
ClearValidationErrorForProperty(propertyName);
_错误。清除();
}
私有void CreateValidationErrorFolistForProperty(字符串propertyName)
{
如果(!\u errors.ContainsKey(propertyName))
_错误[propertyName]=新列表();
}
受保护的无效RAISEERROSCHANGED(字符串属性名称)
{
var handler=ErrorsChanged;
if(处理程序!=null)
{
Invoke(这是新的DataErrorsChangedEventArgs(propertyName));
}
}
受保护的覆盖无效RaisePropertyChanged(字符串propertyName)
{
验证();
base.RaisePropertyChanged(propertyName);
}
公共bool验证()
{
if(_validator==null)
返回true;
if(这是ILoadAndSaveData&&!((ILoadAndSaveData)this).hasloaddeddata)
返回true;
ClearAllValidationErrors();
var results=\u validator.Validate(此);
如果(!results.IsValid)
{
foreach(结果中的var失败。错误)
{
AddValidationErrorForProperty(failure.PropertyName,
新的ValidationInfo(failure.ErrorMessage,ValidationType.Error));
}
}
返回results.IsValid;
}
public void SendValidationMessage()
{
var message=\u errors.Values.SelectMany(propertyValidations=>propertyValidations)
.Aggregate(“保存前请更正验证错误。\r\n”,
(当前,validationInfo)=>current+(“\r\n·”+validationInfo.Message));
Send(newerrormessage(newerrorinfo{Message=Message,Type=“Validation Error”}));
}
public bool ValidateAndSendValidationMessage()
{
var isValid=Validate();
如果(!isValid)
{
SendValidationMessage();
}
返回有效;
}
}
公共界面IValidingViewModel
{
bool验证();
void SendValidationMessage();
bool ValidateAndSendValidationMessage();
}
公共枚举验证类型{错误,警告}
公共类验证信息
{
公共字符串消息{get;set;}
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
    }
}