MVVM - Injeksi Ketergantungan

Pada bab ini, kita akan membahas secara singkat tentang injeksi ketergantungan. Kami telah membahas data binding memisahkan View dan ViewModels dari satu sama lain yang memungkinkan mereka untuk berkomunikasi tanpa mengetahui secara eksplisit apa yang terjadi di ujung lain komunikasi.

Sekarang kita membutuhkan sesuatu yang mirip untuk memisahkan ViewModel kita dari layanan klien.

Pada hari-hari awal pemrograman berorientasi objek, pengembang menghadapi masalah dalam membuat dan mengambil contoh kelas dalam aplikasi. Berbagai solusi telah diajukan untuk masalah ini.

Selama beberapa tahun terakhir, injeksi ketergantungan dan inversi kontrol (IoC) telah mendapatkan popularitas di kalangan pengembang dan lebih diutamakan daripada beberapa solusi lama seperti pola Singleton.

Kontainer Injeksi Ketergantungan / IoC

IoC dan injeksi ketergantungan adalah dua pola desain yang terkait erat dan container pada dasarnya adalah potongan kode infrastruktur yang melakukan kedua pola tersebut untuk Anda.

  • Pola IoC adalah tentang mendelegasikan tanggung jawab untuk konstruksi dan pola injeksi ketergantungan adalah tentang menyediakan ketergantungan ke objek yang sudah dibangun.

  • Keduanya dapat diperlakukan sebagai pendekatan dua fase untuk membangun. Saat Anda menggunakan container, container tersebut mengambil beberapa tanggung jawab sebagai berikut -

    • Itu membangun sebuah objek ketika ditanya.
    • Wadah akan menentukan pada apa objek itu bergantung.
    • Membangun ketergantungan tersebut.
    • Menyuntikkannya ke dalam objek yang sedang dibangun.
    • Melakukan proses secara rekursif.

Mari kita lihat bagaimana kita dapat menggunakan injeksi ketergantungan untuk memutuskan pemisahan antara ViewModels dan layanan klien. Kami akan memasang formulir penanganan penyimpanan AddEditCustomerViewModel dengan menggunakan injeksi ketergantungan yang terkait dengan itu.

Pertama kita perlu membuat antarmuka baru di proyek kita di folder Services. Jika Anda tidak memiliki folder layanan dalam proyek Anda, buatlah terlebih dahulu dan tambahkan antarmuka berikut di folder Layanan.

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); 
   } 
}

Berikut adalah implementasi 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(); 
      } 
   } 
}

Cara sederhana untuk melakukan penanganan Save adalah dengan menambahkan instance baru ICustomersRepository di AddEditCustomerViewModel dan membebani konstruktor AddEditCustomerViewModel dan CustomerListViewModel.

private ICustomersRepository _repo; 

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

Perbarui metode OnSave seperti yang ditunjukkan pada kode berikut.

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; 
}

Berikut ini adalah AddEditCustomerViewModel lengkap.

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; 
         }
      } 
   } 
}

Ketika kode di atas dikompilasi dan dijalankan, Anda akan melihat output yang sama tetapi sekarang ViewModels dipisahkan dengan lebih longgar.

Saat Anda menekan tombol Add Customer, Anda akan melihat tampilan berikut. Ketika pengguna membiarkan field kosong, maka itu akan disorot dan tombol simpan akan dinonaktifkan.