Exception resolving root view-model when using Autofac child lifetime scopes
See original GitHub issueI 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'
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:
- Created 4 years ago
- Comments:24 (12 by maintainers)
Top GitHub 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. 👍
Closing as resolved. I won’t merge that commit which tries
Application.Current