I recently had the opportunity to create a new prism-based application. I had been using the 6.3 version for quite a while, but saw the prism 7 had moved out of prerelease and wanted to give it a try. I created a new prism application using the Prism Template pack and all worked as expected out of the box. I updated the view model like typically do in 6.3 to pass in the Container so I could resolve some objects that at a later time would provide information to the view, in 6.3 I would do the following:
public MainWindowViewModel(IRegionManager aRegionManager,
IUnityContainer aUnityContainer) : base()
Now in 7.1.0.431, I tried to do that same thing, but updated the interfaces to account for the new IOC abstraction.
public MainWindowViewModel(IRegionManager aRegionManager,
IContainerProvider aContainerProvider,
IContainerRegistry aContainerRegistry) : base()
This generates an exception from the ViewModelLocator.AutoWireViewModel for the IContainerX parameters.
System.Exception {Unity.Exceptions.ResolutionFailedException}
{"Resolution of the dependency failed, type = 'Sample.ViewModels.MainWindowViewModel', name = '(none)'.\nException occurred while: while resolving.\nException is: InvalidOperationException - The current type, Prism.Ioc.IContainerProvider, is an interface and cannot be constructed. Are you missing a type
That acts like I am missing a reference, but I that type is being passed into the RegisterTypes call of the application, so all references should be found. Am I doing something wrong for the new 7.X release?
EDIT: Per @mnistic
Here is the code from the template pack provided App.xaml.cs where the IContainerRegistry is passed in.
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
//containerRegistry is a valid instance here
}
Update:
Digging in a little more, the IContainerRegistry that was passed into RegisterTypes lists all of the types/interfaces that are available at the time that method was called. It has an IUnityContainer instance registered. I selected Unity for the IOC when I created the project, but I assumed, maybe incorrectly, that the IContainerRegistry was hiding the clients from the actual implementation. If I update the ViewModel constructor to take in a object of IUnityContainer, it resolves properly.
public MainWindowViewModel(IRegionManager aRegionManager,
IUnityContainer aContainerProvider) : base()
Is this the desired behavior?
Do not do this. You do not want to have the container outside your resolution root, it is horrible to test, hides otherwise obvious dependencies and comes with no benefit at all.
If you need services, inject them directly. If you need factories, inject Func<IProduct>
or IHandcraftedFactory
. If you need all registered types that implement ISomething
, inject ISomething[]
or IEnumerable<ISomething>
.
Example (complex) factory with product:
public interface IFactory
{
IProduct CreateProduct( int someParameter );
}
internal class DeviceFactory : IFactory
{
public DeviceFactory( IService service )
{
_service = service;
}
public IProduct CreateProduct( int someParameter ) => new Device( someParameter, _someService );
private readonly IService _service;
private class Device : IProduct
{
public Device( int someParameter, IService aDependency )
{
// ...
}
}
}
If Device
did not have someParameter
, you'd skip IFactory
and DeviceFactory
and just inject an Func<IProduct>
... Unity takes care that each Device
receives its IService
then.
Remember - the container is there to simplify things: it resolves the dependencies and creates instances and manages singletons. But if you had no container, everything would still work fine, as is the case in your unit tests. You just have to create all dependencies by hand.
Back to the topic at hand - the IContainerRegistry
is just a short-lived, thin wrapper around IUnityContainer
(in your case), so that the registration code looks somewhat similar in different apps using different containers. Prism tries to push you in the right direction (see above) by not registering the IContainerRegistry
so that you use it where you're supposed to use it (during module initialization) and prevents you from using it elsewhere (by making it impossible to inject it).