除了已经提到的(关注点分离、解耦等)之外,单独的 ViewModel 可以阻止可能与 DB 模型一起出现的抽象泄漏。如果您使用启用了导航属性的 EF,则尤其如此。
假设您有汽车和车轮。您正在视图中显示汽车。
案例 1(汽车没有单独的 ViewModel): 在 razor 视图中,很容易出现如下内容:
public class CarModelFromDB
{
public string CarName{get;set;}
//More properties
public IEnumerable<Wheel> Wheel{get;set;}
}
@model IEnumerable<CarModelFromDB>
@foreach(var car in Model)
{
//some View Code
@foreach(var wheel in car.Wheels.Where(x=>x.IsRound=true))
{
<span>wheel.Color</span>
// some View Code
}
}
现在,您获取汽车轮子的逻辑已泄露到视图中,并且还启用了select N+1 situation。我也不认为有任何简单的测试方法。
案例 2(使用 ViewModel 用于汽车):在这种情况下,您可以通过仅发送它需要的内容来限制视图。它可能如下所示:
public class CarViewModel
{
public string CarName{get;set;}
//More properties
public IEnumerable<string> WheelColors{get;set;}
}
@model IEnumerable<CarViewModel>
@foreach(var car in Model)
{
//some View Code
@foreach(var wheelColor in WheelColor)
{
<span>wheelColor</span>
// some View Code
}
}
现在,您的视图代码的功能非常有限,它不会向数据库发送任何恶意查询。您的控制器真正控制了视图的获取方式。您可以将轮逻辑推到那里,或者理想情况下推到从操作方法调用的某些服务方法中。此外,您可以对操作方法进行适当的测试,并对您的系统充满信心。我希望这会有所帮助。
更新
案例 3(动态 ViewModel):
如果您对dynamic types 感到满意,则可以避免所有的强制转换和映射。只要您的视图获得所需的属性,它就会很高兴。这些从哪里来并不重要。所以代码将是:
public class CarViewModel
{
public string CarName{get;set;}
//More properties
public IEnumerable<string> WheelColors{get;set;}
}
// pass the List<CarViewModel> to the view
@model dynamic
@foreach(var car in Model)
{
//some View Code
@foreach(var wheelColor in WheelColor)
{
<span>wheelColor</span>
// some View Code
}
}
潜在的缺点/额外工作是确保您对模型上这些属性的存在进行了测试。
再次像前面提到的那样,这不应被视为一种适合所有类型的解决方案。这些是一些选项,仅在有意义时才使用。