20

我有一个包含以下文件夹的 asp.net-mvc 网站:

  • 控制器
  • 脚本
  • 意见
  • 视图模型
  • 楷模
  • 领域模型

我现在想在另一个 .net 应用程序(一个 Windows 控制台应用程序,所以根本不是 Web)中访问很多这种业务逻辑和数据库访问代码和数据,所以我正在重构以尽可能多地删除 MVC 项目之外的东西和到解决方案中的其他项目中,以便可以与其他解决方案共享代码。

我有两个主要问题;

  1. 我的主要问题是我正在努力寻找一个放置生成 ViewModel 的代码的地方,因为我想在我的控制台应用程序中重用这些代码,因为控制台应用程序发送的电子邮件需要与看法。

  2. 另一个主要问题是,当我的许多实例化我的视图模型的函数都以一堆数据库访问代码开始时,我正在努力了解如何将我的数据库访问代码移出 MVC 项目,同时仍然拥有 ViewModel。

这是到目前为止的一些细节和我的过程:

第 1 步 - 将 DomainModel 移动到另一个项目中 - 成功

所以移动 DomainModel 项目很简单(因为那是很多原始对象,上面有一些业务逻辑 - 没有关于它的网络)。

第 2 步 - 精简控制器 - 成功

我已经尽可能地精简了我的控制器,并将任何业务逻辑或复杂的数据访问逻辑移到了 Models 文件夹中。当我尝试将模型文件夹移到 MVC 项目之外时,出现了一些问题:

第 3 步 - 尝试将模型文件夹移到 MVC 项目之外 - 挣扎

在细化控制器时,我有许多不同的控制器操作,它们转到模型类并返回我传递回视图的 ViewModel。像这样的东西(在我的控制器类中):

 public ActionResult ApplicationDetail(int id)
 {
      AppDetailViewModel applicationViewModel = Model.GenerateAppDetailViewModel(id);
      return View(applicationViewModel);
 }

所以我的模型文件夹中的文件依赖于 ViewModel 类。我确实想集中 GenerateAppDetailViewModel() 函数,因为它在多个不同的控制器中使用。此外,在我的控制台应用程序(发送电子邮件)中,我经常想要获取恰好在某个视图上的所有数据,所以我的代码也“想要”利用视图模型......如果我将它移出 MVC 项目然后我可以重用,但我认为存在依赖问题(显然我的控制台应用程序中不需要 SelectListItem,但在其他情况下,它们只是生成我想要重用的视图所需的不同数据的容器对象)

或者另一件事是依赖于:

System.Web.Mvc

因为我有很多代码:

  1. 查询数据库中的表
  2. 将其转换为对象集合(我正在使用 nhibernate)
  3. 将其转换为某个 DTO 对象(位于 ViewModels 文件夹中)或 SelectListItem 对象列表(用于填充视图中的下拉菜单),这是 System.web.mvc 的一部分。

我想寻找关于打破这种依赖关系的最佳方法的建议,以便我可以将尽可能多的代码移出 MVC 项目以供重用。

问题是,如果我尝试将 ViewModel 代码吸入 Model 文件夹并进入另一个项目,那么我再次陷入困境,因为 ViewModel 类对

系统.Web.Mvc

由于 SelectListItem 之类的原因。

我应该有 2 个视图模型文件夹(一个在 MVC 项目中具有特定的 system.web.mvc 引用,另一个位于不同的项目中?)。似乎对 SelectListItem 的依赖是导致争用的原因

在我见过的大多数示例中,ViewModel 确实依赖于 System.Web.Mvc ,例如本教程

我看过这些问题:

这有点相关,但不确定它们是否回答了我所说的具体的整体重构问题。

4

5 回答 5

17

视图模型特定于特定应用程序。我猜您的 Web 应用程序和控制台应用程序之间的视图模型会有所不同。因此,让每个应用程序定义自己的视图模型以及域模型和视图模型之间的相应映射。不要让您的领域模型拥有将它们转换为视图模型的方法,因为这样您就完全将您的领域层绑定到 UI 层,这是最糟糕的事情。使用映射层(将特定于每个应用程序类型)。AutoMapper是您可以拥有的映射层的一个很好的例子。

甚至不要尝试在控制台应用程序中重用 ASP.NET MVC 视图模型。正如您已经发现的那样,它们将包含对 System.Web.Mvc 的引用,因为例如 ASP.NET MVC 中的 dropDownList 是用IEnumerable<SelectListItem>类表示的,而在控制台应用程序中,天知道,可能是IEnumerable<SomeItemViewModel>.

结论:视图模型和域和视图模型之间的来回映射属于 UI 层(又名 ASP.NET MVC、控制台、WPF ......)。

于 2013-07-05T21:20:00.173 回答
2

我了解您想要实现的目标,我必须告诉您:为不同的 UI 层重用相同的 ViewModel 是绝对正常的。毕竟,VM 是 MVVM 模式的一部分,而该模式是关于分离关注点的。而且,分离意味着能够用其他实现替换较低级别的层。这就是分离软件层(以及其他)的目的。

  1. 首先,您必须假设您的 MVC Web 项目是基于 MVVM 的。这将在精神上帮助您做出正确的决定。我猜你已经这样做了,因为你使用了ViewModel术语。
  2. 使 ViewModel 变得独立于平台。有可能,和它无关SelectListItem。毕竟,SelectListItem 只包含选项文本/值对,以及是否被选中的标志。您显然可以用不同的方式表达相同的信息。将这种 Generic 类型的 ViewModel 传递给 MVC 后,您可以将通用的“SelectListItem”转换为 MVC SelectListItem。是的,它是一种映射,它发生在 MVC 视图中还是在将其传递给视图之前都没有关系。但它必须发生在 UI 层(MVC Web 项目),因为它是特定于平台的映射问题。
  3. 您提到了数据访问代码:这是一个单独的软件层,通常抽象地注入到 ViewModel 中。我预计这部分没有问题。

因此,您最终将拥有不同的软件层(很可能在不同的 .NET 库中):数据访问层、ViewModel 层、MVC Web 层。

也有可能在 MVC 项目中定义 MVC ViewModels(从 Controller 传递到 View),但这些将简单地处理(可能接受)通用 ViewModels 并在 MVC View-specific 术语中公开事物(映射,继承,你的想象力?)。这也很正常——毕竟,MVC 是一个基于 Web 的 UI,它肯定有平台特定的差异,必须在平台级别而不是之前处理。MVC 特定的 ViewModel 将是处理从 generic 到 MVC 的映射的好地方SelectListItem

完成此重构后,通过使用与其他 UI 层(MVC Web 项目)相同的通用ViewModel ,没有什么能阻止您实现另一个 UI 层(您提到的控制台应用程序) 。在控制台应用程序中使用通用 ViewModel 时,如果您遇到特定于控制台平台的问题,您可以按照我上面解释的 MVC 特定 ViewModel 的相同方式提出特定于平台的 ViewModel。

于 2013-07-10T17:10:13.530 回答
1

您可以使用扩展方法在控制器中创建视图模型:

控制器:

 public ActionResult ApplicationDetail(int id)
 {
      var model = _serviceLayer.GetSomeModel(id); 
      var viewModel = model.CreateInstance(model);      
      return View(viewModel);
 }

SomeModelExtensions在你的 mvc 项目中创建它

public class SomeModelExtensions {
      public AppDetailViewModel CreateInstance(this SomeModel model) {
           var viewModel = new AppDetailViewModel();
           // here you create viewmodel object from model with logic
           return viewModel;
      }
}
于 2013-07-04T06:21:03.350 回答
1

一般来说,在我可能需要重用模型的地方布置 MVC 应用程序时,我使用以下设置/架构:

MVC 项目:与 Web 相关的一切。我在这里定义 ViewModel 并将它们映射到域模型。

Models Project:包含所有领域逻辑的类库。

存储库项目:这是一个访问数据库和领域模型的类库。

它的工作方式是 MVC 项目将使用(希望注入的)Repository 库来获取域模型并将其映射到自己的 ViewModel。

如果你也想分离映射,你可以将映射层,正如其他人建议的那样(最终使用 AutoMapper),在一个单独的项目中。映射层将引用存储库(和域模型),而 MVC 应用程序将仅引用映射层。

问题是,在创建 ViewModel 时,映射层需要对 System.Web.Mvc 的引用,正如您所发现的那样,您无法逃避这一点。这就是为什么其他人说,我同意,每个项目应该有一个映射层。

解决这个问题的一个好方法是有一个更通用的映射层,其中包含为常见情况(如电子邮件)定义的映射类。然后在特定的子类中,您可以定义特定情况的映射(例如取决于 System.Web.Mvc 的情况)。

所以最终堆栈将如下所示,依赖关系下降。当然,一切都应该基于接口。

    MVC App                      Console App
       |                            |
       |                            |
   MVC Specific Mapper         Console Specific Mapper
                 \               /  
                  \             /
                   \           /   
                   GenericMapper <- EmailMapper and EmailViewModel can be implemented here
                     |       |      
                     |       |
                Repository   |
                     |       |
                     |       |
                    DomainModels  

以上内容并非毫无困难,如果只有一两个常见情况,那么将映射分开的努力可能是不值得的。这样,从 Generic Mapper 开始,您就可以从 System.Web.Mvc 库中解放出来,而在上面,您可以自由地忘记 DB 访问代码(在某种意义上,Mapper 将充当应用程序的存储库)。

于 2013-07-10T10:24:05.887 回答
0

我假设 MVC Web 将使用与控制台应用程序 + 额外字段相​​同的数据,对吗?

那么,如何从 Model 继承 ViewModel 呢?这样您就可以根据需要重用模型并在 ViewModel 上获取自定义字段。

public class AppDetailModel
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }


    public class AppDetailViewModel : AppDetailModel
    {
        public string ViewProperty { get; set; }
    }
于 2013-07-10T03:46:17.780 回答