簡體   English   中英

ASP.NET MVC架構:ViewModel的組合,繼承還是重復?

[英]ASP.NET MVC Architecture : ViewModel by composition, inheritance or duplication?

我正在使用ASP.NET MVC 3和Entity Framework 4.1 Code First。

假設我有一個User實體:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }        
}

在我的UserController編輯它時,我想添加一個PasswordConfirmation字段並驗證PasswordConfirmation == Password

1.通過構圖

我的第一次嘗試是:

public class EditUserModel
{
    [Required]
    public User User { get; set; }

    [Compare("User.Password", ErrorMessage = "Passwords don't match.")]
    public string PasswordConfirmation { get; set; }
}

在這種情況下客戶端驗證 工作,但 編輯:客戶端驗證工作是巧合。) 不起作用服務器端驗證失敗,並顯示以下消息: 找不到名為User.Password的屬性

編輯:我認為在這種情況下,最好的解決方案是創建自定義CompareAttribute

實現IValidatableObject

public class EditUserModel : IValidatableObject
{
    [Required]
    public User User { get; set; }
    public string PasswordConfirmation { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if(this.PasswordConfirmation != this.User.Password)
            return new[] { new ValidationResult("Passwords don't match", new[] { "PasswordConfirmation " }) };

        return new ValidationResult[0];
    }
}

在這種情況下, 服務器端驗證工作,客戶端驗證不再起作用 實現IClientValidatable似乎有點過於復雜,我不喜歡在這種情況下進行客戶端驗證。

2.通過繼承

public class EditUserModel : User
{
    [Compare("Password", ErrorMessage = "Passwords don't match.")]
    public string PasswordConfirmation  { get; set; }
}

當嘗試使用EF直接保存EditUserModel它不起作用時,我得到一些關於EditUserModel元數據的錯誤消息,所以我使用AutoMapperUser轉換為EditUserModel並向后轉換。 這個解決方案有效,但它更復雜,因為我必須從模型轉換為視圖模型並向后轉換。

3.通過復制

(Malte Clasen建議)

視圖模型將具有模型的所有屬性以及其他屬性。 AutoMapper可用於從一個轉換為另一個。

public class EditUserModel {    
  public string Name { get; set; }    
  public string Email { get; set; }    
  public string Password { get; set; }   
  [Compare("Password", ErrorMessage = "Passwords don't match.")]     
  public string ConfirmPassword { get; set; }        
}

這是我最不喜歡的解決方案,因為代碼重復(DRY)

問題

在這種情況下,繼承,組合和重復的利弊是什么?

是否有一種簡單的方法可以同時進行客戶端和服務器端驗證,而無需將模型轉換為視圖模型並向后轉換?

在討論過這個問題之前,我已經在各種情況下與這三個問題一起消失了。 總的來說,我見過的大多數觀點都支持MVC項目中的重復,並且ViewModel專門為每個視圖構建。 以這種方式,您使用的約定類似於UserDetailsViewModelUserCreateViewModel 正如您所說,在那時,AutoMapper或其他一些自動映射工具將用於從您的域對象轉換為這些平面ViewModel。

雖然我也不喜歡重復代碼,但我也不喜歡用驗證或其他特定於視圖的屬性來污染我的域對象。 另一個優點是,盡管幾乎沒有人會不得不與之抗衡(無論所有專業人員都說過),但是你可以在某種程度上操縱你的域對象而不必操縱你的ViewModel。 我之所以提到這一點,是因為它通常被引用,而不是因為它對我來說很重要。

最后,使用真正平坦的ViewModel可以實現更清晰的標記。 當我使用合成時,我經常創建錯誤,創建名稱類似於User.Address.Street HTML元素。 平面ViewModel至少減少了我這樣做的可能性(我知道,我總是可以使用HtmlHelper例程來創建元素,但這並不總是可行的)。

無論如何,我最近的項目也幾乎需要單獨的ViewModel。 它們都是基於NHibernate的,並且在NHibernate對象上使用代理使得無法直接將它們用於視圖。

更新 - 這是我過去提到的一篇好文章: http//geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx

您也可以考慮域和視圖模型的獨立類,例如,在這種情況下

public class EditUserModel {    
  public string Name { get; set; }    
  public string Email { get; set; }    
  public string Password { get; set; }        
  public string ConfirmPassword { get; set; }        
}

如果Id存儲在url中。 如果您想避免在User和EditorUserModel實例之間進行手動復制, AutoMapper可以為您提供幫助。 這樣,您可以輕松地將視圖模型中的密碼字符串與域模型中的密碼哈希分離。

我試圖解決這個問題,我找到了一個不涉及重復代碼的解決方案。 這是一種解決方法,但在我看來,它比其他提議的解決方案更好。

您擁有所有驗證的用戶模型:

public class UserModel
{
    [Required]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }        
}

您使用新模型組合以前的模型

public class EditUserModel
{
    public UserModel User { get; set; }

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

訣竅在於動作,你可以收到多個模型:

[HtttPost]
public ActionResult UpdateInformation(UserModel user, EditUserModel editUserModel) {
    if (ModelState.IsValid) {
         // copy the inner model to the outer model, workaround here:
         editUserModel.User = user
         // do whatever you want with editUserModel, it has all the needed information
    }
}

通過這種方式,驗證按預期工作。

希望這可以幫助。

我沒有太多使用實體模型,我更喜歡LINQ-SQL模型,所以這可能是不正確的:

為什么不使用應用於實體的元數據類? 使用LINQ-SQL,客戶端和服務器端驗證都會考慮分配的元數據。

據我所知,[MetaDataType]屬性的應用類似於繼承,只有它可以在不實現新類(模型)的情況下對基本實體進行更改。

另外,您可能想要嘗試的另一個選項是創建自定義屬性 - 出於類似目的,我這樣做了一次。 基本上是一個表示成員持久性的標志。

所以我的實體定義如下:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }     

    [DoNotPersist]   
    public string ConfirmPassword {get; set;}

}

另外,我不知道你在做什么來存儲數據,但是我已經在我的DataContext的OnInserting,OnEditing,OnDeleting函數中掛了一個覆蓋,它基本上刪除了任何擁有我的自定義屬性的成員。

我喜歡這種方法很簡單,因為我們為每個模型使用了大量臨時的算法數據(為商業智能構建了良好的UI),這些數據沒有保存在數據庫中,而是在模型函數,控制器等中的任何地方使用 - 所以我們使用依賴注入所有模型存儲庫和控制器,因此我們為每個表提供所有這些額外的數據點。

希望有所幫助!

PS: - 組合與繼承 - 它實際上取決於應用程序的目標用戶。 如果是內部網應用程序,其中安全性不是問題,並且用戶/瀏覽器環境受到控制,那么只需使用客戶端驗證,即:組合。

我贊成合成而不是繼承。

在您的用戶密碼的情況下,看起來您實際上是以明文形式將密碼存儲在Users表中,這非常非常糟糕。

您應該只存儲鹽漬哈希,並且您的EditUserModel應該有兩個字符串屬性用於密碼和密碼確認,這些屬性不是表中的字段。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM