MVVM Dependency Injection with Service Locator
-
I want to automatically bind Views to ViewModels that consume services. For example, my sample app uses an RSS Service to get RSS feeds. In production I want the concrete service. If I want to run Unit Tests on it, I want to pass in a mock service.
public MainWindowViewModel(IRssService rssService)
{
_rssService = rssService;
}First, I created a Service Locator class. This class works fine:
public class ServiceLocator : IServiceLocator
{
private IDictionary<object, object> _services;public ServiceLocator(bool isTestMode) { \_services = new Dictionary<object, object>(); if (isTestMode) { // Add mock services to the dictionary this.\_services.Add(typeof(IRssService), new FakeRssService()); } else { // Add Runtime services to the dictionary this.\_services.Add(typeof(IRssService), new RssService()); } } public T Get<T>() { try { return (T)\_services\[typeof(T)\]; } catch (KeyNotFoundException) { throw new ApplicationException("The requested service is not registered"); } }
}
Next, I created a ViewModelLocator class
public class ViewModelLocator
{
// List of ViewModels
private List<object> _viewModels;// Service Locator private IServiceLocator \_serviceLocator; public bool IsTestMode { get; set; } // Main Window View Model public MainWindowViewModel MainWindowVM { get { return Get<MainWindowViewModel>(); } } //CTOR internal ViewModelLocator() { \_serviceLocator = new ServiceLocator(IsTestMode); \_viewModels = new List<object>(); \_viewModels.Add(new MainWindowViewModel(\_serviceLocator.Get<IRssService>())); } // Get to retrieve the VM public T Get<T>() { try { var vm = (T)\_viewModels.FirstOrDefault(x => x is T); return vm; //return (T)\_viewModels\[typeof(T)\]; } catch (KeyNotFoundException) { throw new ApplicationException("The requested service is not registered"); } }
}
Next I wired it all up XAML - App
<Application x:Class="DIExample1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DIExample1"
StartupUri="MainWindowView.xaml"><Applic