using autofac in MVVM application - c#

My application is going on a break mode after hitting a user button that loads the app setup. I have registered the component in the bootstrapper class.
How can I register the constructor of the user controller in bootstrap class so as to avoid the break?
public class Bootstrapper
{
public IContainer Bootstrap()
{
var builder = new ContainerBuilder();
builder.RegisterType<LoginView>().AsSelf();
builder.RegisterType<SchoolSectionDataService>().As<ISchoolSectionDataService>();
builder.RegisterType<AdminView>().AsSelf();
builder.RegisterType<School>().AsSelf();
builder.RegisterType<MainSchoolSetupViewModel>().AsSelf();
return builder.Build();
}
}
and the user control is:
private MainSchoolSetupViewModel _viewModel;
public School(MainSchoolSetupViewModel schoolSetupViewModel)
{
InitializeComponent();
_viewModel = schoolSetupViewModel;
DataContext = _viewModel;
Loaded += UserControl_Loaded;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
_viewModel.Load();
}

Unfortunately passing viewmodel into user control's constructor is not possible but there few ways around it. The main thing usually is when building combining DI and XAML and MVVM is that only the view models are registered into the container.
Couple options are mentioned in the comments:
Add a static IContainer property in your Bootstrap. Call it in you user control's constructor to get the VM:
public School()
{
InitializeComponent();
_viewModel = Bootstrap.Container.Resolve<MainSchoolSetupViewModel>();
...
Skip DI and instead create the viewmodel instance in XAML:
<UserControl.DataContext>
<local:SchoolViewModel/>
</UserControl.DataContext>
But it's quite likely that you want to there's other possibilities:
Use ViewModelLocator to help out with DI. This is well documented in this answer: https://stackoverflow.com/a/25524753/66988
The main idea is that you create a new ViewModelLocator class:
class ViewModelLocator
{
public SchoolViewModel SchoolViewModel
{
get { return Bootstrap.Container.Resolve<SchoolViewModel>(); }
}
}
And create a static instance of it in App.xaml and use it to create the data context of your user control:
DataContext="{Binding SchoolViewModel, Source={StaticResource ViewModelLocator}}">
For other solutions, one option is to check out source code of some of MVVM Frameworks, like Caliburn.Micro. From Caliburn.Micro you can find ViewModelLocator and ViewLocator which might interest you.

Related

How to move from ServiceLocator to Dependency Injection? Specific example

The problem is moving from the ServiceLocator anti-pattern to Dependency Injection. In view of my inexperience, I can't shift the DI principle to the code implemented now.
Summary
The Summary section is optional for read. You may want to comment on something, advise.
The main purpose of the program is the process of merging placeholder fields of specific information. Number of information makes need to have infrastructure around. Such as forms, services, database. I have some experience with this task. I managed to create a similar program based on WinForms. And it even works! But in terms of patterns, maintenance, extensibility, and performance, the code is terrible. It is important to understand that programming is a hobby. It is not the main education or job.
The experience of implementing the described task on WinForms is terrible. I started studying patterns and new platform. The starting point is UWP and MVVM. It should be noted that the binding mechanism is amazing.
The first problem on the way was solved independently. It is related to navigation in the UWP via the NavigationView located in the ShellPage connected with ShellViewModel. Along with creating a NavigationService. It is based on templates from Windows Template Studio.
Since there is a working WinForms program and its anti-pattern orientation, there is time and a desire to do everything correctly.
Now I'm facing an architecture problem. Called ServiceLocator (or ViewModelLocator). I find it in examples from Microsoft, including templates from Windows Template Studio. And in doing so, I fall back into the anti-pattern trap. As stated above, I don't want this again.
And the first thing that comes as a solution is dependency injection. In view of my inexperience, I can't shift the DI principle to the code implemented now.
Current implementation
The start point of app UWP is app.xaml.cs. The whole point is to transfer control to ActivationService. Its task is adding Frame to Window.Current.Content and navigated to default page - MainPage. Microsoft documentation.
The ViewModelLocator is a singleton. The first call to its property will call constructor.
private static ViewModelLocator _current;
public static ViewModelLocator Current => _current ?? (_current = new ViewModelLocator());
// Constructor
private ViewModelLocator(){...}
Using ViewModelLocator with View (Page) is like this, ShellPage:
private ShellViewModel ViewModel => ViewModelLocator.Current.ShellViewModel;
Using ViewModelLocator with ViewModel is similar, ShellViewModel:
private static NavigationService NavigationService => ViewModelLocator.Current.NavigationService;
Moving to DI
ShellViewModel has NavigationService from ViewModelLocator as shown above. How can I go to DI at this point? In fact, the program is small. And now is a good time to get away from anti-patterns.
Code
ViewModelLocator
private static ViewModelLocator _current;
public static ViewModelLocator Current => _current ?? (_current = new ViewModelLocator());
private ViewModelLocator()
{
// Services
SimpleIoc.Default.Register<NavigationService>();
// ViewModels and NavigationService items
Register<ShellViewModel, ShellPage>();
Register<MainViewModel, MainPage>();
Register<SettingsViewModel, SettingsPage>();
}
private void Register<TViewModel, TView>()
where TViewModel : class
where TView : Page
{
SimpleIoc.Default.Register<TViewModel>();
NavigationService.Register<TViewModel, TView>();
}
public ShellViewModel ShellViewModel => SimpleIoc.Default.GetInstance<ShellViewModel>();
public MainViewModel MainViewModel => SimpleIoc.Default.GetInstance<MainViewModel>();
public SettingsViewModel SettingsViewModel => SimpleIoc.Default.GetInstance<SettingsViewModel>();
public NavigationService NavigationService => SimpleIoc.Default.GetInstance<NavigationService>();
ShellPage : Page
private ShellViewModel ViewModel => ViewModelLocator.Current.ShellViewModel;
public ShellPage()
{
InitializeComponent();
// shellFrame and navigationView from XAML
ViewModel.Initialize(shellFrame, navigationView);
}
ShellViewModel : ViewModelBase
private bool _isBackEnabled;
private NavigationView _navigationView;
private NavigationViewItem _selected;
private ICommand _itemInvokedCommand;
public ICommand ItemInvokedCommand => _itemInvokedCommand ?? (_itemInvokedCommand = new RelayCommand<NavigationViewItemInvokedEventArgs>(OnItemInvoked));
private static NavigationService NavigationService => ViewModelLocator.Current.NavigationService;
public bool IsBackEnabled
{
get => _isBackEnabled;
set => Set(ref _isBackEnabled, value);
}
public NavigationViewItem Selected
{
get => _selected;
set => Set(ref _selected, value);
}
public void Initialize(Frame frame, NavigationView navigationView)
{
_navigationView = navigationView;
_navigationView.BackRequested += OnBackRequested;
NavigationService.Frame = frame;
NavigationService.Navigated += Frame_Navigated;
NavigationService.NavigationFailed += Frame_NavigationFailed;
}
private void OnItemInvoked(NavigationViewItemInvokedEventArgs args)
{
if (args.IsSettingsInvoked)
{
NavigationService.Navigate(typeof(SettingsViewModel));
return;
}
var item = _navigationView.MenuItems.OfType<NavigationViewItem>().First(menuItem => (string)menuItem.Content == (string)args.InvokedItem);
var pageKey = GetPageKey(item);
NavigationService.Navigate(pageKey);
}
private void OnBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args)
{
NavigationService.GoBack();
}
private void Frame_Navigated(object sender, NavigationEventArgs e)
{
IsBackEnabled = NavigationService.CanGoBack;
if (e.SourcePageType == typeof(SettingsPage))
{
Selected = _navigationView.SettingsItem as NavigationViewItem;
return;
}
Selected = _navigationView.MenuItems
.OfType<NavigationViewItem>()
.FirstOrDefault(menuItem => IsMenuItemForPageType(menuItem, e.SourcePageType));
}
private void Frame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw e.Exception;
}
private bool IsMenuItemForPageType(NavigationViewItem item, Type sourcePageType)
{
var pageKey = GetPageKey(item);
var navigatedPageKey = NavigationService.GetNameOfRegisteredPage(sourcePageType);
return pageKey == navigatedPageKey;
}
private Type GetPageKey(NavigationViewItem item) => Type.GetType(item.Tag.ToString());
Update 1
Am I wrong about the equality between ServiceLocator and ViewModelLocator?
Called ServiceLocator (or ViewModelLocator)
Essentially, the current task is to connected View and ViewModel. NavigationService is beyond the scope of this task. So shouldn't it be in ViewModelLocator?
As #Maess notes, the biggest challenge you face (right now) is refactoring the static dependencies into constructor injection. For example, your ShellViewModel should have a constructor like:
public ShellViewModel(INavigationService navigation)
Once you have done that, you can then set up a DI framework (like NInject) with all your dependencies (kind of like your SimpleIoC thing), and, ideally, pull one root object from the container (which constructs everything else). Usually that's the main view model of the application.
I've done this successfully on multiple projects, both WPF and UWP, and it works great. Only thing you have to be careful of is when creating view models at runtime (as you often do), do it by injecting a factory.

WPF C# Ninject with a mainViewModel and multiple viewmodel problem

I'm having problem with with implementing ninject in my application.
My application contains of a MainView view and viewmodel.
The MainView is using resource.xaml to draw the GUI. I'm also binding the viewmodel for each resource in my MainView.xaml.
I'm having a problem with implementing Ninject.
In my MainViewModel I'm creating;
public logViewModel ChangelogViewModel { get; set; } = new logViewModel();
public TabViewModel ToolTabViewModel { get; set; } = new ToolTabViewModel();
which I send as viewmodel content for the resource files.
How can I use ninject to automatically creating those additional viewmodels?
I'm also having problem with using the kernel to bind interfaces.
When my constructor launch, I don't get the functions from the ninject kernel binding.
It seem like at runtime when I check constructor, it only get my connectionstring which is a private variable in the viewmodel. But non of my functions is there.
Bind<IDataAccessor>().To<DataAccessor>().InSingletonScope().WithConstructorArgument("connectionString", connectionString);
Another weird thing is that it doesn't seem like my accessor is running its function when it's called. Does partial class have something to do with this?
I'm working with telerik components.
Like most DI containers, Ninject works with constructor injection.
You tell it what to create when a class/viewmodel constructor asks for a specific interface.
When you Get an instance of that class it provides the parameters you registered.
A quick and dirty example for flavour.
Instead of just a starturl I'm calling startup to show mainwindow.
private void Application_Startup(object sender, StartupEventArgs e)
{
var nj = new StandardKernel();
nj.Bind<IVMOne>().To<VMOne>().InTransientScope();
MainWindow mw = new MainWindow();
mw.DataContext = nj.Get<MainWindowViewModel>();
mw.Show();
}
Here I say when I ask for a IVMOne then give me a VMONe.
MainWindowViewModel wants a IVMOne for it's ctor.
So when I Get a mainwindowviewmodel ninject also gives me a VMOne.
class MainWindowViewModel
{
public IVMOne VMOne { get; set; }
public MainWindowViewModel(IVMOne vmOne)
{
VMOne = vmOne;
}
}
Which I can then bind to:
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBlock Text="{Binding VMOne.Hello}"/>
</Grid>

Make sure that one static class is created before the other

I am developing a C#/WPF/MVVM/UWP app which uses a ViewModelLocator which looks like this:
public class ViewModelLocator
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainPageViewModel MainPage
{
get
{
return ServiceLocator.Current.GetInstance<MainPageViewModel>();
}
}
static ViewModelLocator()
{
// DEBUG LINE: var test = Views.ViewLocator.MainPageKey;
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<MainPageViewModel>();
}
}
I have another class, a ViewLocator, for navigation purposes which looks like this:
public class ViewLocator
{
public static readonly string MainPageKey = typeof(MainPage).Name;
public static readonly string WorkPageKey = typeof(WorkPage).Name;
static ViewLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
var navigationService = new NavigationService();
navigationService.Configure(MainPageKey, typeof(MainPage));
navigationService.Configure(WorkPageKey, typeof(WorkPage));
SimpleIoc.Default.Register<INavigationService>(() => navigationService);
SimpleIoc.Default.Register<IDialogService, DialogService>();
}
}
Before, both classes were combined in ViewModelLocator, however i figured that the ViewModelLocator as part of the "ViewModels-Side-of-things" should be unaware of the views and it's types, which is why I refactored that code into two classes.
My MainPageView then has a button, which triggers a navigation command in the MainPageView.cs
public class MainPageViewModel : ViewModelBase
{
private INavigationService _navigationService;
public RelayCommand CreateNewImageCommand { get; private set; }
public MainPageViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
CreateNewImageCommand = new RelayCommand(CreateNewImage, () => true);
}
public void CreateNewImage()
{
_navigationService.NavigateTo(Views.ViewLocator.WorkPageKey);
}
}
For completeness, here is my App.xaml
<Application
...>
<Application.Resources>
<v:ViewLocator x:Key="ViewLocator" />
<vm:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>
Now what happens is this: if I don't have the DEBUG LINE in ViewModelLocator, at the point at which MainPage requests its ViewModel from the ViewModelLocator, the constructor of ViewLocator has not been called yet and return ServiceLocator.Current.GetInstance<MainPageViewModel>(); throws an exception. If I include the DEBUG LINE, this forces the ViewLocator Constructor to be run first, and everything works fine.
How can I achieve this behaviour without that odd debug line?
You are running into the reason most developers do not use static classes and a service locator.
As suggested in your comments try using dependency injection. This allows inversion of control for the component that manages this wiring.
Look into MEF or Unity as a framework to use rather than rolling your own IoC. Both have advantages and disadvantages but either will work. They also allow for easier and cleaner testing.
MEF
Unity
Despite of all the comments and other answers, I think that I was on the track after all. To solve or rather bypass this problem, I have created added bootstrapping of the SimpleIoC Container in the Startup of the App and move the code from the ViewLocator's constructor there.
I also removed the ServiceLocator calls, which was sort of redundant and rely on SimpleIoC.Default now directly.
The Navigator is still injected into the ViewModels by the SimpleIoC Container. The ViewModelLocator however is necessary work in conjuction with the XAML code.
I recommend this answer as a starting point for others who are also struggling with this issue.
https://stackoverflow.com/a/25524753/2175012
After all, I have taken the side that ServiceLocators are no anti pattern but have to be used carefully.

Re-implementing WindowManager using ModernUI + Caliburn.Micro combination

Here Caliburn.Micro was successfully combined with ModernUI.
But if we want to use multiple windows we also need to re-implement Caliburn's WindowManager to work properly with ModernUI. How can it be done?
UPDATE:
(Additional question about IoC-Container/Dependency Injection)
Ok, as I get it: I used a Constructor Injection here:
public class BuildingsViewModel : Conductor<IScreen>
{
public BuildingsViewModel(IWindowManager _windowManager)
{
windowManager = _windowManager;
}
}
As far as BuildingsViewModel resolved from IoC container,
container itself injected ModernWindowManager implementation of IWindowManager interface because of this line in Bootstrapper's Configure() method:
container.Singleton<IWindowManager, ModernWindowManager>();
If I resolving an object instance from container, it injects all needed dependencies. Like a tree.
1) So now I wonder how can I replace this line using an injection(with interface)?
_windowManager.ShowWindow(new PopupViewModel());
2) If I want my whole project match DI pattern, all objects instances must be injected into ModernWindowViewModel, that resolves from container first?
3) Is it okay to use Caliburn's SimpleContainer for whole project, or better use mature framework like Castle Windsor? Should I avoid mixing?
UPDATE2:
4) Integrating an IoC container into an existing application requires creating this container first(in Main() method of console app for example), and then all object instanses must grow from it with injected dependencies?
Simply create your own derived WindowManager and override EnsureWindow:
public class ModernWindowManager : WindowManager
{
protected override Window EnsureWindow(object rootModel, object view, bool isDialog)
{
var window = view as ModernWindow;
if (window == null)
{
window = new ModernWindow();
window.SetValue(View.IsGeneratedProperty, true);
}
return window;
}
}
Any views that you want to use as popups must be based on ModernWindow and must either use a LinkGroupCollection or you must set the ContentSource property of the window, otherwise there will be no content.
You could possibly make this View-First but it works ViewModel-First using the method above.
e.g. to popup my PopupView I did the following
PopupView.xaml
<mui:ModernWindow x:Class="TestModernUI.ViewModels.PopupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" ContentSource="/ViewModels/ChildView.xaml">
</mui:ModernWindow>
PopupViewModel.cs
public class PopupViewModel : Screen
{
// Blah
}
Code to popup the view from another ViewModel:
public void SomeMethod()
{
_windowManager.ShowWindow(new PopupViewModel()); // Or use injection etc
}
Don't forget to register ModernWindowManager in place of WindowManager in your container!
e.g. using CM's SimpleContainer
container.Singleton<IWindowManager, ModernWindowManager>();
Obviously the only downside I can see to the above is that you can't seem to put content directly in a ModernWindow, so you have to have two UserControls for every popup!
A workaround would be to change EnsureWindow in ModernWindowManager so that it created a UserControl based on ModernWindow and set the ContentSource to the URI of the view you want to load, this will trigger the content loader and wire up your ViewModel. I'll update if I get a minute to try it.
Update:
Ok, so at the moment it's very hacky, but this could be a starting point for something useful. Basically I'm generating a URI based on the namespace and name of the view.
I'm sure there is a more reliable way of doing this, but for my test project it works:
protected override Window EnsureWindow(object rootModel, object view, bool isDialog)
{
var window = view as ModernWindow;
if (window == null)
{
window = new ModernWindow();
// Get the namespace of the view control
var t = view.GetType();
var ns = t.Namespace;
// Subtract the project namespace from the start of the full namespace
ns = ns.Remove(0, 12);
// Replace the dots with slashes and add the view name and .xaml
ns = ns.Replace(".", "/") + "/" + t.Name + ".xaml";
// Set the content source to the Uri you've made
window.ContentSource = new Uri(ns, UriKind.Relative);
window.SetValue(View.IsGeneratedProperty, true);
}
return window;
}
My full namespace for my view was TestModernUI.ViewModels.PopupView and the URI generated was /ViewModels/PopupView.xaml which then was loaded and bound via the content loader automagically.
Update 2
FYI here is my Bootstrapper configure method:
protected override void Configure()
{
container = new SimpleContainer();
container.Singleton<IWindowManager, ModernWindowManager>();
container.Singleton<IEventAggregator, EventAggregator>();
container.PerRequest<ChildViewModel>();
container.PerRequest<ModernWindowViewModel>();
container.PerRequest<IShell, ModernWindowViewModel>();
}
Here I create the container, and register some types.
The CM services such as WindowManager and EventAggregator are both registered against their respective interfaces and as singletons so only 1 instance of each will be available at run time.
The view models are registered as PerRequest which creates a new instance every time you request one from the container - this way you can have the same window popup multiple times without strange behaviour!
These dependencies are injected into the constructor of any objects resolved at run time.
Update 3
In answer to your IoC questions:
1) So now I wonder how can I replace this line using an injection(with interface)? _windowManager.ShowWindow(new PopupViewModel());
Since your viewmodels will now usually need dependencies you need to have some way of injecting them into the instances. If PopupViewModel had several dependencies, you could inject them into the parent class but this would couple the parent viewmodel to PopupViewModel in some way.
There are a couple of other methods you can use to get an instance of PopupViewModel.
Inject it!
If you register PopupViewModel as PerRequest you will get a new instance of it every time you request it. If you only need one popup instance in your viewmodel you can just inject it:
public class MyViewModel
{
private PopupViewModel _popup;
private IWindowManager _windowManager;
public MyViewModel(PopupViewModel popup, IWindowManager windowManager)
{
_popup = popup;
_windowManager = windowManager;
}
public void ShowPopup()
{
_windowManager.ShowPopup(_popup);
}
}
The only downside is that the instance will be the same one if you need to use it multiple times in the same viewmodel, though you could inject multiple instances of PopupViewModel if you knew how many you needed at the same time
Use some form of on-demand injection
For dependencies which are required later on you can use on-demand injection such as a factory
I don't think Caliburn or SimpleContainer support factories out of the box, so the alternative is to use IoC.Get<T>. IoC is a static class which lets you access your DI container after instantiation
public void ShowPopup()
{
var popup = IoC.Get<PopupViewModel>();
_windowManager.ShowWindow(popup);
}
You need to make sure you have correctly registered the container in your bootstrapper and delegated any calls to CM's IoC methods to the container - IoC.Get<T> calls the bootstrapper's GetInstance and other methods:
Here's an example:
public class AppBootstrapper : BootstrapperBase {
SimpleContainer container;
public AppBootstrapper() {
Initialize();
}
protected override void Configure() {
container = new SimpleContainer();
container.Singleton<IWindowManager, ModernWindowManager>();
container.Singleton<IEventAggregator, EventAggregator>();
container.PerRequest<IShell, ModernWindowViewModel>();
// Register viewmodels etc here....
}
// IoC.Get<T> or IoC.GetInstance(Type type, string key) ....
protected override object GetInstance(Type service, string key) {
var instance = container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
// IoC.GetAll<T> or IoC.GetAllInstances(Type type) ....
protected override IEnumerable<object> GetAllInstances(Type service) {
return container.GetAllInstances(service);
}
// IoC.BuildUp(object obj) ....
protected override void BuildUp(object instance) {
container.BuildUp(instance);
}
protected override void OnStartup(object sender, System.Windows.StartupEventArgs e) {
DisplayRootViewFor<IShell>();
}
Castle.Windsor supports factories so that you can Resolve and Release your components and manage their lifetime more explicitly, but I won't go into that here
2) If I want my whole project match DI pattern, all objects instances must be injected into ModernWindowViewModel, that resolves from container first?
You only need to inject the dependencies that the ModernWindowViewModel needs. Anything that is required by children is automatically resolved and injected e.g.:
public class ParentViewModel
{
private ChildViewModel _child;
public ParentViewModel(ChildViewModel child)
{
_child = child;
}
}
public class ChildViewModel
{
private IWindowManager _windowManager;
private IEventAggregator _eventAggregator;
public ChildViewModel(IWindowManager windowManager, IEventAggregator eventAggregator)
{
_windowManager = windowManager;
_eventAggregator = eventAggregator;
}
}
In the above situation, if you resolve ParentViewModel from the container - the ChildViewModel will get all it's dependencies. You don't need to inject them into the parent.
3) Is it okay to use Caliburn's SimpleContainer for whole project, or better use mature framework like Castle Windsor? Should I avoid mixing?
You can mix, but it might be confusing as they won't work with each other (one container won't know about the other). Just stick with one container, and SimpleContainer is fine - Castle Windsor has a lot more features, but you might never need them (I've only used a few of the advanced features)
4) Integrating an IoC container into an existing application requires creating this container first(in Main() method of console app for example), and then all object instanses must grow from it with injected dependencies?
Yes, you create the container, then you resolve the root component (in 99.9% of applications there is one main component which is called the composition root), and this then builds the full tree.
Here is an example of a bootstrapper for a service based application. I'm using Castle Windsor and I wanted to be able to host the engine in a Windows service or in a WPF application or even in a Console Window (for testing/debug):
// The bootstrapper sets up the container/engine etc
public class Bootstrapper
{
// Castle Windsor Container
private readonly IWindsorContainer _container;
// Service for writing to logs
private readonly ILogService _logService;
// Bootstrap the service
public Bootstrapper()
{
_container = new WindsorContainer();
// Some Castle Windsor features:
// Add a subresolver for collections, we want all queues to be resolved generically
_container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel));
// Add the typed factory facility and wcf facility
_container.AddFacility<TypedFactoryFacility>();
_container.AddFacility<WcfFacility>();
// Winsor uses Installers for registering components
// Install the core dependencies
_container.Install(FromAssembly.This());
// Windsor supports plugins by looking in directories for assemblies which is a nice feature - I use that here:
// Install any plugins from the plugins directory
_container.Install(FromAssembly.InDirectory(new AssemblyFilter("plugins", "*.dll")));
_logService = _container.Resolve<ILogService>();
}
/// <summary>
/// Gets the engine instance after initialisation or returns null if initialisation failed
/// </summary>
/// <returns>The active engine instance</returns>
public IIntegrationEngine GetEngine()
{
try
{
return _container.Resolve<IIntegrationEngine>();
}
catch (Exception ex)
{
_logService.Fatal(new Exception("The engine failed to initialise", ex));
}
return null;
}
// Get an instance of the container (for debugging)
public IWindsorContainer GetContainer()
{
return _container;
}
}
Once the bootstrapper is created, it sets up the container and registers all services and also plugin dlls. The call to GetEngine starts the application by resolving Engine from the container which creates the full dependency tree.
I did this so that it allows me to create a service or a console version of the application like this:
Service Code:
public partial class IntegrationService : ServiceBase
{
private readonly Bootstrapper _bootstrapper;
private IIntegrationEngine _engine;
public IntegrationService()
{
InitializeComponent();
_bootstrapper = new Bootstrapper();
}
protected override void OnStart(string[] args)
{
// Resolve the engine which resolves all dependencies
_engine = _bootstrapper.GetEngine();
if (_engine == null)
Stop();
else
_engine.Start();
}
protected override void OnStop()
{
if (_engine != null)
_engine.Stop();
}
}
Console App:
public class ConsoleAppExample
{
private readonly Bootstrapper _bootstrapper;
private IIntegrationEngine _engine;
public ConsoleAppExample()
{
_bootstrapper = new Bootstrapper();
// Resolve the engine which resolves all dependencies
_engine = _bootstrapper.GetEngine();
_engine.Start();
}
}
Here's part of the implementation of IIntegrationEngine
public class IntegrationEngine : IIntegrationEngine
{
private readonly IScheduler _scheduler;
private readonly ICommsService _commsService;
private readonly IEngineStateService _engineState;
private readonly IEnumerable<IEngineComponent> _components;
private readonly ConfigurationManager _configurationManager;
private readonly ILogService _logService;
public IntegrationEngine(ICommsService commsService, IEngineStateService engineState, IEnumerable<IEngineComponent> components,
ConfigurationManager configurationManager, ILogService logService)
{
_commsService = commsService;
_engineState = engineState;
_components = components;
_configurationManager = configurationManager;
_logService = logService;
// The comms service needs to be running all the time, so start that up
commsService.Start();
}
All of the other components have dependencies, but I don't inject those into the IntegrationEngine - they are handled by the container

How to use StructureMap to inject service into a ViewModel referenced from XAML?

I may be going about this all wrong, but I thought I was on the right path. I'm building a WPF app for the first time and am still getting used to some of the ways things are done.
My Thinking
My goal is to make my ViewModel class more testable.
The ViewModel depends on a service class and wouldn't be functional without it.
Therefore, it seems to make sense to me to inject the service into the ViewModel so that I can also start mocking it for testing purposes.
What I did
Made a XAML reference to my view model, which is called "UploaderViewModel":
<Window.DataContext>
<viewModels:UploaderViewModel>
</viewModels:UploaderViewModel>
</Window.DataContext>
NOTE: This works fine before I try to do the injection
Set up structure map in my app.xaml.cs file:
void App_Startup(object sender, StartupEventArgs eventArgs)
{
ContainerBootstrapper.BootstrapStructureMap();
ProcessStartupArguments(eventArgs);
}
Which calls my bootstrapper class:
public static class ContainerBootstrapper
{
public static void BootstrapStructureMap()
{
ObjectFactory.Initialize(
x => x.For<IVEDocumentService>().Use<VEDocumentService>()
);
}
}
I then have my UploaderViewModel class take the IVEDocumentService as a Constructor parameter:
public UploaderViewModel(IVEDocumentService documentService)
{
_documentService = documentService;
//other unrelated code below this
}
What's Going Wrong
I'm receiving an XAML error that says there's no default constructor found for the ViewModel, which makes sense, since I have no UploaderViewModel(), only UploaderViewModel(IVEDocumentService documentService).
Question
What's the best way to resolve this?
Should I create an UploaderViewModel() constructor and make StructureMap call UploaderViewModel with an instance of the service, or is there a better way?
Update 1 -- Attempting to Use a Resource Dictionary, StructureMap error.
Based on this SO Answer to a similar question, I tried:
Updating my App_Startup to add an instance of the UploaderViewModel to a resources dictionary:
void App_Startup(object sender, StartupEventArgs eventArgs)
{
ContainerBootstrapper.BootstrapStructureMap();
var uploaderViewModel = new UploaderViewModel(new Container().GetInstance<IVEDocumentService>());
Current.Resources["UploaderViewModel"] = uploaderViewModel;
ProcessStartupArguments(eventArgs);
}
Added DataContext="{StaticResource UploaderViewModel}" to my <Window> declaration
Commented out my <viewModels:UploaderViewModel/> declaration
Upon doing this, StructureMap now throw an error:
"StructureMap Exception Code: 202No Default Instance defined for
PluginFamily
I figured it out. I think I wasn't configuring the static resource correctly. The solution, using StructureMap, was to do the following:
App.xaml.cs
void App_Startup(object sender, StartupEventArgs eventArgs)
{
ContainerBootstrapper.BootstrapStructureMap();
//Other logic
}
ContainerBootstrapper Class
public static void BootstrapStructureMap()
{
ObjectFactory.Initialize(x => x.For<IVEDocumentService>().Use<VEDocumentService>());
}
MainWindow.xaml.cs
I moved the resource creation into this class:
public MainWindow()
{
var uploaderViewModel = new UploaderViewModel(ObjectFactory.GetInstance<IVEDocumentService>());
this.Resources["UploaderViewModel"] = uploaderViewModel;
InitializeComponent();
}
MainWindow.xaml
On the <Window> definition, I set DataContext="{StaticResource UploaderViewModel}".
Works like a charm. I believe it was a combination of not binding the resources at the proper scope (window).

Categories