ASP.NET MVC 中的视图模型和域模型
本文关键字:模型 视图 NET MVC ASP | 更新日期: 2023-09-27 18:31:42
我正在开发一个 ASP.NET 的MVC应用程序,我遇到了一个非常尴尬的境地。
我有一个页面,用户可以在其中按某些条件搜索某些项目(例如,学生)。我曾经将一组学生传递给我的视图,但后来我添加了一些设置来搜索,所以我决定创建 ViewModel,如下所示
public class SearchViewModel
{
public string SearchString { get; set; }
public bool IsCaseSensitive { get; set; }
...
//other parameters
...
public IEnumerable<Student> Students { get; set; }
}
然后我想到了我必须为每个学生添加一些附加信息的情况,这些信息不存储在数据库中,而是在控制器上生成。我的第一个想法是相当愚蠢的 - 我添加了一个额外的数组来将其保存在 ViewModel 中,如下所示:
public class SearchViewModel
{
public string SearchString { get; set; }
public bool IsCaseSensitive { get; set; }
...
//other parameters
...
public IEnumerable<Student> Students { get; set; }
public IEnumerable<int> someData { get; set; }
}
因此,要获取数据,客户端代码必须获取学生在数组中的位置,然后转到 someData 数组中的相应位置。
我不太喜欢这个想法,所以我将我的模型更改为包含搜索参数 + 一个额外的模型来保存学生对象及其数据。
public class SearchViewModel
{
public string SearchString { get; set; }
public bool IsCaseSensitive { get; set; }
...
//other parameters
...
public IEnumerable<StudentViewModel> StudentModels { get; set; }
}
public class StudentViewModel
{
public Student Student { get; set; }
public int someData { get; set; }
}
创建像StudentViewModel这样的"帮助程序"模型是个好主意吗?我可能不会在任何地方使用StudentViewModel,而是在SearchViewModel内部。鉴于SearchViewModel本身就是一种"帮助"模型,创建另一个仅在SearchViewModel内部使用的模型似乎有点奇怪。是这样吗?
据我所知,ViewModel 永远不应该包含域模型。我应该将学生财产划分为较小的财产吗?例如,像这样:
public class StudentViewModel { public string Name { get; set; } public int GroupId { get; set; } public int someData { get; set; } }
一般来说,正如我所理解的,ViewModel 应该只包含基元及其集合(可能还有其他 ViewModels,如第一个问题)。正确吗?
我不假装这个不可侵犯的真理。但在我看来:
- 通常,视图模型只为一个视图创建。
- 在视图中使用域/数据模型通常是(如果它使开发过程更容易/更快/更清晰)。在您的情况下,我不会在视图模型中使用学生类。
- ViewModel 应包含构建视图所需的所有数据(基元和非基元)。但是,如果您可以通过在控制器中执行一些工作来使视图模型更容易、更清晰,那么应该完成它。
- 创建像StudentViewModel这样的"帮助程序"模型是个好主意吗?
我相信是的。您的视图"包含"一系列"学生观点"。视图模型的存在只是为了向视图提供以最佳方式结构化的数据,以便视图中的必要逻辑尽可能少。
- 据我所知,ViewModel 永远不应该包含域模型。
我建议不要在视图中使用您的实体,尤其是在处理表单或其他数据输入方式时。在这种情况下,创建一个特殊的"表单模型"通常很有趣,该模型仅包含您希望用户提交的属性。这还允许您为此特定表单编写验证逻辑。将其视为为窗体提供服务的模型,类似于视图模型为视图提供服务的方式。使用自动映射器填充这些表单模型,让您的生活更轻松。
但是,在某些情况下,我发现在您的视图中使用实体是可以接受的:使用 EF 和延迟加载时,可以在显示数据时使用实体,以便从延迟加载机制中受益。只有您显示的数据才会从数据库中加载。但是,由于几乎总是事先知道您需要哪些数据,因此通常有更好的数据加载策略。在开发过程中,当发生大量更改时,延迟加载很方便(加载和显示附加数据快速且易于实现,无需更改太多代码),但通常是性能评估期间要解决的第一件事。
- 我应该将学生财产划分为较小的财产吗?一般来说,正如我所理解的,ViewModel 应该只包含基元及其集合(可能还有其他 ViewModels,如第一个问题)。正确吗?
我不同意。您设计了表示业务对象的域,为什么要在视图模型中再次将其丢弃?始终考虑将来需要使用额外属性扩展域模型的可能性,以及您现在可以做些什么来降低此类更改的成本。如果要将域层与表示层分开,请引入包含相同对象的 DTO 层。在这些对象上,您可以应用数据注释和其他仅适用于表示层的内容。使用AutoMapper可以快速完成从域对象填充DTO。但是,请注意不要在这些 DTO 上/使用这些 DTO 编写您的业务逻辑。
这是我的2美分。