WPF,MVVM,带验证的业务对象不太匹配

问题描述 投票:4回答:1

对于我的WPF-Application,我决定使用MVVM。这是我的概念,我将如何实现这种模式。

  • 我的模型(Business Objects)负责验证(这对我来说是必须的)。
  • ViewModels负责将我的模型包装成友好的用户交互和一些安全方面。

我的第一个问题是关于包装还是不在ViewModel中包装我的模型。

  • 当我不在ViewModel中包装我的模型并将模型直接暴露给视图时 - 那时我不明白为什么我需要一个ViewModel(它看起来很无聊)
  • ViewModel应该出于各种原因包装模型: 我不喜欢直接绑定到Model(DateTime,int,...)中的强类型属性,因为当我这样做时=> WPF控制我对这些类型的验证。这真的很糟糕,因为当用户在Datepicker中编写'aaaa'时,我的模型是有效的(我的模型永远不会知道,因为WPF控制强类型属性)并且启用了保存按钮 - 这确实是错误的。 我没有将我的模型的所有属性暴露给视图,我的ViewModel应该保护我的模型(我有一些属性,应该只在表示层有getter而没有setter)

我的决定是,ViewModel绝对应该包装模型。所以ViewModel实施INotifyPropertyChanged

但现在我的业务验证存在问题。

当我采用漂亮的IDataErrorInfo,然后我在ViewModel中拥有整个业务规则,这打破了我的概念。业务规则绝对应该在模型中。

示例:当用户选择类型A时,则字段1和字段2是必需的。当用户选择类型B时,则字段3是必需的 - 该字段应标记为红色,并且保存按钮在无效时禁用。还有更重的东西,如免费/占用DateTime-Ranges。

当我在ViewModel中执行此操作时,这绝对是不好的,因为大多数事情都是业务部分。

那我怎么能做到这一点?

在片刻我有这个解决方法:

所有ValidationRules都在模型中作为简单方法,例如

public string ValidateBirthday(string birthay)
{
    if (...)
    {
        return "Birthday should be…";
    }
    return string.Empty;
}

在我的ViewModel中,我实现了IDataErrorInfo,并重定向到我的模型验证,如下所示:

public string this[string columnName]
{
    get
    {
        switch (columnName)
        {
            case "Birthday":
                return Model.ValidateBirthday(Birthday);
            case "XXX":
                return Model.ValidateXXX(XXX);
            case "YYY":
                return Model.ValidateYYY(YYY);
            break;
        }
    }
}

我从来没有在一个例子中看到这样的东西(重定向到模型),所以我对我的实现非常怀疑。

我的解决方法是否正常,或者您是否看到有任何问题?

我试着提供更多关于我的意思的信息......

我知道Model中的实现INotifyPropertyChanged和IDataErrorInfo。

这适用于从View到Model的直接绑定。

  1. 从视图到模型的直接绑定: public class PersonViewModel:INotifyPropertyChanged {private Person _personModel; public Person PersonModel {get {return _personModel; } set {if(_personModel!= value){_ personModel = value; NotifyPropertyChanged(); }}} public PersonViewModel(Person person) { PersonModel = person; } … }

视图:

<DatePicker Text="{Binding PersonModel.Birthday}"/>

最大的缺点是:WPF控制所有强类型属性。

示例:用户在datepicker中键入07/20/2008,因此PersonModel将被通知,PersonModel可以检查此信息,当OK,然后PersonModel有效=> SaveButton启用。

现在用户在日期选择器中键入“aaa”,WPF将控制此验证,因为它是对强类型属性(DateTime)的绑定。 PersonModel不会被告知,所以PersonModel仍然有效=> SaveButton是启用的!

所以对于那个'问题',我需要正确的ViewModel

  1. ViewModel像这样包装Model: public class PersonViewModel:INotifyPropertyChanged {private Person _personModel; public string Birthday { get { if (_personModel. Birthday!= null) { return ((DateTime) _personModel. Birthday).ToShortDateString(); } else { return String.Empty; } } set { if (_personModel. Birthday.ToString() != value) { DateTime dateValue; if (DateTime.TryParse(value, out dateValue)) { _personModel.Birthday = dateValue; … } else { … } } } } public PersonViewModel(Person person) { _personModel = person; } … }

现在我不直接从View绑定模型。我绑定了包装Model的ViewModel中的Properties。

<DatePicker Text="{Binding Birthday}"/>

最大的优点是:现在我可以完全控制用户在字段中输入的内容。当用户在Datepicker中键入类似'aaa'的字符串时,我可以捕获这个=>将状态设置为无效并禁用SaveButton。

这是一个原因,为什么我不采用从View到Model的直接绑定。其他原因是readonly Property。在Model I中,我已经获取并设置了每个Property,但是为了安全问题,我将不会使用get和set来提供Model中的所有Properties。因此,ViewModel也可以通过仅使用get包装此属性来解决此问题。通过直接绑定,你不能做所有这些事情。

我的观点是,我肯定会在ViewModel中包装模型中的所有属性,但是如何在模型中使用漂亮的IDataErrorInfo(它只适用于直接绑定)?

wpf mvvm business-rules idataerrorinfo
1个回答
3
投票

您混合的两个概念是:业务对象和验证。

现在几乎每个系统都使用客户端 - 服务器架构,即使它是一个独立的应用程序。

在这种情况下,您有两个验证位置:

  • 在向服务器发送任何内容之前,客户端负责确保输入的数据有效,以增强用户体验并避免服务器过载和安全问题。
  • 服务器负责验证传入数据,以避免格式错误,格式错误的数据和安全问题。

也:

  • Bussiness Objects(BO)是服务器使用的类,通常表示数据库。
  • 数据传输对象(DTO)是服务器发送给客户端的类。
  • ViewModels既是UI的后端代码,也是DTO的包装器。

您的模型对象不应该有任何逻辑,因为您将使用某些代码破坏它们,在某些时候您需要重用它们。

如此处所述,您应该将该验证逻辑分离为仅了解该对象以及如何验证它们的服务。这样,您就可以使用UI中的验证服务。

您的Save按钮应仅对UI更改做出反应,您只能从ViewModel获取。

基本上,您将在这里应用SOLID principles:每个层都有非常明确的职责(模型 - >数据,服务 - >验证,dto - >为客户端准备的数据,viewmodels - > UI交互)。所有代码都易于使用,易于扩展且易于重构。

编辑

第一和第二个问题:UI仅验证输入:数字字段中没有随机字符,文本字段中没有sql字符,日期格式正确等。

认为“如果那样那么”应该由后端处理,正如您所描述的:

  1. 点击保存。
  2. UI数据有效。
  3. DTO发送到后端。
  4. 后端分析DTO,它无效。
  5. 后端发回找到的错误。
  6. UI显示找到的错误。

第三个问题:这对我来说是正确的。

第四个问题:DTO只是一个概念,您可以使用通过WCF进行通信的真实后端服务器,或者您可以拥有一堆充当服务但在同一应用程序域中调用的类(与任何其他项目引用一样) 。在任何一种情况下,您都可以选择要发送和接收的数据。

你应该开始朝那个方向发展,然后看看哪个更适合你。

© www.soinside.com 2019 - 2024. All rights reserved.