2009-10-28 75 views
5

我對用於在asp.net mvc中進行業務規則驗證的方法有疑問。在Asp.net MVC中使用異常驗證業務邏輯

目前,我有一個異常類,看起來像這樣

public class ValidationException : Exception 
{ 
    private ModelStateDictionary State { get; set; } 
    public ValidationException(ModelStateDictionary state) 
    { 
     State = state; 
    } 
    public void MergeModelStates(ModelStateDictionary state) 
    { 
     state.Merge(this.State); 
    } 
} 

和看起來像這樣

public void Validate(IEntity entity) 
{ 
    ModelStateDictionary state = new ModelStateDictionary(); 
    if (entity.Contact != null && _service.GetBy(entity.Contact.Id) == null) 
     state.AddModelError("Contact", "Invalid Contact."); 
    if (entity.Title.Length > 8) 
     state.AddModelError("title", "Title is too long..."); 
    ... etc 
    if (!state.IsValid) 
     throw new ValidationException(state); 
} 

驗證器和控制器,做這樣的事情

public ActionResult Add() 
{ 
    var entity = new InputModel; 
    try 
    { 
     TryUpdateMode(inputModel); 
     ..... Send input to a Repository (Repository calls Validate(entity); 
    } 
    catch (ValidationException validationException) 
    { 
     validationException.MergeModelStates(this.ModelState); 
     TryUpdateModel(inputModel); 
     return View("Add",inputModel); 
    } 
    return View("List"); 
} 

使用異常來做這樣的事情是錯誤的嗎? 有更好方法的例子嗎?我真的不想將驗證添加到模型實體本身。我已經看到完成的唯一方法是將控制器模型狀態注入到存儲庫層,但對我來說似乎很渺茫。

感謝所有幫助

回答

13

例外一般應爲特殊情況下,並沒有處理的東西,可能你的程序的正常執行過程中經常發生。有很多很好的原因 - 這裏的一些我已經相當經常碰到:

  1. 性能問題 - 例外通常是相當昂貴的操作 - 如果他們拋出定期性能可能會受到影響。
  2. 處理未捕獲的驗證異常 - 如果您碰巧使用您的代碼而未處理異常,則會將驗證錯誤顯示爲「黃色屏幕」或碰撞處理程序 - 可能不是最好的用戶體驗。
  3. 例外情況的結構不允許用戶獲得良好的信息。看一下異常類 - 沒有太多的設置方式可以提供良好的面向用戶的信息,這是將信息傳遞給用戶所需的東西。任何時候我試圖以這種方式使用異常,我已經結束了一大堆帶有屬性的子類,這些子類並沒有真正意義上屬於異常。

一種方法我平時喜歡做的是提供一個公共驗證方法,它返回的錯誤的列表(但從來沒有拋出異常本身),然後它調用驗證(Save方法)和將引發異常有任何錯誤。如果將行爲從「拋出,如果模型無效」切換到「拋出,如果代碼在模型處於無效狀態時嘗試保存」。

爲了解決下面有關在驗證和保存中拋出的性能的評論 - 在Save()中拋出拋出Validate()將具有完全相同的性能損失。然而,關鍵的區別在於,這絕不會發生 - 您正在防範開發人員不恰當地使用您的類,而不是將異常用作驗證方法。正確地編寫,代碼調用保存方法應該是這個樣子:

ValidationResult result = obj.Validate(); 
if (result.IsValid) { 
    obj.Save(); 
} else { 
    // display errors to the user 
} 

如果開發商忘了保存前檢查驗證狀態的異常只會被拋出。這有利於允許驗證在不使用異常的情況下進行,並且通過永不允許保存無效實體來保護數據庫。理想情況下,您不會在控制器中捕獲異常,並讓常規錯誤處理例程處理它,因爲問題不再是用戶輸入,而是開發人員的錯誤。

+0

謝謝!這對我來說看起來更清潔,因爲ModelStateDictionary不在除控制器之外的任何其他類中使用。 從Save方法拋出而不是從validate方法拋出性能差異? 哦,一旦異常被拋出並被捕獲,我將需要調用驗證並獲取所有錯誤並將它們添加到模型狀態? – AlteredConcept 2009-10-28 15:01:22

+0

好吧,我必須在控制器中再次驗證一次,並再次在保存方法中驗證。廢話我想在存儲庫層做我所有的驗證,但我想我也必須在控制器中做到這一點。哦,好吧..謝謝! – AlteredConcept 2009-10-28 19:51:54

2

理想情況下,你會使用數據批註功能在ASP.NET MVC 2: -

http://stephenwalther.com/blog/archive/2008/09/10/asp-net-mvc-tip-43-use-data-annotation-validators.aspx

http://weblogs.asp.net/scottgu/archive/2009/07/31/asp-net-mvc-v2-preview-1-released.aspx

例如: -

public class Person 
{ 
    [Required(ErrorMessage="Please enter a name.")] 
    public String Name { get; set; } 
} 

如果你沒有升級,仍然有一個解決方案:

如果您向驗證程序傳遞Controller的ModelState字典的引用(如果擔心問題分離,請將其包含在接口中),如果發現錯誤,則調用AddModelError,那麼在Controller中可以調用if(ModelState。 IsValid的),並採取適當的行動: -

var entity = new InputModel(); 

TryUpdateModel(entity); 
MyValidator.Validate(entity, ModelState); 

if(ModelState.IsValid) { ... 

和您的驗證是這樣的: -

public void Validate(IEntity entity, ModelStateDictionary state) 
{ 
    if (entity.Contact != null && _service.ValidId(entity.Contact.Id) == null) 
    state.AddModelError("Contact", "Invalid Contact."); 
    // etc 
}