当你有富模特的时候你会怎么做?

本文关键字:你会怎么做 模特 当你 | 更新日期: 2023-09-27 18:10:39

我正在处理一个项目中的现有代码,该项目在整个地方都有丰富的模型(而不是POCOs)。基本上,有没有什么好方法来混合富模型和ViewModels没有重复的代码?


  • 富模型具有数据验证。有什么简单的方法来重用这些与ViewModels?

的例子:

public class PersonViewModel : INotifyPropertyChanged, IDataErrorInfo
{
    private Person _person;
    [Required] //This seems redundent...
    public String FirstName { ... }
}
public class Person
{
    [Required]
    public String FirstName { ... }
}

这只是一个例子…基本上,如果你有一个富模型,有没有办法在维护MVVM和避免冗余代码的同时利用它?我真的想避免让我的模型是任何数据上下文或完全暴露的ViewModel。

例如:

public class PersonViewModel : INotifyPropertyChanged, IDataErrorInfo
{
    private Person _person;
    //This seems like a bad thing to do...
    public Person ThePerson { get { return _person; } }
}

当你有富模特的时候你会怎么做?

这是一个经典的问题。

如果您用特定于表示层的接口和属性"污染"了您的Model类,那么您通过只拥有任何逻辑模型的一个版本来获得效率,但是失去了表示和业务模型独立发展的能力。

如果你保持你的模型"纯"并且维护一个单独的视图模型,你在每个视图模型中都获得了灵活性,但是由于必须维护(和在两个版本之间映射)而失去了效率。

从理论的角度来看,对于更复杂的系统,我会推荐后者。如果您的系统相对简单(比如CRUD),并且您不希望这两种类型的模型独立发展,那么使用前者可能是相当安全的。

显然,这两种方法并不是相互排斥的,在逐个屏幕的基础上做出决定也不是闻所未闻的。

MVVM的一个思想是将表示层与数据层分开。这使您能够在不更改数据层的数据的情况下更改表示层正在处理的数据。

因此,来自表示层的数据仅在用户请求时才写入数据层。你的冗余FirstName属性作为一个图层边界,让你可以灵活地实现一些简单的"撤销所有更改"。

考虑使用一个通用的ValueViewModel来处理数据绑定所需的值更改通知。按照这种方法,您的视图模型看起来像:

public class PersonViewModel : INotifyPropertyChanged, IDataErrorInfo
{
    private Person _person;
    [Required] //This seems redundent...
    public ValueViewModel<String> FirstName { ... }
}

使用此模式,您的模型不需要实现INotifyPropertyChanged接口,该接口再次将您的表示层与数据层分开。

MVVM是一个非常复杂的模式,你会经常看到一些在第一个视图上看起来有点正式的东西,但是遵循这个模式会给你很大的灵活性。如果您选择违反MVVM规则,那么您就把整个应用程序体系结构置于危险之中,因为这一违反破坏了使用MVVM获得的灵活性。因此,如果您打算违反MVVM,请考虑根本不要使用它。

我从来没有看到过从ViewModel为视图暴露整个模型的任何错误。我知道这不是"MVVM-Purist"的方法,但它简单、快速,而且效果很好。

但我确实理解这两种方法都是同样有效的,并且通常对于非常大的代码库,Model和ViewModel之间的分离从长远来看会使生活更容易。在这种情况下,为什么不让ViewModel验证返回ModelValidation呢?它不像使用DataAnnotations那么漂亮,但您不会在多个位置创建验证。

例如,我经常这样写:

public string GetValidationError(string propertyName)
{
    string s = null;
    switch (propertyName)
    {
        case "FirstName":
        case "LastName":
            s = Person.GetValidationError(propertyName);
            break;
    }
    return s;
}
string IDataErrorInfo.this[string propertyName]
{
    get { return this.GetValidationError(propertyName); }
}