2010-09-08 42 views
0

我通過WCF RIA Services元數據類使用Required屬性,並在其下面使用實體框架。提交之前MVVM /新實體/必需屬性/驗證

我創建一個新的實體,並讓視圖綁定到視圖模型。用戶查看它一段時間,點擊四周,然後嘗試保存它。

這裏的場景是,用戶沒有選擇或點擊到具有必需字段屬性的字段。

如何在提交數據之前確保所有必填字段都有數據?

Winforms有這個相同的問題,我用它來循環表單中的所有字段,以確保所有驗證器都已通過。

是否必須重新編寫頁面的基類 - 重新?

是否沒有辦法確保所有屬性驗證在發送回服務器之前被觸發?我是否必須使用反射並使用Required屬性來拾取所有字段?

我正在使用實體框架,並且我研究了INotifyDataErrorInfo - 但是在旅行到DB之後使用(據我瞭解)。

我知道我不是第一個打這個 - 但在我的研究中,我找不到這種情況的一個很好的例子。

回答

1

你看過System.ComponentModel.DataAnnotations中的Validator.ValidateObject嗎?我很確定它完全符合你的要求。

+0

是的!你是對的 - 但是文檔和示例利用這門課缺乏耐心,我很想知道更多,以及如何最好地將它與WCF集成RIA Services。我很驚訝WCF RIA在將變更集發送到服務器之前沒有對所有必填字段進行實體級檢查,擁有實體級的驗證方法將會非常棒,它可以強制執行所有屬性屬性在en tity。我有些驚訝MS會錯過這個驗證水平!我是否錯過了一些事情?從這裏開始:http://jeffhandley.com/archive/2009/10/16/validator.aspx – codeputer 2010-09-09 18:41:32

0

INotifyDataErrorInfo與訪問數據庫(或通過服務邊界調用,這正是我假設您的意思)沒有任何關係。 INotifyDataErrorInfo是您查看模型可以實現的接口,以向您的視圖報告您的視圖模型具有驗證錯誤。掛鉤視圖模型的驗證以使用該界面仍然取決於您,除非這是WCF RIA Services爲您提供的免費功能,我並不贊同這一點。

我在我的視圖模型中使用[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 
    } 
} 

希望這有助於!

+0

馬特 - 非常感謝您的廣泛發帖,以及您使用驗證所做的工作。我還沒有看到Fluent Validation庫,但是我在你的帖子後進行了快速審查。在WCF RIA Services(以及來自MS的其他產品)中,使用屬性通過組件庫創建驗證規則目前非常流行。我看到你的設計(和流利的圖書館是背離的),你會說這是真的嗎?我願意看看所有的可能性,但我已經走上了屬性的道路。有一個例子,我可以下載?Tks。R – codeputer 2010-09-08 17:42:59

+1

我從來沒有喜歡驗證的屬性IMO,屬性對元數據有用,但驗證是業務邏輯,但正如我在答案中提到的那樣,我確實使用屬性我的虛擬機屬性提示了需要哪些值的提示 – 2010-09-09 19:18:17