2011-07-01 73 views
5

在asp.net mvc中一個常見的推薦做法是,you should not send your business models to your views ..相反,您應該創建特定於每個視圖的viewmodels。你在哪裏把你的驗證在asp.net mvc 3?

完成此操作後,您可以在控制器中調用ModelState.IsValid方法,從而有效地檢查視圖模型的有效性,但不檢查業務對象。

傳統的處理方法是什麼?

public class Person 
{ 
public int ID {get; set;}; 

[Required] 
public string Name {get; set;} 

[Required] 
public string LastName {get; set;} 

public virtual ICollection<Exam> Exams {get; set;} 

} 

public class PersonFormViewModel 
{ 

public int ID {get; set;};  


[Required] 
public string Name {get; set;} 

[Required] 
public string LastName {get; set;} 

} 

這正是我現在所擁有的,但林不知道,如果[必需]屬性應該出現在這兩種模式或僅視圖模型或只是商業模式。

有關此問題的任何提示都將受到讚賞。

更多鏈接支持我的觀點,即始終使用視圖模型是一種常見的良好做法。

How to add validation to my POCO(template) classes

http://blogs.msdn.com/b/simonince/archive/2010/01/26/view-models-in-asp-net-mvc.aspx

+0

你的「商業模式」究竟是什麼? –

+0

Person是一個由EntityFramework跟蹤的類。 PersonViewModel顯然不是..閱讀我鏈接到的博客文章,你將理解我試圖遵循的實踐..因此,驗證邏輯應該到哪裏去的問題。 – ignaciofuentes

+0

謝謝 - 抱歉,我掃描了文章並在其中搜索了「商業模式」,但沒有點擊。 –

回答

7

我的選擇是做視圖模型輸入驗證,並在域模型業務驗證。

換言之,任何數據註釋(如必填字段,長度驗證,正則表達式等)都應該在您的視圖模型上完成,並且在發生錯誤時添加到模型狀態。

而且您可能會擁有不僅僅依賴「表單」的業務/域規則,所以您應該在域模型(在映射回來後執行驗證)或者使用服務層。

我們所有的模型都有一個名爲「Validate」的方法,我們在持續之前調用服務。如果它們失敗了業務驗證,那麼它們會拋出自定義異常,該業務驗證會被控制器捕獲並添加到模型狀態。

可能不是每個人的一杯茶,但它是一致的。按照要求業務驗證的

例子:

這裏是我們的域模型,它代表了通用的「郵報」的一個例子(問題,照片,視頻等):

public abstract class Post 
{ 
    // .. fields, properties, domain logic, etc 

    public void Validate() 
    { 
     if (!this.GeospatialIdentity.IsValidForThisTypeOfPost()) 
     throw new DomainException(this, BusinessException.PostNotValidForThisSpatial.); 
    } 
} 

你看到那裏,我檢查業務規則,並拋出自定義異常。 DomainException是我們的基礎,我們有許多派生的實現。我們有一個名爲BusinessException的枚舉,其中包含我們所有例外的值。我們在枚舉上使用擴展方法來提供基於資源的錯誤消息。

這不僅僅是檢查模型上的字段,例如「所有帖子都必須有主題」,因爲雖然這是域的一部分,但它首先是輸入驗證,因此通過數據註釋處理在視圖模型上。

現在,控制器:

[HttpPost] 
public ActionResult Create(QuestionViewModel viewModel) 
{ 
    if (!ModelState.IsValid) 
    return View(viewModel); 

    try 
    { 
     // Map to ViewModel 
     var model = Mapper.Map<QuestionViewModel,Question>(viewModel); 

     // Save. 
     postService.Save(model); // generic Save method, constraint: "where TPost: Post, new()". 

     // Commit. 
     unitOfWork.Commit(); 

     // P-R-G 
     return RedirectToAction("Index", new { id = model.PostId }); 
    } 
    catch (Exception exc) 
    { 
     var typedExc = exc as DomainException; 

     if (typedExc != null) 
     { 
     // Internationalised, user-friendly domain exception, so we can show 
     ModelState.AddModelError("Error", typedExc.BusinessError.ToDescription()); 
     } 
     else 
     { 
     // Could be anything, e.g database exception - so show generic msg. 
     ModelState.AddModelError("Error", "Sorry, an error occured saving the Post. Support has been notified. Please try again later."); 
     } 
    } 

    return View(viewModel); 
} 

所以,通過我們得到的「保存」方法上的服務的時候,該模型已通過輸入驗證。然後Save方法調用post.Validate(),調用業務規則。

如果發生異常,控制器將捕獲並顯示消息。如果它通過Save方法併發生另一個錯誤(90%的時間,例如Entity Framework),我們將顯示一條通用錯誤消息。

正如我所說,不是每個人,但這對我們的團隊很好。我們有清晰的表示和域驗證分離,以及從原始HTTP POST到成功後重定向的一致控制流程。

HTH

+0

你會介意發表一個這樣的小例子嗎?特別是控制器方法。 – ignaciofuentes

+2

@NachoF - 如你所願,添加示例。 – RPM1984

0

通常您的視圖模型將包含對模型中的一個參考 - 一個視圖模型只需要如果你需要顯示在您的視圖其他信息不可用在你的模型中,有不需要複製已經存在的數據。

+0

那麼您所說的是,當您不需要業務對象本身不包含的額外信息時,您將業務模型對象傳遞給視圖? – ignaciofuentes

+0

正確,例如http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx Scott Guthrie將包含數據註釋的Person類型傳遞給他在控制器中的觀點。 – devdigital

+0

我剛剛閱讀了您已鏈接的文章,並且無法在任何地方看到Jimmy說'您不應將商業模式發送給您的觀點'。事實上,「不」這個詞不會出現在文章的任何地方,吉米說:「當然,這些觀點中的許多僅僅在我們項目的約束條件下才真正有效」,「這些觀點可能適用於或不適用於您的項目從事於。再次,模式都是關於權衡,好處和責任' – devdigital

4

元數據「好友」類正是這是什麼。驗證創建一次,但可以在兩個模型和視圖模型類使用:

public class PersonMetaData 
{ 
    [Required] 
    public string Name {get; set;} 

    [Required] 
    public string LastName {get; set;} 
} 

[MetadataType(typeof(PersonMetaData))] 
public class Person 
{ 
    public string Name {get; set;} 
    public string LastName {get; set;} 
} 

[MetadataType(typeof(PersonMetaData))] 
public class PersonFormViewModel 
{ 
    public string Name {get; set;} 
    public string LastName {get; set;} 
} 
+0

BUt在將整個業務類傳遞到View中並將其用作ViewModel來生成可編輯數據並將其重新提交給Controller並將其傳遞給ORM x 1000視圖一分鐘VS使用特定於ViewModel視圖並將該模型傳遞到業務中以轉換爲正確的數據。數據總是很繁忙,但好友類如何影響viewEngine中繁忙服務器的開銷?確定它易於輕鬆轉換,但是服務器抖動也很容易,因爲我們沒有意識到這些操作的含義。 – ppumkin

1

既然你總是傳遞的ViewModels你的觀點,你的域模型不是通過視圖暴露給最終用戶的話,我不要沒有看到驗證域模型本身的任何需要。例如,如果您通過表單發佈從視圖中接收到PersonViewModel,則只有在PersonViewModel本身有效時,纔會將其轉換爲人員模型(可能通過automapper等)。所以我相信輸入驗證應該保留在視圖模型中,因爲它們是必然要輸入的。

2

很好的回答,由RPM1984和漂亮的代碼示例。

我的觀點仍然是從一開始就使用Variant 2是生產力和結構之間的實用平衡,但在某些情況下,您總是必須願意轉向Variant 3,所以我主張將兩種方法混合使用邏輯。模式&然而,實踐總是在做變式3,因爲它是理想的關注點分離等,它避免了你在同一個解決方案中使用兩種方法,有些人不喜歡和我一起工作的許多客戶選擇變體3並運行適用於所有型號。

我認爲關鍵是RPM1984所說的 - 在您的視圖模型中重用您的業務實體對於重用驗證有用,但要記住,您的業務邏輯經常需要進行不同的驗證(例如,檢查該記錄不存在)。如果您使用Variant 3,它可以讓您將視圖模型驗證集中在視圖需求上(這需要花費額外的精力),但您總是需要某種業務邏輯驗證。

+0

如果你沒有什麼新的話,你應該考慮使用評論而不是答案,或者upvote。 –