2011-12-13 162 views
14

我試圖在我的應用程序中實現驗證策略。我有一個MVC層,服務層,存儲庫和域POCO。現在在MVC層,我在我的視圖模型上使用數據註釋來驗證用戶輸入,從而讓我給用戶快速反饋。在控制器中,在使用automapper設置域對象之前,我調用ModelState.IsValid來檢查輸入。服務層驗證

這是我的麻煩所在。我將我的域對象傳入需要根據業務規則進行驗證的服務,但是如何將驗證錯誤傳遞迴控制器?我找到的示例執行以下操作之一:

  • 在服務層中拋出異常並捕獲Contoller。但這似乎是錯誤的,肯定例外情況是例外,我們應該返回一些有意義的東西。
  • 使用ModelStateWrapper並將ModelStateDictionary注入到服務中。但是這種方法最終展現爲循環依賴(控制器依賴於服務,服務依賴於控制器),這似乎是一種糟糕的代碼異味。
  • 將驗證方法添加到POCO。與此相關的問題是業務規則可能依賴於其他POCO對象,所以必須在可訪問所需表和對象的服務中完成。

有沒有更簡單的方法我缺少?我已經看到很多關於這個問題的問題,但除了上面提到的問題外,沒有具體的解決方案我在考慮在服務中進行驗證的任何方法都可以傳回一些我可以在控制器中使用的鍵/值對象,但我不確定這種策略是否會在稍後出現問題。

+0

您可能會發現以下答案有用:http://stackoverflow.com/a/4851953/29407 –

+0

感謝達林,這看起來很有前途,雖然它確實用於Exception方法。這是通常被接受的方式嗎? – James

回答

13

我認爲相當優雅的方法是在服務上有一個驗證方法,該方法返回來自業務邏輯的模型錯誤字典。這樣,沒有將ModelState注入到服務中 - 該服務只是進行驗證並返回任何錯誤。然後由控制器將這些ModelState錯誤重新合併到ViewData中。

所以服務驗證方法可能如下:

public IDictionary<string, string> ValidatePerson(Person person) 
{ 
    Dictionary<string, string> errors = new Dictionary<string, string>(); 

    // Do some validation, e.g. check if the person already exists etc etc 

    // Add model erros e.g.: 
    errors.Add("Email", "This person already exists"); 
} 

然後你可以使用擴展方法在你的控制器到這些錯誤映射到ModelState中,是這樣的:

public static class ModelStateDictionaryExtensions 
{ 
    public static void Merge(this ModelStateDictionary modelState, IDictionary<string, string> dictionary, string prefix) 
    { 
     foreach (var item in dictionary) 
     { 
      modelState.AddModelError((string.IsNullOrEmpty(prefix) ? "" : (prefix + ".")) + item.Key, item.Value); 
     } 
    } 
} 

而且您的控制器然後將使用:

ModelState.Merge(personService.ValidatePerson(person), ""); 
+0

這會在使用Service.Create()(例如)之前從控制器調用嗎?但是如果Service.Create()有一些傳入對象失敗的專門業務邏輯會怎麼樣?我們將如何通知控制器?在我看來,每種服務方法都需要自己的獨特驗證,因此可以通知控制器問題。我想我的服務方法可以返回IDictionary? – James

+0

是的,您可以單獨調用Validate,但也應該在create方法中從服務本身調用驗證。這樣,創建方法也可以返回錯誤字典。如果字典爲空,則驗證成功。 –

+0

我認爲這可能是最好的選擇。正如許多指南中所建議的那樣,我不想沒有很好的理由拋出異常。你自己用過這種方法嗎?我主要關心的是,如果一個Service方法已經有一個返回類型,那麼我將無法返回IDictionary。我想這就是爲什麼一些指南使用ModelStateWrapper。 – James

1

作爲cre按照Ian的建議,你可以用一個接受函數的驗證器來完成這個工作。

例如在服務層:

public void ValidateModel(Customer customer, Action<string, string> AddModelError) 
{ 
    if (customer.Email == null) AddModelError("Email", "Hey you forgot your email address."); 
} 

然後在您的控制器,您驗證通過一個調用:

myService.ValidateModel(model, ModelState.AddModelError); 

或者說你想用你的驗證在一個控制檯應用程序而無需訪問ModelStateDictionary,你可以做到這一點:

errors = new NameValueDictionary(); 
myService.ValidateModel(model, errors.Add); 

這兩項工作的原因 ModelStateDictionary.AddModelError()NameValueDictionary.Add()Action<string, string>的方法簽名匹配。