I've been using Unity for some time in my project. I have a singleton Container which I use to register types and/or instances, and later resolve.
today i wanted to automate some of the instantiation by using property or constructor injection.
I started with Logger class. In Application start i have this code:
RegisterType<Logger, Logger>();
than in my ViewModel
[Dependency]
public Logger MyLogger {
get;
set;
}
here is how i instantiate the viewmodel that has this property (in MainWindow.xaml.cs)
private void InitializeViewModel() {
_vm = new MainViewModel(MainGrid);
...
MyContainer.GetInstance().Container.RegisterInstance<MainViewModel>(_vm);
I can't get that [property injector] to work. Does property injection NEED to be paired up with a constructor? I am already using a constructor that has some parameters..
Something's wrong in your example. If you want the Logger to be injected into MainViewModel, you'd have to let the container create the MainViewModel for you.
However, in your code you are creating it by yourself. As I look at it, it should be:
_vm = Container.Resolve<MainViewModel>();
or at least
_vm = new MainViewModel();
Container.BuildUp( _vm );
Instead of using new MainViewModel() to create the viewmodel, you need to have your Unity Container do the creation.
Container.RegisterType<MainViewModel>(
new ContainerControlledLifetimeManager(),
new InjectConstructor(MainGrid)
);
And then you can use Container.Resolve<MainViewModel>() to get your singleton formerly known as _vm.
Note: The ContainerControlledLifetimeManager part tells Unity to only create a single instance of MainViewModel and return it for everything.
Related
New to Simple Injector, trying to get some pieces working for a prototype. I am creating a WPF application that uses Simple Injector and ReactiveUI, but can't seem to get explicit property injection via attribute to trigger. The specific example I am working through is just testing injection of a logger. The plan is to roll this into a decorator, but I have run across the need for attribute injection with previous projects/DI libraries. Just want to verify I am able to use it.
Snippet of the bootstrapping:
private Container RegisterDependencies(Container container = null)
{
container ??= new Container();
// Container initialization that must precede dependency registration
// occurs here
// Enable property injection via the [Import] attribute
container.Options.PropertySelectionBehavior =
new ImportPropertySelectionBehavior();
SimpleInjectorInitializer initializer = new SimpleInjectorInitializer();
Locator.SetLocator(initializer);
Locator.CurrentMutable.InitializeSplat();
Locator.CurrentMutable.InitializeReactiveUI();
container.UseSimpleInjectorDependencyResolver(initializer);
container.RegisterConditional(
typeof(ILogger),
c => typeof(NLogLogger<>)
.MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
container.Register<MainWindow>();
container.Register<ISystem, System>(Lifestyle.Singleton);
container.Verify();
return container;
}
An instance of the System is requested from the DI container in the static RunApplication called from Main:
var system = container.GetInstance<ISystem>();
And here is the property injection in the system:
public class System : ISystem
{
[Import] public ILogger Logger { get; set; }
public System()
{
// Logger is null here. NullReferenceException is thrown
Logger.LogInfo("Creating System");
}
}
At this point in the constructor, the Logger property is null and attempt to log fails with exception. I should mention the ILogger is my own abstraction of NLog. If I instead perform constructor injection:
public System(ILogger logger)
Simple Injector picks up on this and resolves the dependency fine. I have tried changing the Import attribute to a different custom-defined Dependency attribute, no change. Have also tried just instantiating the logger as a singleton, same behavior.
Really appreciate any ideas, I'm running dry on searching forums, the SimpleInjector/ReactiveUI docs, and Steven's DI book.
Edit - here is the PropertySelectionBehavior code as well:
public class PropertySelectionBehavior<T> : IPropertySelectionBehavior
where T : Attribute
{
public bool SelectProperty(
Type implementationType, PropertyInfo propertyInfo) =>
propertyInfo.GetCustomAttributes(typeof(T)).Any();
}
public class ImportPropertySelectionBehavior :
PropertySelectionBehavior<ImportAttribute> { }
2nd Edit - I can take out all of the initialization related to ReactiveUI and still reproduce same behavior. New sample looks like:
private Container RegisterDependencies(Container container = null)
{
container ??= new Container();
container.Options.PropertySelectionBehavior =
new ImportPropertySelectionBehavior();
// Logger registration
container.RegisterConditional(
typeof(ILogger),
c => typeof(NLogLogger<>)
.MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
// UI registration
container.Register<MainWindow>();
//container.Register<MainWindowViewModel>();
container.Register<ISystem, System>(Lifestyle.Singleton);
container.Verify();
return container;
}
You are using the Logger property from inside System's constructor. Properties, however, are only initialized after the constructor finished. If you remove Simple Injector from the equation, and fallback to plain old C#, you would see the same. For instance:
var system = new System() // <-- constructor call
{
Logger = new NLogLogger<System>() // Logger_set is called after the ctor
};
If you run this code, you will see the same NullReferenceException thrown by the constructor of System.
What this means is that you shouldn't use any properties from inside your constructor. Even more broadly, from a DI perspective, you shouldn't use any service inside your constructor (or during construction for that matter) as is described by Mark Seemann here.
Update, the explicit property injection is working fine. It occurs after construction. I imagine there are design reasons for this, although somehow it was contrary to my mental model that the property injection would be performed on-demand/on first use.
Planning on experimenting a bit more to see what control is available over the timing to resolve property dependencies. If anyone who is more experienced has any advice on that or can point me to additional documentation I would welcome it. The decorator sounds like the more elegant way to make sure the logger is available as expected and allow independent lazy loading of decoratee concerns. Some discussion here:
SimpleInjector - "Lazy" Instantiate a singleton that has dependencies on first use
I am using Prism v6.3.0 for a UWP app. I am using the Unity Container for DI.
Following problem occurs: whenever my app navigates to a given view (e.g. MainPage) there seems that a new instance of the corresponding ViewModel is created. I would like to reuse the same VM (basically, to have it only once created).
I am having: prismMvvm:ViewModelLocator.AutoWireViewModel="True" in all of the pages' XAML.
My code never calls directly any VM constructor, so it cannot be that. I am also registering only services in the ConfigureContainer override.
What I've tried is to register the Views in App.xaml.cs (it seems to make no difference):
private void RegisterViews()
{
Container.RegisterInstance(new LoginPage(), new ContainerControlledLifetimeManager());
Container.RegisterInstance(new SettingsPage(), new ContainerControlledLifetimeManager());
Container.RegisterInstance(new MainPage(), new ContainerControlledLifetimeManager());
}
Any hints?
Since no one answered, I tried figuring out on my own once more.
Now, I know that probably the best way to go about it would be to modify the container that Prism uses so it registers all the VMs as singletons. Because that sounded like too much work for this issue, I instead chose to first take a look at the ViewModelLocationProvider class. The ViewModelLocationProvider is used by PRISM to look up the VM for a given View (that has AutoWireViewModel set to true) and to inject the found VM type in the given View's DataContext.
I tried coming up with a factory for a given view that would ensure that only one VM instance would be created ever and found following solution:
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.Register<MainPage>((delegate
{
var vm = Container.Resolve<MainViewModel>();
if (Container.Registrations.All(r => r.RegisteredType != typeof(MainViewModel)))
Container.RegisterInstance<MainViewModel>(vm, new ContainerControlledLifetimeManager());
return vm;
}));
ViewModelLocationProvider.Register<SettingsPage>((delegate
{
var vm = Container.Resolve<SettingsViewModel>();
if (Container.Registrations.All(r => r.RegisteredType != typeof(SettingsViewModel)))
Container.RegisterInstance<SettingsViewModel>(vm, new ContainerControlledLifetimeManager());
return vm;
}));
}
I am building an application which uses dependency injection following the options pattern design. I have a custom ORM class named DataManager. When I create an instance of DataManager I inject the connection string into the class as below.
public class DataManager : CommonDataManager {
private readonly ConnectionStrings _connectionStrings;
public DataManager(IOptions<ConnectionStrings> options) {
this._connectionStrings = options.Value;
}
When creating the DataManager object which parameter should I use? What should the initialisation code look like?
DataManager dm = new DataManager(?);
DI integration means that the DI engine is going to be responsible for object creation. So instead of directly initializing your class, you should rather do the following. Register also your DataManager with DI, so you can later instantiate it from serviceCollection:
In your ConfigureServices method add the following:
services.AddTransient<DataManager>();
Note, that you should decide the lifespan of your instance (I've chosen Transient here, but it's up to you - Singletone, Scoped, ...).
And then when you need to instantiate it, call as follows:
serviceProvider.GetRequiredService<DataManager>();
If, however, you want to go with your approach and want to instantiate the DataManager yourself, you should get the parameter from IoC container as follows:
DataManager dm = new DataManager(sp.GetRequiredService<IOptions<ConnectionStrings>>());
Note: that in both cases I assume that you've already registered the options with the DI in your Startup.ConfigureServices method.
I've been working with Caliburn.Micro and MEF and I'm trying to get a viewmodelfactory implementation working. What I'm attempting is to create a chain of ViewModels for a dialog window (each ViewModel instantiates one or more viewmodels to generate the overall layout of the window). I'm having trouble importing the viewmodelfactory correctly; I can get it without a problem using [ImportingConstructor], however when I try to use [import] I end up with a null reference exception against viewModelFactory.
The "ViewModelFactory" which I have implemented is as per:
http://blog.pglazkov.com/2011/04/mvvm-with-mef-viewmodelfactory.html
and I'm trying to import the viewmodel as per the following:
[Import]
public IViewModelFactory viewModelFactory { get; set; }
and IViewModelFactory itself has an export declared (and works correctly with [ImportingConstructor]
[Export(typeof(IViewModelFactory))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class ViewModelFactory : IViewModelFactory
Attempt 2
MY next effort was trying to add an instance of ViewModelFactory into the composition container:
protected override void Configure()
{
var catalog =
new AggregateCatalog(
AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());
container = new CompositionContainer(catalog);
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue<IViewModelFactory>(new ViewModelFactory());
batch.AddExportedValue(container);
container.Compose(batch);
}
However this results in an error within the ViewModelFactory, stating that the composition container which is Lazy loaded is null.
I'm trying to find a solution that will allow me to still use the Factory approach, as it allows me to use constructor parameters which are currently required as part of my viewmodels.
EDIT
I was able to get this to work by having an "Initialise" function within my viewmodels, using [ImportingConstructor] on my ViewModels with a constructor that only contains the IViewModelFactory declaration. However, this now required me to instantiate the viewmodel and make a call to the "initialise" function whenever I am creating these, so a more elegant approach would be great.
Thanks.
Managed to implement a different solution to this, which was to use:
IoC.Get<*ViewModelName*>();
Still haven't worked out why the [Import] by itself didn't work, however this certainly solved the issue for me.
Technologies
C# 4.0
Prism 4 with Unity for DI
WPF
MVVM
Preface
There are two projects in my solution, MyApp.Shell and MyApp.ModuleFoo
MyApp.Shell's Unity Bootstrapper
protected override IModuleCatalog CreateModuleCatalog()
{
// Module assemblies are read from a directory.
DirectoryModuleCatalog moduleCatalog = new DirectoryModuleCatalog();
moduleCatalog.ModulePath = #".\Modules";
return moduleCatalog;
}
The project MyApp.ModuleFoo contains a View and a View Model.
The ViewModel
// Somehow, Unity sees this class and registers the type.
public class FooViewModel : ViewModelBaseClass
{
public string FooText
{
get { return "Foo!"; }
}
}
The View
<Label Content={Binding FooText} />
The View's Code-behind
// Unity automatically sees this as Constructor Injection,
// which is exactly what I desire.
public FooView(FooViewModel viewModel)
{
DataContext = viewModel;
...
}
MyApp.FooModule's Initialization
Perhaps registering FooView with the region manager is inadvertently registering FooViewModel with Unity?
public void Initialize()
{
var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
regionManager.RegisterViewWithRegion("FooRegion", typeof(FooView));
}
The view correctly displays "Foo!".
Problems
How do I tell Unity to register only a single instance of FooViewModel?
Additionally, (and I'm thinking ahead here), how would I tell unity not to register FooViewModel?
Thanks for the help.
Edit:
Added MyApp.FooModule's Initialization code
Edit (Solution):
It turns out RegisterViewWithRegion has two overloads. From Prism's documentation, when the overload I'm using is used, a new instance of the view is created. I'm assuming this also creates a new instance of FooViewModel.
The other overload uses a delegate to resolve FooView. The documentation says this overload is used in the "ViewModel-first" approach. I'm going to make this question as answered, but if anyone has any additional insight, I'd love to hear.
// Somehow, Unity sees this class and registers the type.
public class FooViewModel : ViewModelBaseClass
...
I am surprised that you say this as Unity does not register types inside the container by default. You have to tell it to do so either programmatically or in the config file.
When you have concrete classes (not interfaces) they will automatically get created by Unity whether they are registered or not. If not the default behavior is to create a new instance each time. No lifetime management is applied also.
As far as your questions:
To register only one type within your initialisation of your module just have.
Container.RegisterType<FooViewModel>(new ContainerControlledLifetimeManager());
The lifetime manager will instruct unity to only create one instance of the view model.