MVVM - การฉีดพึ่งพา

ในบทนี้เราจะพูดคุยสั้น ๆ เกี่ยวกับการฉีดแบบพึ่งพา เราได้กล่าวถึงการผูกข้อมูลที่แยกมุมมองและ ViewModels ออกจากกันแล้วซึ่งช่วยให้พวกเขาสามารถสื่อสารได้โดยไม่ทราบแน่ชัดว่าเกิดอะไรขึ้นที่ส่วนอื่น ๆ ของการสื่อสาร

ตอนนี้เราต้องการสิ่งที่คล้ายกันเพื่อแยก ViewModel ของเราออกจากบริการไคลเอ็นต์

ในช่วงแรก ๆ ของการเขียนโปรแกรมเชิงวัตถุนักพัฒนาประสบปัญหาในการสร้างและดึงอินสแตนซ์ของคลาสในแอปพลิเคชัน มีการเสนอวิธีแก้ปัญหาต่างๆสำหรับปัญหานี้

ในช่วงไม่กี่ปีที่ผ่านมาการฉีดแบบพึ่งพาและการควบคุมแบบผกผัน (IoC) ได้รับความนิยมในหมู่นักพัฒนาและมีความสำคัญเหนือกว่าโซลูชันรุ่นเก่าบางอย่างเช่นรูปแบบ Singleton

การฉีดขึ้นอยู่กับภาชนะ / IoC

IoC และการฉีดพึ่งพาเป็นรูปแบบการออกแบบสองแบบที่มีความสัมพันธ์กันอย่างใกล้ชิดและโดยพื้นฐานแล้วคอนเทนเนอร์นั้นเป็นส่วนของรหัสโครงสร้างพื้นฐานที่ทำทั้งสองรูปแบบให้คุณ

  • รูปแบบ IoC เป็นเรื่องเกี่ยวกับการมอบหมายความรับผิดชอบในการก่อสร้างและรูปแบบการฉีดพึ่งพาเป็นเรื่องเกี่ยวกับการให้การอ้างอิงกับวัตถุที่สร้างขึ้นแล้ว

  • พวกเขาทั้งสองสามารถถือเป็นแนวทางสองเฟสในการสร้าง เมื่อคุณใช้คอนเทนเนอร์คอนเทนเนอร์จะมีความรับผิดชอบหลายประการดังนี้ -

    • สร้างวัตถุเมื่อถูกถาม
    • คอนเทนเนอร์จะกำหนดว่าวัตถุนั้นขึ้นอยู่กับอะไร
    • การสร้างการอ้างอิงเหล่านั้น
    • ฉีดเข้าไปในวัตถุที่กำลังสร้าง
    • ทำซ้ำกระบวนการ

มาดูกันว่าเราสามารถใช้การฉีดขึ้นข้อมูลเพื่อแยกการแยกส่วนระหว่าง ViewModels และบริการไคลเอ็นต์ได้อย่างไร เราจะวางสายการจัดการการบันทึกแบบฟอร์ม AddEditCustomerViewModel โดยใช้การฉีดการพึ่งพาที่เกี่ยวข้องกับสิ่งนั้น

ก่อนอื่นเราต้องสร้างอินเทอร์เฟซใหม่ในโครงการของเราในโฟลเดอร์ Services หากคุณไม่มีโฟลเดอร์บริการในโครงการของคุณให้สร้างก่อนและเพิ่มอินเทอร์เฟซต่อไปนี้ในโฟลเดอร์ Services

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

ต่อไปนี้คือการนำ 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(); 
      } 
   } 
}

วิธีง่ายๆในการจัดการบันทึกคือการเพิ่มอินสแตนซ์ใหม่ของ ICustomersRepository ใน AddEditCustomerViewModel และโอเวอร์โหลดตัวสร้าง 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 แยกออกจากกันอย่างหลวม ๆ

เมื่อคุณกดปุ่มเพิ่มลูกค้าคุณจะเห็นมุมมองต่อไปนี้ เมื่อผู้ใช้เว้นช่องว่างไว้ช่องนั้นจะถูกไฮไลต์และปุ่มบันทึกจะปิดใช้งาน