我应该如何将我的商业模式与我的观点联系起来?

时间:2013-12-10 12:53:29

标签: c# asp.net-mvc-4 mvvm automapper abstraction

我在开发过程中遇到了一个有趣的问题。现在,我使用一个与数据库无关的工作层单元来抽象我的ASP MVC 4 Web应用程序中实际数据库依赖项的数据访问。

实现工作单元接口的每个单独的数据库项目都知道我的业务模型(直接进出数据库的模型)。我不太确定我对这种方法的看法,但这不是我要问的问题。

我是否应该使用像AutoMapper这样的解决方案将我的商业模式转换为域模型/从域模型转换 - 模型传递给视图并用于任何不应该访问数据库字段的工作(即ID) ?

例如,考虑我的BusinessModels项目,我有以下类

BusinessModels
    /UserAccounts/
         User.cs
           - ID
           - Username
           - HashedPassword
           - Salt
         UserSettings.cs
           - IsSubscribedToNewsletter
           - AllowDirectEmails

使用AutoMapper将这些User和UserSettings模型绑定到单个模型中是否有意义

MyProject
   /DomainModels/
       User.cs
          - Username
          - HashedPassword
          - Salt
          - IsSubscribedToNewsletter
          - AllowDirectEmails

出于观点的目的?

这个问题也延伸到非MVC项目,但我觉得当我正在研究一个MVC项目时,在该标签中询问它会更有意义。

TLDR在映射业务模型/实体以查看模型方面是否有任何意义,或者是否提供了不必要的抽象层?如果是这样,存储库是否包含业务模型或视图模型(它们会自动映射到引擎盖下的业务模型)?

2 个答案:

答案 0 :(得分:1)

关于viewmodels我将它们视为您希望使用的数据的摘要。

因此,从您的示例中,您的viewmodel将包含来自UserUserSettings类的数据。假设您有一个名为UserData.cshtml的视图,那么我会像这样编写代码:

public class UserDataViewModel
{
    public string Username { get; set; }
    public bool AllowDirectEmails { get; set; }
    // etc ...
}

public ActionResult UserData()
{
    var viewModel = new UserDataViewModel();

    viewModel.UserName = "Whatever";
    viewModel.AllowDirectEmails = false;

    // Or however you get the data for the user.....

    return View(viewModel) 
}

希望你明白了。因此,您可以将来自外部类的信息合并到一个viewmodel类中。基本上在viewmodel类中将所有内容绑定在一起。

我将viewmodel类命名为与将要使用的视图相同的名称。这可以帮助编写文档,并使新手代码更容易使用。

答案 1 :(得分:1)

您可以将视图模型用于两个不同的事物:

  • 呈现新视图(GET操作),将视图模型对象作为视图模型传递
  • 使用视图模型作为参数
  • ,在Post Action(POST操作)中从视图接收数据

(我知道,第二个是有争议的。但是使用视图模型并不奇怪)

GET操作的模型需要渲染视图所需的所有属性:

  • 您正在展示/编辑的实体的值
  • 呈现视图所需的额外值(例如,用于下拉列表的SelectLists)

假设您有一个可以属于一个UserGroup的用户。

在这种情况下,如果要编辑用户,模型需要:

  • 用户数据
  • 用户组列表

我会使用这样的模型:

public class EditUserModel
{
   public User User {get;set;}
   public SelectList UserGroups {get;set;}
}

如您所见,我直接将User添加为属性。但我不会将类别列表添加为属性,因为我不需要整个类别列表,并且视图中包含所有属性。此外,如果您对控制器进行单元测试,则可以验证SelectList是否符合预期(如果您在视图中创建了“用户组”列表,则无法完成)

但是,如果您不需要View中用户的所有属性,该怎么办?是否值得删除User属性,并为Name,Email,JoinedData,Active ...添加单独的属性?我认为anser是NO。想象一下,您添加/删除或重命名某些用户实体属性。如果在视图模型中有单独的属性,则在更新视图之前,您还必须更改它们。并且,如果您依赖自动映射(自动映射器,值注入器),您甚至不会意识到是否犯了一些错误。

我还说视图模型可用于将数据发布回控制器。所以你可以这样做:

[HttpPost]
public ActionResult Edit(EditUserModel userModel)

如果这样做,模型绑定器将使用表单控件中的值填充userModel。所以你会回到半空的模型。在这种情况下,UserGroups列表将为null,并且,根据您编辑的用户属性的数量,User也可以具有许多null / non-initialized属性。

为了避免出错,在某些情况下建议创建一个不同的模型(可能还有辅助类),以明确预计会发布到模型的内容。

例如,如果您有一个操作来显示整个用户数据,但只允许更改其密码,则可以创建一个具有两个属性的类:Password和PasswordConfirmation。

在这种情况下,POST的视图模型只能有Password和PasswordConfirmation。并为具有此继承属性的GET导出模型,以及用户组和用户列表。

为什么继承而不使用独立类?简单地说,当您使用Html.TextBoxFor(m => m.User.Name)之类的东西时,只有当post动作的参数具有相同的结构时,Model Binder才能设置User属性的Name属性。即如果get的视图模型具有以下结构:

public ChangePasswordModel
{
   public string Password {get;set;}
   public string PasswordConfirmation {get;set;}
   // extra properties, like the list of user groups, the user data...
}

帖子的模型有这样的结构:

public PostedChanegPasswordModel
{
   public User User {get;set;}
}

Html.TextBoxFor(m => m.EditedUser.Name)呈现的输入内容不会绑定到PostedEditViewModel的User.Name

但如果你这样做:

public EditUserModel : PostedEditUserModel
{
   // extra properties, like the list of user groups
}

数据绑定没有任何问题。

通常,您必须小心使用用于发布和获取的模型。我建议使用尽可能多的不同视图模型。

何时使用自动属性映射到全新视图和不同模型?

您必须有充分的理由拥有不同的视图模型。这可能是从外部启动应用程序(即首先设计)的结果,或者是因为团队在实施业务逻辑之前或期间开发UI。

在这种情况下,您可以发现视图模型的类和视图本身已经定义,并且与您的实体非常相似,但不完全相同。当我认为使用映射器时,这是一个例子。

使用不同类的另一个原因是将接口与逻辑分离。但这通常只发生在前一种情况中。