2012-03-13 131 views
4

讓我先說這個問題,並說明使用實體框架不適合我們。企業中企業實體驗證的首選方法

在我們的財務組織中,我們有跨越解決方案的業務實體。有些人擁有其他人不需要的界面。驗證和業務規則必須包含在實體中。

我針對爲我生成的DAL和DTO編寫代碼,這些DAL使用procs在數據庫上運行CRUD(可能是SQL可能是Oracle)。

因此,當我創建MVC,WCF,控制檯應用程序等。如果可以實現更好的驗證方法,問題一直在嘮叨。

這裏是一個實體對象一個典型的幾個特性:

[DefaultValue("")] 
public string Branch { 
    get { return _branch; } 
    set { 
     if (value != null && value == _branch) return; 
     const string propertyName = "Branch"; 
     ValidationInstance.Clear(propertyName); 
     ValidationInstance.ValidateRequired(propertyName, value); 
     ValidationInstance.ValidateNumeric(propertyName, value); 
     ValidationInstance.ValidateLength(propertyName, value, 2); 
     _branch = value; 
     if (EntityState != EntityStateType.New) 
      EntityState = EntityStateType.Changed; 
    } 
} 

    [DefaultValue(0)] 
public decimal HighDefermentMargin { 
    get { return _highDefermentMargin; } 
    set { 
     if (value == _highDefermentMargin) return; 
     const string propertyName = "HighDefermentMargin"; 
     ValidationInstance.Clear(propertyName); 
     ValidationInstance.ValidateRange(propertyName, value); 
     _highDefermentMargin = value; 
     if (EntityState != EntityStateType.New) 
      EntityState = EntityStateType.Changed; 
    } 
} 

正如你可以看到有進行越來越詳細的驗證數據註釋和顯式調用驗證類的組合。

在一個MVC應用程序中,我們在ViewModel上苦心地複製了驗證,因此我們獲得了客戶端和服務器端的驗證。這裏是同一屬性的視圖模型版本從上面:

[Required] 
[Range(0.0, 99.99)] 
[Display(Name = "High Deferment Margin")] 
public decimal HighDefermentMargin { get; set; } 

這裏的主要區別是,在實體驗證加載錯誤插入的驗證類的錯誤集合,可以在當時的實體進行查詢去拯救自己。如果(!IsValid)則拋出一個包含錯誤數組的自定義異常。控制器通過它們循環並將它們添加到ModelState中。

我開始研究一些幾乎有幾百個字段的類。即使它們被面向對象分解,領域的數量仍然非常高。這些都是貸款認證等,對於單個記錄有很多數據。必須寫出對許多屬性的驗證使我想吐。我不能只編寫一個實用程序來生成實體和驗證,因爲業務規則是驅動驗證的原因,而不是數據庫。這意味着一個字段在數據庫中可以爲空,但不允許根據業務規則保留爲空值,或者該字段可以爲空,但只有在單獨的字段具有值時纔是空的等。

所以,可以使用just View Model中的數據註釋與實體的實現方式是否相同?我可以爲非標準驗證編寫自定義驗證器,然後爲更復雜的內容編寫業務規則。驗證錯誤是否會從實體中獲得更高的級別,以便UI可以像ModelState一樣以友好的方式通知用戶?其他人在這種情況下做什麼?

+0

我有一個建議。在遇到無效輸入或在通過業務規則引擎進行驗證時規則未滿足的任何情況下,只需冒泡該異常,以便可以在Controller中捕獲該異常,然後將其添加到modelstate的錯誤集合中。例如:拋出一個自定義的異常,並且不要在規則引擎中捕獲該異常,只需重新拋出,直到它到達控制器。然後使用Html.ValidationSummary()在UI中顯示。 – Saravanan 2012-03-16 03:50:01

+0

這樣做會奏效 - 但這個想法是爲了避免評估集合中的屬性或獲取並提供更多的AoP設計模式。我已經在房產中找到了收集錯誤的策略。所有拋出異常都會提供一種不同的方式來向調用者顯示錯誤。這只是交換不同代碼的代碼。我想消除屬性中的代碼。 – 2012-03-16 14:09:53

+0

內置的類型默認爲必需,您不需要添加該屬性。 – RickAndMSFT 2012-03-22 17:07:05

回答

3

通常,屬性上的驗證屬性可以獲得不同的驗證結果(驗證是否通過),具體取決於它是在UI層,BUsiness層還是DAL中評估的。考慮一個Required屬性,如果應用於ViewModel,它可能會失敗,但在業務層中,它可以通過,因爲用戶沒有提供的值是從其他源提供的。然而,像電子郵件格式這樣的驗證規則總是會得到相同的結果。但是,存在一些可能需要在業務層重複驗證的成熟問題......僅僅是因爲Web服務器更容易受到攻擊並且更容易受到攻擊。但是,重複兩次相同的驗證並不意味着要寫兩次代碼。您可以收集您想要應用於同一概念實體的各種版本(ViewModel,BL,DAL)的常用dll中的多個圖層中應用的所有屬性,其中您定義了MetaDataType。這樣,您可以在不重複代碼的情況下滿足安全要求。

至於建議的saravanan你可以使用異常通信在其它層UI層發現的驗證錯誤(還可以配置WCF通信異常細節給客戶端)

+0

這是一個有趣的策略。我將不得不更多地瞭解MetaDataTypes,看看它是否能夠實現我期待的目標。不知道這是否會成爲問題,但我的ViewModel通常是多個實體的子集,並且不映射1:1。假設我想要一個強化實體即使通過UI重新驗證也是正確的。例如,如果用戶界面受到影響並且ViewModel將實體設置爲「活動」狀態。企業中的業務規則和驗證將評估和否定攻擊。我需要看到一些很好的替代策略的具體例子。 – 2012-03-16 14:04:15

+0

「ViewModels通常是多個實體的子集並且不映射」。 您可以將Just 1 MetaDataType應用於每個對象,因此我建議這樣做:不是將幾個對象的屬性混合到一個唯一的根類中,而是定義一個根ViewModel。這種根ViewModel的每個屬性都包含一個子視圖模型,其屬性是JUST ONE BL對象屬性的一個子集。這樣每個ViewModel子可能具有的是不同的MetadataType。 屬性是幾個屬性的組合(例如,價格總和直接放在根模型中或您想要的值) – 2012-03-16 14:42:05

+0

這樣,每個子ViewModel都具有應用於其中的MetadataType中包含的屬性的子集。 ...如果metadatatype無法在所應用的對象中找到ALL ITS PROPERTIES,則會出現一種設計錯誤,它會拋出一個異常(難以理解爲什麼?)。但是,我寫了一個自定義的DataAnnotationsMetaDataprovider,它可以克服這個問題我把它放在我的開源項目「Mvc Controls Toolkit」中,但是如果你只需要DataAnnotationMetaDataProvider,我可以給你發一個獨立的文件,你可以在你的項目中安裝 – 2012-03-16 14:53:50