question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

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

See original GitHub issue

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.

Issue Analytics

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

github_iconTop GitHub Comments

1reaction
nathan-alden-hpcommented, Mar 4, 2020

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. 👍

0reactions
canton7commented, Mar 6, 2020

Closing as resolved. I won’t merge that commit which tries Application.Current

Read more comments on GitHub >

github_iconTop Results From Across the Web

Autofac instance per lifetime scope only on child scope, not ...
I'm using autofac in my application and struggling to get this situation working. I registered my type as a PerLifetimeScope, but this rule ......
Read more >
Controlling Scope and Lifetime - Autofac's documentation!
If you resolve IDisposable items from the root lifetime scope (container) they will be held until the container is disposed, which is generally...
Read more >
ObjectDisposedException in child lifetime scopes of ...
I've reproduced a situation related to #971 Actually it's related indirectly to async code and unobserved tasks. You can reproduce the error ...
Read more >
Resolve from root lifetime scope
The brute force way to do this is to take a dependency on ILifetimeScope, then down-cast it to ISharingLifetimeScope (http://api.autofac.org/html/260FE584.htm).
Read more >
Working with Lifetime Scopes — Autofac 7.0.0 documentation
You can create a lifetime scope by calling the BeginLifetimeScope() method on any existing lifetime scope, starting with the root container. Lifetime scopes...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found