canton7 / Stylet

A very lightweight but powerful ViewModel-First MVVM framework for WPF for .NET Framework and .NET Core, inspired by Caliburn.Micro.
MIT License
995 stars 144 forks source link

Multiple registrations for type Container found (StyletIoCRegistrationException) #114

Closed HelloWorld183L closed 4 years ago

HelloWorld183L commented 4 years ago

I'm trying to use the StyletIoC to build my IoC container but I am getting a NullReferenceException when builder.BuildContainer() is called as the RootViewModel is null even though I've inherited the default Bootstrapper class with my ShellViewModel as the TRootViewModel. I've checked all the Stylet samples and templates and according to them, I've set up Stylet just fine. Question: What could potentially be the cause for this NullReferenceException?

EDIT: I fixed this problem by using the ".ToSelf();" method for the ShellViewModel. However, I now have a "StyletIoCRegistrationException" where there's multiple registrations of type Container found (but I have no binding for Container).

New question: What could cause this exception?

canton7 commented 4 years ago

Please post your bootstrapper

HelloWorld183L commented 4 years ago

Here is the bootstrapper code:

using Dynamensions.Infrastructure.Busses.MessageBus;
using PersonalStatementTool.Core;
using Stylet;
using StyletIoC;

namespace PersonalStatementTool.Presentation2.Views
{
    public class Bootstrapper : Bootstrapper<ShellViewModel>
    {
        protected override void ConfigureIoC(IStyletIoCBuilder builder)
        {
            BindViewModels(builder);
            BindServices(builder);

            builder.BuildContainer();
        }

        protected override void Configure()
        {
            base.Configure();

            var viewManager = this.Container.Get<ViewManager>();
            viewManager.NamespaceTransformations.Add("PersonalStatementTool.Presentation2", "PersonalStatementTool.Presentation2.Views");
            viewManager.NamespaceTransformations.Add("PersonalStatementTool.Core", "PersonalStatementTool.Core");

            var mainWindow = new ShellView();
            var mainViewModel = this.Container.Get<ShellViewModel>();
            mainWindow.DataContext = mainViewModel;
            mainWindow.Show();
        }

        private void BindViewModels(IStyletIoCBuilder builder)
        {
            builder.Bind<ShellViewModel>().ToSelf();
            builder.Bind<CourseViewModel>().ToSelf().InSingletonScope();
            builder.Bind<SkillsViewModel>().ToSelf().InSingletonScope();
            builder.Bind<WorkExperienceViewModel>().ToSelf().InSingletonScope();
        }

        private void BindServices(IStyletIoCBuilder builder)
        {
            builder.Bind<IMessageBus>().To<MessageBus>().InSingletonScope();
            builder.Bind<IViewManager>().To<ViewManager>().InSingletonScope();
            builder.Bind<IWindowManager>().To<WindowManager>().InSingletonScope();
        }
    }
}
canton7 commented 4 years ago
  1. You shouldn't need to call BuildContainer yourself -- the base Bootstrapper does it
  2. How come you're showing the main window yourself? The base Bootstrapper will show the window for ShellViewModel
  3. How come you're binding IViewManager and IWindowManager yourself? The base bootstrapper binds those (unless you've got your own implementations?)
  4. You shouldn't need to self-bind ShellViewModel: concrete types are automatically self-bound in transient scope.
canton7 commented 4 years ago

but I am getting a NullReferenceException when builder.BuildContainer() is called as the RootViewModel is null

That doesn't make sense -- BuildContainer shouldn't throw a NullReferenceException. What exactly was the stack trace?

HelloWorld183L commented 4 years ago
  1. You shouldn't need to call BuildContainer yourself -- the base Bootstrapper does it
  2. How come you're showing the main window yourself? The base Bootstrapper will show the window for ShellViewModel
  3. How come you're binding IViewManager and IWindowManager yourself? The base bootstrapper binds those (unless you've got your own implementations?)
  4. You shouldn't need to self-bind ShellViewModel: concrete types are automatically self-bound in transient scope.
  1. Ah I hadn't known that. According to the StyletIoC Configuration wiki page, it says that I must call the BuildContainer method myself (https://github.com/canton7/Stylet/wiki/StyletIoC-Configuration). Perhaps the wiki page could do with an update to this new information?
  2. I hadn't known this previous information before, hence why I was showing the window. Is there a part of the wiki I may have skipped over by mistake?
  3. This was some leftover experimental code that I had before finding out that the base bootstrapper binds those.
  4. This was also experimental code to try debug this issue, I hadn't realised that I still kept my experimental code.

When I remove the method call for "BuildContainer()", I end up getting the same exception with a different message (No registrations found for service ViewManager), implying that removing the method call made a difference.

HelloWorld183L commented 4 years ago

but I am getting a NullReferenceException when builder.BuildContainer() is called as the RootViewModel is null

That doesn't make sense -- BuildContainer shouldn't throw a NullReferenceException. What exactly was the stack trace?

More specifically, the RootViewModel itself threw a NullReferenceException when I've looked in VS Locals. I believe this should be the stack trace:

   at StyletIoC.StyletIoCBuilder.<>c__DisplayClass14_0.<BuildContainer>b__3(BuilderBindTo binding) in /_/Stylet/StyletIoC/StyletIoCBuilder.cs:line 177
   at System.Linq.Enumerable.WhereListIterator`1.MoveNext()
   at StyletIoC.StyletIoCBuilder.BuildContainer() in /_/Stylet/StyletIoC/StyletIoCBuilder.cs:line 181
   at PersonalStatementTool.Presentation2.Views.Bootstrapper.ConfigureIoC(IStyletIoCBuilder builder) in C:\Users\Leon\Desktop\PersonalStatementApplication\PersonalStatementTool\src\PersonalStatementTool.Presentation2\Bootstrapper.cs:line 15
   at Stylet.Bootstrapper`1.ConfigureBootstrapper() in /_/Stylet/Bootstrapper.cs:line 38
   at Stylet.BootstrapperBase.Start(String[] args) in /_/Stylet/BootstrapperBase.cs:line 74
   at Stylet.BootstrapperBase.<Setup>b__9_0(Object o, StartupEventArgs e) in /_/Stylet/BootstrapperBase.cs:line 48
   at System.Windows.Application.OnStartup(StartupEventArgs e)
   at System.Windows.Application.<.ctor>b__1_0(Object unused)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
canton7 commented 4 years ago

I've no idea how you got it to fail on that line: in theory that's impossible. Do you have two threads which are trying to set up the bootstrapper at the same time, or something? Can you share your project, or at least something which reproduces the issue?

HelloWorld183L commented 4 years ago

I've no idea how you got it to fail on that line: in theory that's impossible. Do you have two threads which are trying to set up the bootstrapper at the same time, or something? Can you share your project, or at least something which reproduces the issue?

I should be able to share the project soon but what eliminated this exception message was the fact that I used the "ToSelf()" method on the ShellViewModel. Perhaps this is something to do with the binding of the ShellViewModel (I haven't looked at the Stylet code but this is my assumption)?

EDIT: Here's the method that I had altered slightly to fix this problem:

private void BindViewModels(IStyletIoCBuilder builder)
        {
            builder.Bind<ShellViewModel>(); <-- This needs a "ToSelf();" method
            builder.Bind<CourseViewModel>().ToSelf().InSingletonScope();
            builder.Bind<SkillsViewModel>().ToSelf().InSingletonScope();
            builder.Bind<WorkExperienceViewModel>().ToSelf().InSingletonScope();
        }
canton7 commented 4 years ago

Ah yes, I can see how builder.Bind<ShellViewModel>(); could cause that exception. You shouldn't need to bind that type at all though, since concrete types are implicitly self-bound.

canton7 commented 4 years ago

Leaving open, as I should fix that to throw a more descriptive exception.

Are there any problems with the bootstrapper you posted above?

HelloWorld183L commented 4 years ago

Leaving open, as I should fix that to throw a more descriptive exception.

Are there any problems with the bootstrapper you posted above?

Well I'm getting a StyletViewLocationException where it seems to be looking for the view in the wrong namespace as I want it to look for the views in the "PersonalStatementTool.Presentation2.Views" namespace. I may be able to find ways around this as I'm still in the stage where I should be able to find the problem myself.

EDIT: I've managed to get the ShellView to display with the use of the NamespaceTransformations property of the view manager, although this may not work in the future.

viewManager.NamespaceTransformations.Add("PersonalStatementTool.Core", "PersonalStatementTool.Presentation2.Views");
canton7 commented 4 years ago

Yes, it expects your views and viewmodels to be in similar namespaces, although that can be customised as you found out.