MVVM - Tiêm phụ thuộc
Trong chương này, chúng ta sẽ thảo luận ngắn gọn về việc tiêm phụ thuộc. Chúng tôi đã đề cập đến việc phân tách ràng buộc dữ liệu giữa các View và ViewModels với nhau cho phép chúng giao tiếp với nhau mà không cần biết rõ ràng điều gì đang xảy ra ở đầu kia của giao tiếp.
Bây giờ chúng ta cần một cái gì đó tương tự để tách ViewModel của chúng ta khỏi các dịch vụ khách hàng.
Trong những ngày đầu của lập trình hướng đối tượng, các nhà phát triển đã phải đối mặt với vấn đề tạo và truy xuất các thể hiện của các lớp trong ứng dụng. Nhiều giải pháp khác nhau đã được đề xuất cho vấn đề này.
Trong vài năm qua, tính năng tiêm phụ thuộc và đảo ngược kiểm soát (IoC) đã trở nên phổ biến trong giới phát triển và được ưu tiên hơn một số giải pháp cũ hơn như mẫu Singleton.
Chứa phụ thuộc / IoC
IoC và tiêm phụ thuộc là hai mẫu thiết kế có liên quan chặt chẽ với nhau và vùng chứa về cơ bản là một đoạn mã cơ sở hạ tầng thực hiện cả hai mẫu đó cho bạn.
Mẫu IoC là về việc ủy quyền trách nhiệm xây dựng và mẫu chèn phụ thuộc là về việc cung cấp các phụ thuộc cho một đối tượng đã được xây dựng.
Cả hai đều có thể được coi là một cách tiếp cận hai giai đoạn để xây dựng. Khi bạn sử dụng một thùng chứa, thùng chứa có một số trách nhiệm như sau:
- Nó xây dựng một đối tượng khi được hỏi.
- Vùng chứa sẽ xác định đối tượng đó phụ thuộc vào cái gì.
- Xây dựng các phụ thuộc đó.
- Tiêm chúng vào vật thể đang được xây dựng.
- Quy trình làm đệ quy.
Chúng ta hãy xem cách chúng ta có thể sử dụng phụ thuộc chèn để phá vỡ phân tách giữa ViewModels và các dịch vụ khách hàng. Chúng tôi sẽ kết nối biểu mẫu AddEditCustomerViewModel xử lý lưu bằng cách sử dụng phụ thuộc chèn liên quan đến điều đó.
Đầu tiên, chúng ta cần tạo một giao diện mới trong dự án của chúng ta trong thư mục Services. Nếu bạn không có thư mục dịch vụ trong dự án của mình thì hãy tạo trước tiên và thêm giao diện sau vào thư mục Dịch vụ.
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<List<Customer>> GetCustomersAsync();
Task<Customer> GetCustomerAsync(Guid id);
Task<Customer> AddCustomerAsync(Customer customer);
Task<Customer> UpdateCustomerAsync(Customer customer);
Task DeleteCustomerAsync(Guid customerId);
}
}
Sau đây là việc triển khai 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<List<Customer>> GetCustomersAsync() {
return _context.Customers.ToListAsync();
}
public Task<Customer> GetCustomerAsync(Guid id) {
return _context.Customers.FirstOrDefaultAsync(c => c.Id == id);
}
public async Task<Customer> AddCustomerAsync(Customer customer){
_context.Customers.Add(customer);
await _context.SaveChangesAsync();
return customer;
}
public async Task<Customer> 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();
}
}
}
Cách đơn giản để thực hiện xử lý Lưu là thêm một phiên bản mới của ICustomersRepository trong AddEditCustomerViewModel và nạp chồng phương thức khởi tạo AddEditCustomerViewModel và CustomerListViewModel.
private ICustomersRepository _repo;
public AddEditCustomerViewModel(ICustomersRepository repo) {
_repo = repo;
CancelCommand = new MyIcommand(OnCancel);
SaveCommand = new MyIcommand(OnSave, CanSave);
}
Cập nhật phương thức OnSave như được hiển thị trong đoạn mã sau.
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;
}
Sau đây là AddEditCustomerViewModel hoàn chỉnh.
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;
}
}
}
}
Khi đoạn mã trên được biên dịch và thực thi, bạn sẽ thấy cùng một đầu ra nhưng bây giờ các ViewModels được phân tách lỏng lẻo hơn.
Khi bạn nhấn nút Thêm khách hàng, bạn sẽ thấy giao diện sau. Khi người dùng để trống bất kỳ trường nào, trường đó sẽ được tô sáng và nút lưu sẽ bị tắt.