EF实体与服务模型与视图模型(MVC)

时间:2011-02-25 15:54:04

标签: asp.net-mvc entity-framework viewmodel

我正在尝试理解和设计您的应用/域模型(POCO / DTO)的良好实践。

假设我有以下数据库表,帐号:

UserID int
Email varchar(50)
PasswordHash varchar(250)
PasswordSalt varchar(250)

当然,EF4会像这样构建实体:

public class Account
{
    public int UserID { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    public string PasswordSalt { get; set; }
}

现在,假设我有一个用于注册新用户的视图模型,可能看起来像这样:

public class RegistrationViewModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

最后,我有一项需要注册用户的服务:

public class RegistrationService
{
    public void RegisterUser(??? registration)
    {
        // Do stuff to register user
    }
}

我正在试图弄清楚要传递给RegisterUser方法的内容。当然,视图模型位于我的Web应用程序(表示层)下,因此我不希望将其传递给我的服务。

所以,我正在考虑四种可能性之一:

1)设置与RegistrationViewModel类似(如果不相同)的服务模型,并使用:

public class RegistrationServiceModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

public class RegistrationService
{
    public void RegisterUser(RegistrationServiceModel registration)
    {
        // Do stuff to register user
    }
}

2)设置模型的接口,并在我的视图模型中继承它,并设置我的方法接受接口:

public interface IRegistrationModel
{
    string Email;
    string Password;
}

public class RegistrationServiceModel : IRegistrationModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

public class RegistrationService
{
    public void RegisterUser(IRegistrationModel registration)
    {
        // Do stuff to register user
    }
}

3)传入Account实体,在我的控制器中执行RegistrationViewModel-to-Account映射:

public class RegistrationService
{
    public void RegisterUser(Account account)
    {
        // Do stuff to register user
    }
}

4)将我的视图模型从演示文稿移到域/服务层,并将其传递给服务方法:

public class RegistrationService
{
    public void RegisterUser(RegistrationViewModel account)
    {
        // Do stuff to register user
    }
}

这三种情况都不是理想的,因为我看到每种情况都存在问题。所以我想知道是否有另一种我无法想到的方法。

有什么好的做法?

提前致谢。

3 个答案:

答案 0 :(得分:9)

确定使用第三次选项。正如šljaker所说,服务应该不知道应用程序的呈现部分(您的ViewModel是其中的一部分)。

当然,也不要通过包含大量过渡模型(如RegistrationServiceModel)或 - 甚至更糟 - IRegistrationModel(最后一个会导致“界面爆炸”一天)来使周围的事情过于复杂化。

所以:

  1. 拥有一个域实体(POCO实体与实体框架或NHibernate或NoRM或其他任何内容持久保存)。
  2. 拥有一个ViewModel,它代表您在给定上下文中的域模型。如有必要,请不要犹豫,按照每个控制器操作ViewModel。严格的ViewModel(与您的View一致1:1)的副作用优点是完全没有过度发布和欠发布问题。这取决于你的具体情况/品味。
  3. 将DataAnnotation属性与ViewModel一起使用以提供基本验证(记住也要验证业务规则,但它应该位于电线后面 - 服务/存储库层内)。
  4. 不要让App Service了解ViewModels。创建域实体实例并将其提供给服务(以验证/持久化)。
  5. 使用AutoMapper作为快速映射域实体到ViewModel的选项。
  6. 通过“控制器”操作或自定义ViewModel将传入的FormCollectionIModelBinder映射到您的实体。
  7. (可选)我建议您关注Thunderdome Principle。这是一个非常真正方便的ViewModels用法。

答案 1 :(得分:8)

您永远不会将视图模型传递给服务。服务甚至不知道您可能已在表示层中定义的视图模型的存在。服务适用于域模型。
使用Auto mapper在视图模型和域模型之间进行映射,反之亦然。

就个人而言,我从未听说过DDD中的服务模型(服务的视图模型)。

答案 2 :(得分:3)

在这种情况下,使用DTO(数据传输对象)非常有意义。您可以在服务层创建一个AccountDto类,并使用它将注册数据传递给服务。在某些情况下,它可能类似于ViewModel,但通常您可以在View中显示比创建用户所需的更多内容。为了进一步说明这一点,您的ViewModel可能至少看起来像这样:

public class RegistrationViewModel
{
    [Required]
    public string Email { get; set; }

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

    [Required]
    [Compare("Password")]
    public string RepeatPassword { get; set; } 
}

虽然您的DTO只需要EmailPassword属性。

public class AccountDto
{
    public string Email { get; set; }
    public string Password { get; set; }
}

如您所见,ViewModel仅包含View所需的数据。电子邮件验证和密码比较逻辑发生在Web层中。您使用DTO只获取服务的电子邮件和密码。然后在服务层您散列密码,填充您的Entity对象并将值保存到数据库。