我有一个带有以下文件夹的asp.net-mvc网站:
我现在想要在另一个.net应用程序(一个Windows控制台应用程序,所以根本不是网络)访问大量的业务逻辑和数据库访问代码和数据,所以我重构以删除尽可能多的东西在MVC项目之外以及解决方案中的其他项目,以便可以与其他解决方案共享代码。
我有两个主要问题;
我的主要问题是我很难找到一个放置生成ViewModel的代码的地方,因为很多这些代码我想在我的控制台应用程序中重用,因为控制台应用程序发送的电子邮件需要视图中的相同数据。
另一个主要问题是,我很难看到如何将我的数据库访问代码移出MVC项目,同时仍然在我的许多我的viewmodels实例化的函数以一堆数据库开头的ViewModel访问代码。
到目前为止,这里有一些细节和我的过程:
所以移动DomainModel项目很简单(因为那里有许多原始对象,其中包含一些业务逻辑 - 没有关于它的网络)。
我尽可能地减少了控制器的数量,并将任何业务逻辑或复杂的数据访问逻辑移到了Models文件夹中。当我试图将模型文件夹移到MVC项目之外时,有些事情破灭了:
在稀疏控制器时,我有许多不同的控制器动作转到模型类并返回我传递回视图的ViewModel。像这样(在我的控制器类中):
public ActionResult ApplicationDetail(int id)
{
AppDetailViewModel applicationViewModel = Model.GenerateAppDetailViewModel(id);
return View(applicationViewModel);
}
因此,我的Model文件夹中的文件依赖于ViewModel类。我确实希望集中GenerateAppDetailViewModel()函数,因为它在多个不同的控制器中使用。此外,在我的控制台应用程序(发送电子邮件,我经常想要获取恰好在某个视图上的所有数据,所以我的代码“希望”也利用视图模型..如果我将它移出MVC项目然后我可以重用但我认为有依赖性问题(显然我不需要在我的控制台应用程序中使用SelectListItem,但在其他情况下,它们只是生成视图所需的不同数据的容器对象,我确实要重用)
或其他破坏的事情依赖于:
System.Web.Mvc
因为我有很多代码:
我想寻找关于打破这种依赖关系的最佳方法的建议,这样我就可以尽可能多地从MVC项目中移出代码以便重用。
问题在于,如果我尝试将我的ViewModel代码吸入Model文件夹并进入另一个项目,那么我再次陷入困境,因为ViewModel类对很多依赖
System.Web.Mvc
由于像SelectListItem这样的东西。
我应该有2个视图模型文件夹(MVC项目中有一个具有特定的system.web.mvc引用,另一个位于不同的项目中吗?)。似乎依赖于SelectListItem是导致争用的原因
在大多数示例中,我看到ViewModel确实依赖于System.Web.Mvc such as this tutorial
我见过这些问题:
这有点相关但不确定它们是否回答了我所说的特定整体重构问题。
答案 0 :(得分:17)
查看模型特定于特定应用程序。我想您的Web应用程序和控制台应用程序之间的视图模型会有所不同。因此,每个应用程序都定义自己的视图模型以及域模型和视图模型之间的相应映射。不要让您的域模型拥有将它们转换为视图模型的方法,因为这样您就可以将域层完全绑定到UI层,这是最糟糕的事情。使用映射层(将特定于每种应用程序类型)。 AutoMapper
是您可以拥有的地图图层的一个很好的示例。
甚至不要尝试在控制台应用程序中重用ASP.NET MVC视图模型。正如您已经发现的那样,它们将包含对System.Web.Mvc的引用,因为例如ASP.NET MVC中的dropDownList用IEnumerable<SelectListItem>
类表示,而在控制台应用程序中,上帝知道,可能是{{1 }}
结论:在域和视图模型之间查看模型和来回映射属于UI层(a.k.a ASP.NET MVC,Console,WPF,...)。
答案 1 :(得分:2)
我理解您想要实现的目标,并且我必须告诉您:对于不同的UI层重用相同的ViewModel,绝对正常。毕竟,VM是MVVM模式的一部分,该模式完全是关于分离关注点。而且,分离意味着能够用其他实现替换较低级别的层。 是单独软件层(以及其他)的目的。
ViewModel
term。SelectListItem
无关。毕竟,SelectListItem只包含选项文本/值对,加上标志是否被选中。你显然可以用不同的方式表达相同的信息。将此Generic类型的ViewModel传递给MVC后,您可以转换泛型&#34; SelectListItem&#34;到MVC SelectListItem
。是的,它是一种映射,无论是在MVC视图中还是在将其传递给View之前都无关紧要。但它必须发生在UI层(MVC Web项目)上,因为它是一个特定于平台的映射问题。因此,您将最终拥有不同的软件层(最有可能在不同的.NET库中):数据访问层,ViewModel层,MVC Web层。
有可能在MVC项目中定义MVC ViewModel(从Controller传递到View),但这些只是处理(可能接受)通用ViewModels并在MVC View特定术语中公开事物(映射,继承,你的想象力?)。这也是正常的 - 毕竟,MVC是一个基于Web的UI,它肯定具有特定于平台的差异,必须在平台级别上处理,而不是之前。 MVC特定的ViewModel将是处理从泛型到MVC SelectListItem
的映射的好地方。
完成此重构之后,没有什么可以阻止您实现另一个UI层 - 您提到的控制台应用程序,通过使用相同的泛型 ViewModels作为其他UI层 - MVC网络项目。在控制台应用程序中使用通用ViewModel时,如果您遇到特定于Console平台的问题,您可以按照我上面针对MVC特定的ViewModel所解释的方式提出特定于平台的ViewModel。
答案 2 :(得分:1)
您可以使用扩展方法在控制器中创建视图模型:
控制器:
public ActionResult ApplicationDetail(int id)
{
var model = _serviceLayer.GetSomeModel(id);
var viewModel = model.CreateInstance(model);
return View(viewModel);
}
在您的mvc项目中创建此SomeModelExtensions
public class SomeModelExtensions {
public AppDetailViewModel CreateInstance(this SomeModel model) {
var viewModel = new AppDetailViewModel();
// here you create viewmodel object from model with logic
return viewModel;
}
}
答案 3 :(得分:1)
一般情况下,我在布局MVC应用程序时使用以下设置/架构,我可能需要重用模型:
MVC项目:与网络相关的一切。我在这里定义ViewModel并将它们映射到域模型。
Models Project:包含所有域逻辑的类库。
存储库项目:这是一个访问数据库和域模型的类库。
它的工作方式是MVC项目将使用(希望注入的)存储库来获取域模型并将其映射到自己的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库中解放出来,而在上面您可以自由地忘记数据库访问代码(在某种意义上,Mapper将充当应用程序的存储库)。
答案 4 :(得分:0)
我认为MVC网站将使用与控制台应用+额外字段相同的数据,对吧?
那么,从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; }
}