gpt4 book ai didi

c# - 如何从大型ViewModel中分离出命令

转载 作者:太空宇宙 更新时间:2023-11-03 17:24:18 25 4
gpt4 key购买 nike

我的 View 模型包含很多命令,这使我的 View 模型很大。我想将我的命令从viewmodel中分离出来。目前,我的解决方案是为每个命令创建一个类,如下所示,

 public class TestCommand : CommandBase
{
private MainViewModel vm;

public TestCommand(MainViewModel vm)
{
this.vm = vm;
}

public override bool CanExecute(object parameter)
{
return true;
}

public override void ExecuteCommand(object parameter)
{
vm.logger.log(...);
...
}
}

由于我需要在ViewModel中使用某些方法或属性,因此必须将viewmodel作为参数传递给命令。对于此解决方案,有两个缺点:
1.项目中有很多命令文件,如果一个 View 中命令的平均数量为15,则10个 View 将在项目中包含150个命令文件;
2.将ViewModel作为参数传递给命令需要一些属性或方法,这些属性或方法应将私有(private)的更改为public;将viewmodel传递给命令也很奇怪。

还有其他解决方案来分隔命令吗?

最佳答案

TL; DR:

ViewModels是表示逻辑,主要在命令中表达,因此命令占用ViewModel代码的大部分并不稀奇。不要过分用INotifyPropertyChanged将ViewModels设置为纯数据持有人(就像使用ViewModels时在ASP.NET MVC中一样)。

长版

由于缺少更多细节,很难为您提供具体的提示,但是这里有一些一般性准则。您可以使用有关所使用命令类型的更多详细信息来更新您的问题,我将尝试更新该问题。

  • 演示逻辑

    ViewModels的主要关注是演示。 ViewModels中没有业务逻辑的位置。

    必须将业务逻辑提取到您的业务/域模型(如果遵循富域模型)或服务(在贫血域模型中)。在富域模型中,您的服务层通常很薄,并且大部分用于协调多个模型之间的操作。

    因此,如果您的ViewModel/命令正在执行与表示形式无关的任何逻辑(如果单击按钮A,则禁用按钮B,C和D或隐藏GroupBoxA或“禁用按钮A(如果缺少数据)(CanExecuteICommand))),可能是在做太多。
  • 关注点分离:

    您的ViewModel可能会尝试做超出预期目的的事情。您示例中的记录器就是这样的提示。记录与ViewModel无关。

    ViewModel是关于表示和表示逻辑的,而日志记录是应用程序的关注点(因为它不属于域/业务逻辑)。

    通常,ViewModel可以分为两个或多个ViewModel(即管理客户列表并允许编辑所选客户的ViewModel,通常可以分为2个或3个ViewModel:CustomersViewModel(显示列表),CustomerDetailViewModelCustomerViewModel(详细信息)给客户)和CustomerEditViewModel(以编辑相关客户)

    日志记录和缓存之类的问题应使用装饰器模式完成。这要求您的服务和/或存储库正确使用和实现接口(interface),然后您可以创建用于缓存或日志记录的装饰器,而无需注入(inject)服务的原始实例,而是可以实现装饰器。

    依赖注入(inject)(DI)和控制反转(IoC)容器确实可以帮助您。必须手动将其连接起来(又名穷人DI),这是屁股上的痛苦。具体示例不在此答案的范围内。
  • 命令中的业务逻辑

    命令不应包含业务逻辑。当您的命令包含太多代码(通常超过5至20行代码)时,这是一个很好的线索,即您的命令可能会执行过多的操作。

    命令实际上应该只绑定(bind)多个服务调用,并将数据分配给属性和/或引发事件/消息(与表示层相关。不要与域事件混淆,不应在命令内部引发域事件)。它们类似于MVC中的“ Action ”(例如,类似于ASP.NET MVC中使用的框架)。

    命令通常应该看起来像这样
    var customer = new Customer { Name = this.CustomerName, Mail = this.CustomerMail };
    try {
    this.customerService.AddCustomer(customer);
    // Add it to Observable<Customer> list so the UI gets updated
    this.Customers.Add(customer);
    // the service should have populated the Id field of Customer when persisting it
    // so we notify all other ViewModels that a new customer has been added
    this.messageBus.Publish(new CustomerCreated() { CustomerId = customer.Id } );
    } catch (SomeSpecificException e) { // Handle the Exception }

    或者
    this.Customers = this.customerRepository.GetAll();
    // Or this for async commands
    this.Customers = await this.customerRepository.GetAllAsync();
  • 封装

    许多命令与ViewModel本身紧密耦合,并且需要访问ViewModel或Model的内部状态(不应将Model直接暴露给View,这会使Model与View耦合以及模型中断的任何更改)您的 View 和绑定(bind))。

    在不破坏封装的情况下,将这些ICommands移出ViewModel可能很困难。

  • 当然,您也可以在一类中实现多个命令
    public class MyViewModelCommandHandler
    {
    private readonly IMyRepository myRepository;

    public MyViewModelCommandHandler(/* pass dependencies here*/)
    {
    // assign and guard dependencies

    MyCommand = new RelayCommand(MyCommand, CanExecuteMyCommand);
    MyOtherCommand = new RelayCommand(MyOtherCommand, CanExecuteMyOtherCommand);
    }

    public ICommand MyCommand { get; protected set; }
    public ICommand MyOtherCommand { get; protected set; }

    private void MyCommand()
    {
    // do something
    }

    private void CanExecuteMyCommand()
    {
    // validate
    }

    private void MyOtherCommand()
    {
    // do something else
    }

    private void CanExecuteMyOtherCommand()
    {
    // validate
    }
    }

    在您的ViewModel中,只需分配这些命令
    public class MyViewModel : ViewModelBase 
    {
    public MyViewModel()
    {
    var commandHandler = new MyCommandHandler(this);
    OneCommand = commandHandler.MyCommand;
    OtherCommand = commandHandler.MyOtherCommand;
    }

    public ICommand OneCommand { get; private set; }
    public ICommand OtherCommand { get; private set; }
    }

    您还可以使用IoC容器将 MyCommandHandler注入(inject) View 中,这需要稍微重新构建命令处理程序类,以根据需要创建 ICommand。然后你可以像
    public class MyViewModel : ViewModelBase 
    {
    public MyViewModel(MyCommandHandler commandHandler)
    {
    OneCommand = commandHandler.CreateMyCommand(this);
    OtherCommand = commandHandler.CreateMyOtherCommand(this);
    }

    public ICommand OneCommand { get; private set; }
    public ICommand OtherCommand { get; private set; }
    }

    但这只是改变了您的问题,尽管不能解决要点1.至5.。因此,我建议您首先尝试上述列表中的建议,如果您的命令仍然包含“太多行代码”,请尝试其他解决方案。

    我不太喜欢它,因为它会创建不必要的抽象,但收效甚微。

    ViewModel主要由表示逻辑组成,这是很常见的,因为这是它们的用途,并且表示逻辑通常位于命令内部。除此之外,您只有属性和构造函数。除了检查值是否已更改,然后赋值以及一个或多个 OnPropertyChanged调用外,属性不应有其他任何东西。

    因此,您的ViewModel的50-80%是来自命令的代码。

    关于c# - 如何从大型ViewModel中分离出命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35037158/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com