Stylet: Exception resolving root view-model when using Autofac child lifetime scopes

I am building a WPF application that loads views and view models from plugin assemblies (called modules) at runtime. Due to the structure of my code, I am unable to use a single Autofac lifetime scope. I create a child lifetime scope where I register components dependent on views, view-models, or Stylet:

public class AutofacBootstrapper<TRootViewModel> : BootstrapperBase
    where TRootViewModel : class
{
    private ILifetimeScope? _lifetimeScope;
    private TRootViewModel? _rootViewModel;

    public sealed override object GetInstance(Type type)
    {
        return _lifetimeScope!.Resolve(type);
    }

    public override void Dispose()
    {
        ScreenExtensions.TryDispose(_rootViewModel);

        _lifetimeScope?.Dispose();

        base.Dispose();
    }

    protected sealed override void ConfigureBootstrapper()
    {
        var containerBuilder = new ContainerBuilder();

        // Register components not dependent on views, view-models, or Stylet
        OnRegisterComponents(containerBuilder);

        IContainer container = containerBuilder.Build();

        _lifetimeScope = container.BeginLifetimeScope(
            scopedContainerBuilder =>
            {
                // Register host views and view-models

                scopedContainerBuilder.RegisterType<NavigationItemView>().AsSelf().ExternallyOwned();
                scopedContainerBuilder.RegisterType<NavigationItemViewModel>().AsSelf().ExternallyOwned();
                scopedContainerBuilder.RegisterType<RootView>().AsSelf().ExternallyOwned();
                scopedContainerBuilder.RegisterType<RootViewModel>().AsSelf().ExternallyOwned();

                scopedContainerBuilder.RegisterType<ModuleFactory>().As<IModuleFactory>().SingleInstance();

                // Load module assemblies

                var moduleAssemblyLoader = container.Resolve<IModuleAssemblyLoader>();
                IReadOnlyCollection<string> moduleAssemblyPaths = moduleAssemblyLoader.FindModuleAssemblyPaths($"*.{IModuleAssembly.FilenameSuffix}.dll");

                moduleAssemblyLoader.LoadModuleAssemblies(container, moduleAssemblyPaths);

                // Register module assembly components

                foreach (IModuleAssembly moduleAssembly in moduleAssemblyLoader.LoadedModuleAssemblies)
                {
                    moduleAssembly.RegisterComponents(scopedContainerBuilder);
                }

                // Register modules

                foreach (Assembly assembly in moduleAssemblyLoader.LoadedAssemblies)
                {
                    scopedContainerBuilder
                        .RegisterAssemblyTypes(assembly)
                        .Where(type => type.IsAssignableTo<IModule>() && type.IsPublic && !type.IsAbstract)
                        .AsSelf();
                }

                // Register Stylet components

                var viewManagerConfig =
                    new ViewManagerConfig
                    {
                        ViewFactory = GetInstance,
                        ViewAssemblies = new List<Assembly> { typeof(App).Assembly }
                    };

                viewManagerConfig.ViewAssemblies.AddRange(moduleAssemblyLoader.LoadedAssemblies);

                scopedContainerBuilder.RegisterType<MessageBoxViewModel>().As<IMessageBoxViewModel>().ExternallyOwned();
                scopedContainerBuilder.RegisterInstance<IViewManager>(new SkinAwareViewManager(container.Resolve<IHostConfiguration>(), viewManagerConfig));
                scopedContainerBuilder.RegisterType<WindowManager>().As<IWindowManager>().SingleInstance();
                scopedContainerBuilder.RegisterInstance<IWindowManagerConfig>(this).ExternallyOwned();
            });
    }

    protected override void Launch()
    {
        DisplayRootView(_rootViewModel = (TRootViewModel)GetInstance(typeof(TRootViewModel)));
    }

    protected override void OnExit(ExitEventArgs e)
    {
        _lifetimeScope?.Dispose();
    }

    protected virtual void OnRegisterComponents(ContainerBuilder containerBuilder)
    {
    }

    protected T GetInstance<T>()
    {
        return (T)GetInstance(typeof(T));
    }
}

You can see how I load module assemblies and then pass them to the ViewManagerConfig at the appropriate time. Unfortunately, I get an exception inside DisplayRootView (note that TRootViewModel is a view-model in the main application assembly, not a module assembly):

System.InvalidOperationException: 'The ViewManager resource is unassigned. This should have been set by the Bootstrapper'

image image Something important to note: This exception only occurs when at least one view-model in a module assembly derives from Screen (which I require). If I remove the derivation, the exception no longer occurs.

I took a look at the Stylet source code and found the place where the exception is thrown, but there’s so much magic going on that I haven’t been able to determine the cause or how to fix it. Do you have any advice for my scenario? If there were a way to change the view assemblies list after DI registration then I wouldn’t have to use the child lifetime scope.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 24 (12 by maintainers)

Commits related to this issue

Most upvoted comments

FWIW, I based my Autofac bootstrapper off the one in the repo. I’ll take a look at your suggestions. Great library, BTW–much cleaner than Caliburn Micro. 👍