I'm familiar with WPF and MVVM pattern. Now, I'm trying to practice Dependency Injection Pattern in my new WPF app with Autofac. And I want to figure out how to inject dependency into MVVM View Model class.
As code attached below, I have a root view model class MainViewModel
, it can create other view model (eg. MonitorPageViewModel
) instance when needed. In MonitorPageViewModel
, it also need to create other sub view model (eg. MonitorDashboardViewModel
) instance when required. And all these view models may have several dependencies (ILogger
, IRepository<RawMessage>
, IParsingService
, and so on).
In my previous WPF projects without using any IoC container, I always new
a sub view model in parent view model when needed and use a ServiceLocator to provide the services needed. Now, I want to figure out a more dependency injection way to do this.
I get several ways (demonstrated in code below), but I'm not satisfied with any one of it.
- use the IoC container take the response to create
MainViewModel
; and inject the IoC Container instance toMainViewModel
; then, use the IoC container instance to resolve every object the child view models constructor need. If the child view model class need to resolve other class, then inject the IoC into it. [It sounds another ServiceLocator]. - inject all the services needed by
MainViewModel
and its descendant view models and pass services along the view model chain. Andnew
the view model instance at where it's needed. [Need to inject a lot of services, and pass them down]
I don't want to use a MVVM framework such as Caliburn.Micro
in this project. Is there a simple yet elegant solution?
public interface ILogger
{
// ...
}
public interface IRepository<T>
{
// ...
}
public interface IStatisticsService
{
// ...
}
public class RawMessage
{
// ...
}
public class Device
{
// ...
}
public class Parser
{
// ...
}
public interface IParsingService
{
void Parse(Parser parser);
}
public class DockPaneViewModel : ViewModelBase
{
// ...
}
public class HomePageViewModel : DockPaneViewModel
{
public HomePageViewModel(ILogger logger)
{
// ...
}
}
public class MonitorDashboardViewModel : DockPaneViewModel
{
public MonitorDashboardViewModel(IStatisticsService statisticsService)
{
// ...
}
}
public class MonitorPageViewModel : DockPaneViewModel
{
public MonitorPageViewModel(ILogger logger, IRepository<RawMessage> repository,
IRepository<Parser> parserRepository, IParsingService parsingService)
{
// ...
}
public void CreateDashboard()
{
IStatisticsService statisticsService = ??; // how to resolve the service?
var dashBoardVm = new MonitorDashboardViewModel(statisticsService); // how to create this?
}
}
public class ResourceManagementViewModel : DockPaneViewModel
{
public ResourceManagementViewModel(ILogger logger, IRepository<Device> deviceRepository)
{
// ...
}
}
Here is the MainViewModel
with alternative constructors
public class MainViewModel : ViewModelBase
{
public ObservableCollection<DockPaneViewModel> DockPanes
{
get;
set;
} = new ObservableCollection<DockPaneViewModel>();
#region approach 1
// use the IOC container take the response to create MainViewModel;
// and inject the Ioc Container instance to MainViewModel;
// then, use the IOC container instance to resovle every thing the child view models need
private readonly ISomeIocContainer _ioc;
public MainViewModel(ISomeIocContainer ioc)
{
_ioc = ioc;
}
public void ResetPanes_1()
{
DockPanes.Clear();
DockPanes.Add(new HomePageViewModel(_ioc.Resolve<ILogger>())); // how to new child view model and how to provide the constructor parameters?
DockPanes.Add(new MonitorPageViewModel(_ioc.Resolve<ILogger>(),
_ioc.Resolve< IRepository<RawMessage>>(),
_ioc.Resolve<IRepository<Parser>>(),
_ioc.Resolve<IParsingService>())); // also need to inject ISomeIocContainer to MonitorDashboardViewModel for resolve IStatisticsService
DockPanes.Add(new ResourceManagementViewModel(_ioc.Resolve<ILogger>(),
_ioc.Resolve<IRepository<Device>>()));
// add other panes
}
#endregion
#region approach 2
// pasing all dependencies of MainViewModel and all descendant View Models in to MainViewModel,
// and pass dependencies along the ViewModel chain.
private readonly ILogger _logger;
private readonly IRepository<RawMessage> _repository;
private readonly IRepository<Parser> _parserRepository;
private readonly IRepository<Device> _deviceRepository;
private readonly IParsingService _parsingService;
private readonly IStatisticsService _statisticsService;
public MainViewModel(ILogger logger, IRepository<RawMessage> repository,
IRepository<Parser> parserRepository, IRepository<Device> deviceRepository,
IParsingService parsingService, IStatisticsService statisticsService)
{
_logger = logger;
_repository = repository;
_parserRepository = parserRepository;
_deviceRepository = deviceRepository;
_parsingService = parsingService;
_statisticsService = statisticsService;
}
public void ResetPanes_2()
{
DockPanes.Clear();
DockPanes.Add(new HomePageViewModel(_logger)); // how to new child view model and how to provide the constructor parameters?
DockPanes.Add(new MonitorPageViewModel(_logger, _repository, _parserRepository, _parsingService)); // also need pass statisticsService down
DockPanes.Add(new ResourceManagementViewModel(_logger, _deviceRepository));
// add other panes
}
#endregion
}