Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/259.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# MVVM:ViewModel和业务逻辑连接_C#_Wpf_Design Patterns_Mvvm - Fatal编程技术网

C# MVVM:ViewModel和业务逻辑连接

C# MVVM:ViewModel和业务逻辑连接,c#,wpf,design-patterns,mvvm,C#,Wpf,Design Patterns,Mvvm,在使用MVVM模式完成了几个项目之后,我仍然在努力扮演ViewModel的角色: ... private Person person; ... string IDataErrorInfo.this[string propertyName] { get { string error = (person as IDataErrorInfo)[propertyName]; return error; } } 我过去所做的: 仅将模型用作数

在使用MVVM模式完成了几个项目之后,我仍然在努力扮演ViewModel的角色:

...

private Person person;

...

string IDataErrorInfo.this[string propertyName]
{
    get
    {
        string error = (person as IDataErrorInfo)[propertyName];
        return error;
    }
}
我过去所做的: 仅将模型用作数据容器。 将操作ViewModel中数据的逻辑放入。(这就是业务逻辑,对吗?) 缺点:逻辑是不可重用的

我现在正在尝试的是: 使ViewModel尽可能薄。 将所有逻辑移到模型层。 仅在ViewModel中保留表示逻辑。 缺点:如果在模型层内更改数据,则会使UI通知变得非常痛苦

因此,我将给大家举一个更清楚的例子:

情景: 用于重命名文件的工具。 课程: 文件:表示每个文件; 规则:包含如何重命名文件的逻辑

如果Im遵循方法1: 为文件、规则和视图->重命名服务模型创建视图模型。 将所有逻辑放入重命名服务模型: 包含FileViewModel和RuleViewModel的列表以及正在进行的逻辑。 简单快速,但不可重复使用

如果Im遵循方法2: 创建一个新的模型类->重命名器,其中包含一个文件列表、规则和对每个文件进行交互并应用每个规则的继续逻辑。 为文件、规则和重命名器创建Viewmodel。 现在,RenamerViewModel只包含Renamer模型的一个实例,外加两个ObservableCollections来包装Renamer的文件和规则列表。 但整个逻辑都在重命名器模型中。因此,如果通过方法调用触发重命名器模型来操作某些数据,则ViewModel不知道操作了哪些数据。 因为模型不包含任何PropertyChange通知,我将避免这种情况。
因此,业务逻辑和表示逻辑是分开的,但这使得通知UI变得很困难。

这两种方法都是有效的,但还有第三种方法:在模型层和VM层之间实现服务。如果你想让你的模型保持沉默,服务可以提供一个与用户界面无关的中间人,它可以以可重用的方式强制执行你的业务规则

因为模型不包含任何PropertyChange通知,我将避免这种情况


你为什么要回避这个?不要误解我的意思,我倾向于尽可能地保持我的模型的沉默,但是在模型中实现更改通知有时是有用的,并且当您这样做时,您只依赖于
System.ComponentModel
。它完全与用户界面无关。

将业务逻辑放在viewmodel中是一种非常糟糕的方法,因此我将很快地说,永远不要这样做,并继续讨论第二个选项

将逻辑放在模型中更加合理,这是一个良好的开端。缺点是什么?你的问题是

因此,如果触发重命名器模型以通过方法操作某些数据 调用时,ViewModel不知道操作了哪些数据。因为 该模型不包含任何PropertyChange通知,我将 避免这样

好吧,让您的模型实现INotifyPropertyChanged肯定会让您获得更好的结果。但是,有时确实不可能做到这一点——例如,模型可能是一个局部类,其中属性由工具自动生成,并且不会引发更改通知。这是不幸的,但不是世界末日

如果你想买东西,就得有人付钱;如果不是模型提供此类通知,那么您只有两个选择:

  • viewmodel知道模型上的哪些操作(可能)会导致更改,并在每次此类操作后更新其状态
  • 其他人知道哪些操作会导致更改,并在模型包装更改后通知viewmodel更新其状态
  • 第一个选项也是一个坏主意,因为实际上它又回到了将“业务逻辑”放在viewmodel中。没有将所有业务逻辑放在viewmodel中那么糟糕,但仍然如此

    第二种选择更有希望(不幸的是,还有更多的工作要实施):

    • 将部分业务逻辑放在单独的类(“服务”)中。该服务将通过适当地使用模型实例来实现您想要执行的所有业务操作
    • 这意味着服务知道模型属性何时可能更改(这是确定的:model+service==业务逻辑)
    • 该服务将向所有相关方提供有关更改型号的通知;您的viewmodels将依赖于该服务并接收这些通知(以便他们知道“他们的”模型何时更新)
    • 由于业务操作也由服务实现,因此这仍然非常自然(例如,当在viewmodel上调用命令时,反应是调用服务上的适当方法;请记住,viewmodel本身不知道业务逻辑)

    有关这种实现的更多信息,请参见我的答案和。

    您也可以在:Model和ViewModel上实现IDataErrorInfo,但仅在Model中执行验证,这将简化您仅在Model上实现业务规则的方式

    例:

    视图模型:

    ...
    
    private Person person;
    
    ...
    
    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            string error = (person as IDataErrorInfo)[propertyName];
            return error;
        }
    }
    
    型号:

    public class Person:INotifyPropertyChanged,IDataErrorInfo
    {
    
    ...
    
       string IDataErrorInfo.this[string propertyName]
       {
            get { return this.GetValidationError(propertyName); }
       }
    
    ...
    
       string GetValidationError(string propertyName)
       {
            if(propertyName == "PersonName")
                 //do the validation here returning the string error
       }
    }
    
    另外,看看MVCVM模式,我实际上正在使用它,将业务逻辑抽象到控制器类而不是模型或视图模型是非常好的,我做了以下工作

  • 仅使用XAML视图逻辑的视图

  • 处理单击处理程序和创建新视图模型的ViewModel。处理路由事件等

  • 模型,它是我的数据容器和关于验证模型数据的业务逻辑

  • 使用数据填充模型的服务。例如调用web服务器、从磁盘加载、保存到磁盘等。根据示例,我的模型和服务通常都会实现IPropertyChanged。或者,它们可以使用事件处理程序

  • 任何c