Search code examples
c#wpfprismprism-6dryioc

How do I Configure the Module Catalog in Prism using DryIoc instead of Unity?


I'm working with the Prism Library WPF Samples here (specifically, this one).

I'm trying to convert the sample's bootstrapper from using the Unity container to DryIoc. The original code looks like this:

class Bootstrapper : UnityBootstrapper
{
    protected override DependencyObject CreateShell()
    {
        return Container.Resolve<MainWindow>();
    }

    protected override void InitializeShell()
    {
        Application.Current.MainWindow.Show();
    }

    protected override void ConfigureModuleCatalog()
    {
        var catalog = (ModuleCatalog)ModuleCatalog;
        catalog.AddModule(typeof(ModuleAModule));
    }
}

My new code looks like this:

class Bootstrapper : DryIocBootstrapper
{
    protected override DependencyObject CreateShell()
    {
        return Container.Resolve(typeof(MainWindow), true) as DependencyObject;
    }

    protected override void InitializeShell()
    {
        Application.Current.MainWindow.Show();
    }

    protected override void ConfigureModuleCatalog()
    {
        var catalog = (ModuleCatalog)ModuleCatalog;
        catalog.AddModule(typeof(ModuleAModule));
    }
}

But when I try to run the new code, I get the following exception:

enter image description here

The Inner Exception says:

Activation error occurred while trying to get instance of type ModuleAModule, key ""

And the Inner Exception of that Exception says:

Unable to get constructor of DryIoc.Rules using provided constructor selector when resolving DryIoc.Rules {ReturnDefault} as parameter "rules"
  in DryIoc.Container as parameter "container"
  in ModuleA.ModuleAModule.

Code for ModuleAModule:

public class ModuleAModule : IModule
{
    IRegionManager _regionManager;
    Container _container;

    public ModuleAModule(RegionManager regionManager, Container container)
    {
        _regionManager = regionManager;
        _container = container;
    }

    public void Initialize()
    {
        _regionManager.RegisterViewWithRegion("ContentRegion", typeof(PersonList));

        _container.RegisterTypeForNavigation<PersonDetail>();
    }
}

Solution

  • Instead of passing concrete Container type to module constructor - you need to pass IContainer:

    public ModuleAModule(RegionManager regionManager, IContainer container)
    {
        _regionManager = regionManager;
        _container = container;
    }
    

    This will fix your issue, but navigation will not work. To fix navigation - you need to do the same with RegionManager, that is: pass IRegionManager and not concrete type:

    public ModuleAModule(IRegionManager regionManager, IContainer container)
    

    Not only in module itself, but in other places, such as in PersonListViewModel:

    public PersonListViewModel(IRegionManager regionManager)
    

    Passing concrete implementations is not a good practice in general, and here it breaks on multiple levels with DryIoc. That's because both Container and RegionManager, as concrete types, are not registered in DryIoc container (only interfaces). But when you try to resolve unregistered type, DryIoc, instead of throwing exception, will try to create instance of this type (and resolve its dependencies if any).

    With Container that creation just fails. With RegionManager it succeeds but problem is every resolution creates new instance of RegionManager (while IRegionManager interface is registered as singleton). So your module, your PersonListViewModel etc all have different region manager instances, so navigation breaks.