MVVM – 종속성 주입

이 장에서는 종속성 주입에 대해 간략하게 설명합니다. 데이터 바인딩은 이미 통신의 다른 쪽 끝에서 무슨 일이 벌어지고 있는지 명시 적으로 알지 못해도 서로 통신 할 수 있도록 View와 ViewModel을 서로 분리하는 것을 다루었습니다.

이제 클라이언트 서비스에서 ViewModel을 분리하는 것과 유사한 것이 필요합니다.

객체 지향 프로그래밍의 초기에 개발자는 응용 프로그램에서 클래스의 인스턴스를 만들고 검색하는 문제에 직면했습니다. 이 문제에 대한 다양한 해결책이 제안되었습니다.

지난 몇 년 동안 종속성 주입 및 제어 반전 (IoC)은 개발자들 사이에서 인기를 얻었으며 Singleton 패턴과 같은 일부 이전 솔루션보다 우선했습니다.

의존성 주입 / IoC 컨테이너

IoC 및 종속성 주입은 밀접하게 관련된 두 가지 디자인 패턴이며 컨테이너는 기본적으로 이러한 패턴을 모두 수행하는 인프라 코드 청크입니다.

  • IoC 패턴은 생성에 대한 책임을 위임하는 것이고 의존성 주입 패턴은 이미 생성 된 객체에 대한 의존성을 제공하는 것입니다.

  • 둘 다 구성에 대한 2 단계 접근 방식으로 취급 할 수 있습니다. 컨테이너를 사용할 때 컨테이너는 다음과 같은 몇 가지 책임이 있습니다.

    • 요청하면 객체를 생성합니다.
    • 컨테이너는 해당 개체가 의존하는 대상을 결정합니다.
    • 이러한 종속성을 구성합니다.
    • 그것들을 건설중인 물체에 주입합니다.
    • 재귀 적으로하는 과정.

종속성 주입을 사용하여 ViewModel과 클라이언트 서비스 간의 분리를 끊는 방법을 살펴 보겠습니다. 이와 관련된 종속성 주입을 사용하여 저장 처리 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(); 
      } 
   } 
}

저장 처리를 수행하는 간단한 방법은 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; 
         }
      } 
   } 
}

위의 코드가 컴파일되고 실행되면 동일한 출력이 표시되지만 이제 ViewModel이 더 느슨하게 분리됩니다.

고객 추가 버튼을 누르면 다음과 같은 화면이 나타납니다. 사용자가 필드를 비워두면 강조 표시되고 저장 버튼이 비활성화됩니다.