2010-10-26 64 views
1

我正在開發具有從EF 4.0創建的業務對象的應用程序。該應用程序與具有類存儲庫的數據層分層。以上是業務流程根據需求進行映射的業務層。業務層正在調用數據層。實體對象驗證和業務規則驗證

業務對象具有複雜的關聯。一種可能的情況: 商家有多個地址 商家屬於一個類別 商人有一個帳戶 帳戶具有一個錢包

用戶將創建與上述業務對象(圖)沿新商戶。創建和更新商家時有各種業務規則。

我想將我的驗證分成兩個子部分。 1)將通過驗證塊5.0和2處理的標量屬性驗證)業務流程規則要在業務層組件中處理的驗證。不過,我在集中報告破損規則(標量屬性驗證和業務流程規則驗證)方面面臨困難。我不希望在映射的業務流程違反業務規則的地方注入異常。

其中一個例如如下: 創建新商家時,需要驗證類別。因爲如果某個類型的類別與這個新商家相關聯,那麼業務規則就會說這個商家不能在系統中存在兩次。

現在,當UI將新的商家圖傳遞給業務層組件時,首先驗證BO標量屬性驗證(驗證的bu驗證塊),然後將此BO傳遞到業務流程規則驗證方法中以檢查各種規則。我想舉報可能存在的違規問題,並且不允許持有商家和圖表對象。

請分享任何有價值的設計方法來管理集中驗證規則記錄和報告到UI層。

編輯:我不想在EF SaveChanges,ChangeAssociation等事件處理程序上合併驗證。

+0

不要忘記標記你最喜歡的答案;-) – Steven 2010-11-21 16:33:02

回答

1

雖然你說你不想拋出異常,但我發現拋出異常非常有效。尤其是當您有多個子系統驗證系統時,將一種類型的異常作爲門面引發將非常有效。

您可以做的是創建一個自定義異常(即名爲ValidationException),當您的驗證應用程序塊(VAB)報告錯誤或業務規則報告錯誤時,該異常將被拋出。在表示層中,您必須捕獲此ValidationException並以便於用戶使用的方式向最終用戶報告此確切類型的異常。

爲兩個驗證子系統使用單一的異常類型,允許您以一致的方式報告錯誤,並確保當您(或其他開發人員)忘記處理驗證錯誤時,不會忽視驗證錯誤。處理錯誤應該是非常明確的IMO。當你讓方法返回一個錯誤列表時,很容易忘記處理它們。在創建自定義異常時,很容易將屬性添加到包含驗證錯誤列表的異常類型。這樣的錯誤列表很容易從VAB中提取。我不知道您用於業務規則驗證的驗證系統是什麼,但不能很難從中提取錯誤代碼列表。

最簡單的方法在UI來處理,這是當然的使用try - catch

var businessCommand = new CreateNewMerchantCommand(); 
businessCommand.Name = "Wikki"; 
// etc 

try 
{ 
    businessCommand.Execute(); 
} 
catch (ValidationException ex) 
{ 
    UIValidationHelper.ReportValidationErrors(ex.Errors); 
} 

有這些try當然 - catch陳述到處是醜陋的,但至少這段代碼很容易遵循。取決於您構建業務層的方式以及您使用的UI技術,您可以使用更漂亮的解決方案。舉例來說,你可以用實際操作中,可以如下失敗,一個動作:

var businessCommand = new CreateNewMerchantCommand(); 
businessCommand.Name = "Wikki"; 
// etc 

UIValidationHelper.ExecuteOrDisplayErrors(() => 
{ 
    businessCommand.Execute(); 
}); 

UIValidationHelper應該是這樣的:

public static class UIValidationHelper 
{ 
    public static void ExecuteOrDisplayErrors (Action action) 
    { 
     try 
     { 
      action(); 
     } 
     catch (ValidationException ex) 
     { 
      // Show the errors in your UI technology 
      ShowErrorMessage(ex.Errors); 
     } 
    } 
} 

的另一種選擇,那我用我自己的過去,是通過事件擴展業務層。爲了這個工作,你需要一個構造,比如命令,就像我在我的例子中使用的那樣。當使用事件,該UI看起來是這樣的:

var businessCommand = new CreateNewMerchantCommand(); 
businessCommand.Name = "Wikki"; 
// etc 

businessCommand.ValidationErrorOccurred += 
    UIValidationHelper.DisplayValidationErrors; 

businessCommand.Execute(); 

這個例子鉤到ValidationErrorOccurred事件的命令實例的靜態方法。這裏的技巧是讓該命令的Execute方法捕獲ValidationException,並在註冊時將它們路由到ValidationErrorOccurred。當沒有方法註冊時,這個異常應該會喚起調用堆棧,因爲未處理的驗證異常當然不會被忽視。

儘管可以直接從業務層做到這一點,但它會使您的業務層依賴於特定的驗證技術。除此之外,此方法允許客戶選擇以任何他們想要的方式處理驗證錯誤,或決定根本不處理它們(例如,當您不希望在特定用例中出現驗證錯誤時)。

使用ASP時。NET Web窗體中,UIValidationHelperDisplayValidationErrors方法看起來是這樣的:

public static class UIValidationHelper 
{ 
    public static void DisplayValidationErrors(
     object sender, ValidationErrorEventArgs e) 
    { 
     Page page = GetValidPageFromCurrentHttpContext(); 
     var summary = GetValidationSummaryFromPage() 

     foreach (var result in e.Error.Results) 
     { 
      summary.Controls.Add(new CustomValidator 
      { 
       ErrorMessage = result.Message, 
       IsValid = false 
      }); 
     } 
    } 

    private static Page GetValidPageFromCurrentHttpContext() 
    { 
     return (Page)HttpContext.Current.CurrentHandler; 
    } 

    private ValidationSummary GetValidationSummaryFromPage(Page page) 
    { 
     return page.Controls.OfType<ValidationSummary>().First(); 
    } 
} 

這種靜態輔助方法注入了報告的錯誤消息到頁面上的ValidationSummary控制。它期望該頁面在頁面的根級別包含一個ValidationSummary控件。可以輕鬆創建更靈活的解決方案。

我喜歡向您展示的最後一件事是採用此解決方案時BusinessCommand的外觀如何。基類這些命令可能是這個樣子:

public abstract class BusinessCommand 
{ 
    public event EventHandler<ValidationErrorEventArgs> 
     ValidationErrorOccurred; 

    public void Execute() 
    { 
     try 
     { 
      this.ExecuteInternal(); 
     } 
     catch (ValidationException ex) 
     { 
      if (this.ValidationErrorOccurred != null) 
      { 
       var e = new ValidationErrorEventArgs(ex); 
       this.ValidationErrorOccurred(this, e); 
      } 
      else 
      { 
       // Not rethrowing here would be a bad thing! 
       throw; 
      } 
     } 
    } 

    protected abstract void ExecuteInternal(); 
} 

ValidationErrorEventArgs看起來是這樣的:

public class ValidationErrorEventArgs : EventArgs 
{ 
    public ValidationErrorEventArgs(ValidationException error) 
    { 
     this.Error = error; 
    } 

    public ValidationException Error { get; private set; } 
} 

我希望這一切有意義,對不起,我長的答案:-)

祝你好運。

+0

謝謝你這樣一個詳細的答案。是的,我同意有客戶異常更合適,然後不拋出異常。除了具有驗證規則的日誌記錄鏈接到客戶異常是正確的方法。 – Wikki 2010-10-26 15:43:10

0

對象成員驗證可以在封裝業務對象內完成。無論你在setter屬性中執行此操作還是作爲單獨的方法調用都由您決定。如果對象的值設置爲無效值,應用程序是否允許數據進入錯誤類型的系統,範圍檢查等。

至於規則部分,我會查看每個對象的訪問者模式圖表,你試圖實現一些規則檢查。我會根據發現的情況報告這可能是一個新的嵌套對象。我個人對這個報告方面的偏好是使用訪問者模式,根據您的效率需求生成XML文檔或其他自定義嵌套類。訪問者模式中的實際規則可以在訪問者模式之外聲明,最好以聲明方式聲明。例如CheckDuplicateRecord。這將允許重用。

將所有這些與業務層保持在同一層,但將業務層進一步細分爲規則驗證層和業務對象。

我個人使用EF的方法是使用POCO對象,因爲這些對象允許可擴展性。然後,我會在UI上進行一些驗證,然後在傳輸到Business Object層時進行一些驗證,然後在EF DAL層中再次執行驗證。