13

這裏的情景:建議在POCO驗證與ASP.NET MVC /實體框架

  • ASP.NET MVC2 Web應用程序
  • 實體框架4(純POCO的,自定義數據上下文)
  • 庫模式工作模式
  • 依賴注入的
  • 單位
  • 服務層中介控制器 - >庫

所以基本上,所有的酷東西。 :)

事件

流動的基本UI操作( 「添加郵報」):

  1. 控制器調用添加(POST)在服務層方法
  2. 服務層調用添加(T )上儲存庫
  3. 庫調用ADDOBJECT(T)自定義數據上下文
  4. 控制器調用通訊它()在工作單位

現在,我試圖找出我可以把我的驗證。

在這個階段,我需要兩種類型的驗證:

  1. 簡單,獨立的POCO驗證,如「後必須有一個標題」。這似乎是POCO上的Data Annotations的自然選擇。
  2. 複雜的業務驗證,例如「無法將註釋添加到鎖定的帖子」。這不能通過數據註釋完成。現在

,我一直在讀「編程實體框架,第二版」,由朱莉·勒曼(這是極好的BTW),並一直在尋找到鉤住SavingChanges事件,以便進行「最後一分鐘」驗證。這將是一個很好的方法來確保驗證總是每當我做「某事」(添加,修改,刪除)時發生,但它也有點遲了國際海事組織(因爲項目已經在狀態管理器) - 所以我能如果驗證失敗,請刪除它們?

我當然可以讓我的POCO實現一個接口(稱爲「IValidatable」),並在此事件期間在此接口上調用一個方法。

但是,這似乎「爲時已晚」的業務驗證 - 這是共識嗎?

我基本上在這裏尋找指導,我試圖設計一個可複用的,複雜的業務邏輯的智能驗證方案,鑑於我的上述架構。如你所知,POCO的EF意味着POCO具有DB上的所有屬性 - 所以我可能有一個「PostID」屬性,帶有get/set訪問器(因爲EF需要獲取/設置這些屬性)。

但問題是,「PostID」是一個身份列,所以我如何保護該領域從明確設置?例如,如果我(出於某種原因)執行以下操作:

var post = service.FindSingle(10); 
post.PostId = 10; 
unitOfWork.Commit(); 

這將拋出SqlException。我怎樣才能防止這個?我不能「隱藏」這個屬性(因爲它是私有的,甚至是內部的),因爲POCO正在與Repository進行單獨的彙編。

有關驗證的說明 - 我打算創建自定義異常(從異常派生)。所以當驗證失敗時,我需要拋出這些異常。

這樣的話,我可以在我的控制器上的代碼是這樣的:

[HttpPost] 
public ActionResult AddPost(Post post) 
{ 
    try 
    { 
     IUnitOfWork uow = new UnitOfWork(); 
     postService.Add(post); 
     uow.Commit(); 
    } 
    catch(InvalidPostOperation ipo) 
    { 
     // add error to viewmodel 
    } 
} 

我將不得不手工做服務層每次我做一個添加驗證?那我該如何處理保存? (因爲這是工作單元而不是服務層)。

因此,爲了防止這種從一個「所有的地方」的問題,這裏是我的問題:

  1. 簡單的POCO驗證 - 這應該與數據註釋做了什麼?優點/缺點/陷阱?
  2. 在什麼情況下(如果有的話)我們是否應該掛鉤EF Data上下文的SavingChanges事件以提供驗證?
  3. 我應該在哪裏進行復雜的業務驗證?在服務明確,或在POCO的(我可以從服務呼叫)的方法。我如何創建一個智能/可重用計劃?
  4. 我們怎樣才能「隱藏」POCO自動生成的屬性被篡改?

任何想法將不勝感激。

道歉,如果這篇文章「太長」,但它是一個重要的問題,可以通過很多方式解決,所以我想提供所有信息,以獲得最佳答案。

謝謝。

編輯

下面的答案是有幫助的,但我還是比較理想的狀況尋找更多的想法。還有誰?

+0

其中一個問題太多,但關於#3,請記住驗證是上下文敏感的(或基於任務的)。在'Post'上創建一個'IsValid'屬性是很有誘惑力的,但是你需要一個'IsValidForDraft'來保存缺少某些信息的草稿。當然,POCO不應該意識到「草案出版」的概念。 :) – bzlm 2010-10-07 06:25:37

+1

唉來吧,爲什麼要結束投票?我意識到它是一個「長」的問題。但這就是爲什麼我最終得到了4個問題。僅僅因爲我問了4件事情,並不意味着這個Q值得投票結束,因爲「太模糊」。所有4個q都涉及到一個主題。 – RPM1984 2010-10-07 06:41:52

+0

@bzlm - 是的,明白了你的觀點。 :) – RPM1984 2010-10-07 06:42:51

回答

1
  1. 好像你說的,DataAnnotations不適合所有情況。根據我的經驗,缺點主要是複雜的驗證(多屬性和多屬性不同的對象)。
  2. 如果我是你,我會盡可能地離開數據層(EF)的業務/域驗證。如果有數據層驗證方案,那麼很好(例如,驗證複雜的父/子關係 - 這純粹是數據庫的東西)。
  3. 是的,複雜的業務驗證應該位於服務層或模型對象中(通過部分類或一些繼承方法附加:接口/派生類)。 ActiveRecord人員,Repository Pattern人員和DDD人員之間對此存在爭議,但隨着適合您的做法很簡單,並且可以實現快速部署和低成本應用程序維護。這是一個簡單的example of how you might attach more complex validation to domain objects,但仍然與DataAnnotations接口兼容,因此是'MVC友好'。
  4. 好問題。 - 我還沒找到解決方案,我100%滿意。我玩過私人定製者的想法,並不是很好。請快速閱讀此summarized Evans DDD book。它很好的快速閱讀,它可以提供一些有關模型對象和值對象的目的和區別的見解。這就是我認爲對象設計可以減輕你對房產「篡改」(如你所說的)的問題,但卻沒有修復房產的可見性。也就是說,另一種解決方案可能在別處。希望這可以幫助。
+0

謝謝,我正在下載並看看那本書。我對POCO的基本數據註釋非常滿意,對複雜驗證更感興趣。這個例子非常有趣(而且聰明)會考慮到這一點。與4)不能讓它私人。正如我所說,POCO是在一個組合中(沒有EF參考),EF/repo在另一個組合中。所以它需要公開。我認爲有一種方法可以說「setter只適用於特定的程序集」。事情是,我只希望存儲庫具有對PostId屬性的「設置」訪問權限。 – RPM1984 2010-10-07 02:25:18

+0

你認爲這個可見性修飾符是'Internal',它只適用於我認爲的類(98%?)。不屬性。對不起,我無法幫助#4,這就是爲什麼我建議這本書。我也有同樣的擔憂。有一件事可能對你有幫助嗎?你有沒有考慮過沒有在一個單獨的大會?我曾經擁有所有我的解決方案,其中包含獨立的數據,服務,測試和MVC/Web項目,直到我認爲這是太多的維護開銷。現在我在一個項目/程序集中擁有數據/模型/服務。對我來說更容易,我仍然可以得到我需要的相同的邏輯分隔。嘗試一下。 – 2010-10-07 02:29:39

+0

我可能是錯的,[你可能能夠將一個類型成員設置爲'Internal'](http://msdn.microsoft.com/en-us/library/7c5ka91b.aspx) – 2010-10-07 02:43:41

1

嘿,也許有點晚了,但在這裏不用反正...

這一切都取決於你的架構,即是否有一個合乎邏輯的分離 - 在你的應用程序:用戶界面,服務層,庫層。如果您正在勾選Save事件,那麼該做什麼?從我觀察到的情況來看,你會調用存儲庫Layer for Persistance only right?但是,您正在掛鉤保存事件,將控制權交還給服務層/業務層,然後通過正確的方式強制保存?

我個人覺得在服務層/業務層應該在完成照顧它然後說,嘿,先生回購層 - >保存此對象。

關於驗證,Data Annotations應該與UI一起使用,所以簡單的賦值如[Required]等,這對客戶端驗證會有幫助,但複雜的業務邏輯或複雜的驗證應該掛鉤到服務層/業務層,這樣它可以在所有實體/對象/ POCOS等中重用。

關於防止某些私有字段不被篡改...只允許您的服務層/業務層實際設置對象將堅持(是的,我的意思是... :))手工進行編碼的,我覺得這對我來說是最安全的選擇,無論如何,我會簡單做:

var updatedpost = _repo.GetPost(post.postid); 
updatedpost.comment = post.comment; 
updatedpost.timestamp = datetime.now; 

類的buseinss層需要控制浪費,但這種方式,不過這只是我的經驗,我可能是錯的,我也讀了不少成模型綁定,validaiton和其他的東西卻似乎有這樣的情況,事情從來沒有像工作預期例如[必需的]屬性(請參閱Brad WIlson的)文章。