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 แยกออกจากกันอย่างหลวม ๆ
เมื่อคุณกดปุ่มเพิ่มลูกค้าคุณจะเห็นมุมมองต่อไปนี้ เมื่อผู้ใช้เว้นช่องว่างไว้ช่องนั้นจะถูกไฮไลต์และปุ่มบันทึกจะปิดใช้งาน