📜  MVVM –依赖注入

📅  最后修改于: 2020-11-19 05:26:09             🧑  作者: Mango


在本章中,我们将简要讨论依赖注入。我们已经介绍了将视图和ViewModel彼此分离的数据绑定,这些视图使View和ViewModel可以进行通信,而无需明确知道通信另一端的情况。

现在,我们需要类似的东西来将ViewModel与客户端服务分离。

在面向对象编程的早期,开发人员面临着在应用程序中创建和检索类实例的问题。针对该问题已经提出了各种解决方案。

在过去的几年中,依赖注入和控制反转(IoC)在开发人员中越来越流行,并且优先于某些较旧的解决方案,例如Singleton模式。

依赖注入/ IoC容器

IoC和依赖项注入是两个紧密相关的设计模式,而容器基本上是一堆基础结构代码,可以为您完成这两种模式。

  • IoC模式是关于委派构造的责任,而依赖项注入模式是关于为已经构造的对象提供依赖性。

  • 它们都可以视为构建的两阶段方法。使用容器时,该容器承担以下几项职责:

    • 询问时构造一个对象。
    • 容器将确定该对象所依赖的对象。
    • 构造那些依赖项。
    • 将它们注入正在构造的对象中。
    • 递归地执行过程。

让我们看一下如何使用依赖注入来打破ViewModel和客户端服务之间的解耦。我们将使用与此相关的依赖项注入来连接保存处理AddEditCustomerViewModel表单。

首先,我们需要在“服务”文件夹中的项目中创建一个新界面。如果您的项目中没有服务文件夹,请先创建它,然后在“服务”文件夹中添加以下界面。

using MVVMHierarchiesDemo.Model; 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks;

namespace MVVMHierarchiesDemo.Services { 

   public interface ICustomersRepository { 
      Task> GetCustomersAsync(); 
      Task GetCustomerAsync(Guid id); 
      Task AddCustomerAsync(Customer customer); 
      Task UpdateCustomerAsync(Customer customer); 
      Task DeleteCustomerAsync(Guid customerId); 
   } 
}

以下是ICustomersRepository的实现。

using MVVMHierarchiesDemo.Model; 

using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text; 
using System.Threading.Tasks;

namespace MVVMHierarchiesDemo.Services { 

   public class CustomersRepository : ICustomersRepository {
      ZzaDbContext _context = new ZzaDbContext();

      public Task> GetCustomersAsync() { 
         return _context.Customers.ToListAsync(); 
      }

      public Task GetCustomerAsync(Guid id) { 
         return _context.Customers.FirstOrDefaultAsync(c => c.Id == id); 
      }
        
      public async Task AddCustomerAsync(Customer customer){ 
         _context.Customers.Add(customer); 
         await _context.SaveChangesAsync(); 
         return customer;
      }

      public async Task UpdateCustomerAsync(Customer customer) {
        
         if (!_context.Customers.Local.Any(c => c.Id == customer.Id)) { 
            _context.Customers.Attach(customer); 
         } 
            
         _context.Entry(customer).State = EntityState.Modified;
         await _context.SaveChangesAsync(); 
         return customer;
            
      }

      public async Task DeleteCustomerAsync(Guid customerId) {
         var customer = _context.Customers.FirstOrDefault(c => c.Id == customerId); 
            
         if (customer != null) {
            _context.Customers.Remove(customer); 
         }
            
         await _context.SaveChangesAsync(); 
      } 
   } 
}

执行保存处理的简单方法是在AddEditCustomerViewModel中添加ICustomersRepository的新实例,并重载AddEditCustomerViewModel和CustomerListViewModel构造函数。

private ICustomersRepository _repo; 

public AddEditCustomerViewModel(ICustomersRepository repo) { 
   _repo = repo; 
   CancelCommand = new MyIcommand(OnCancel);
   SaveCommand = new MyIcommand(OnSave, CanSave); 
}

如下面的代码所示,更新OnSave方法。

private async void OnSave() { 
   UpdateCustomer(Customer, _editingCustomer); 
    
   if (EditMode) 
      await _repo.UpdateCustomerAsync(_editingCustomer); 
   else 
      await _repo.AddCustomerAsync(_editingCustomer); 
   Done(); 
} 

private void UpdateCustomer(SimpleEditableCustomer source, Customer target) { 
   target.FirstName = source.FirstName; 
   target.LastName = source.LastName; 
   target.Phone = source.Phone; 
   target.Email = source.Email; 
}

以下是完整的AddEditCustomerViewModel。

using MVVMHierarchiesDemo.Model; 
using MVVMHierarchiesDemo.Services; 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text;
using System.Threading.Tasks;

namespace MVVMHierarchiesDemo.ViewModel { 

   class AddEditCustomerViewModel : BindableBase { 
      private ICustomersRepository _repo; 
        
      public AddEditCustomerViewModel(ICustomersRepository repo) { 
         _repo = repo;
         CancelCommand = new MyIcommand(OnCancel); 
         SaveCommand = new MyIcommand(OnSave, CanSave); 
      } 
        
      private bool _EditMode; 
        
      public bool EditMode { 
         get { return _EditMode; } 
         set { SetProperty(ref _EditMode, value); } 
      }

      private SimpleEditableCustomer _Customer; 
        
      public SimpleEditableCustomer Customer { 
         get { return _Customer; } 
         set { SetProperty(ref _Customer, value); } 
      }
        
      private Customer _editingCustomer = null;

      public void SetCustomer(Customer cust) { 
         _editingCustomer = cust; 
            
         if (Customer != null) Customer.ErrorsChanged -= RaiseCanExecuteChanged; 
         Customer = new SimpleEditableCustomer();
         Customer.ErrorsChanged += RaiseCanExecuteChanged;
         CopyCustomer(cust, Customer); 
      }

      private void RaiseCanExecuteChanged(object sender, EventArgs e) { 
         SaveCommand.RaiseCanExecuteChanged(); 
      }

      public MyIcommand CancelCommand { get; private set; } 
      public MyIcommand SaveCommand { get; private set; }

      public event Action Done = delegate { };
        
      private void OnCancel() { 
         Done(); 
      }

      private async void OnSave() { 
         UpdateCustomer(Customer, _editingCustomer); 
            
         if (EditMode) 
            await _repo.UpdateCustomerAsync(_editingCustomer); 
         else 
            await _repo.AddCustomerAsync(_editingCustomer); 
         Done(); 
      }

      private void UpdateCustomer(SimpleEditableCustomer source, Customer target) { 
         target.FirstName = source.FirstName; 
         target.LastName = source.LastName; 
         target.Phone = source.Phone; 
         target.Email = source.Email; 
      }

      private bool CanSave() { 
         return !Customer.HasErrors; 
      }
        
      private void CopyCustomer(Customer source, SimpleEditableCustomer target) { 
         target.Id = source.Id; 
            
         if (EditMode) { 
            target.FirstName = source.FirstName; 
            target.LastName = source.LastName; 
            target.Phone = source.Phone; 
            target.Email = source.Email; 
         }
      } 
   } 
}

当上面的代码被编译和执行时,您将看到相同的输出,但是现在ViewModels更松散地分离了。

MVVM依赖注入MainWindow1

当您按添加客户按钮时,您将看到以下视图。当用户将任何字段留空时,该字段将突出显示,并且保存按钮将被禁用。

MVVM依赖注入MainWindow2