Caliburn Micro Constructor Injection Failed
Asked Answered
W

2

13

I am learning Caliburn Micro and try to make use of the EventAggregator from the official site.

However, I got an exception

"No parameterless constructor defined for this object."

The message itself is clear but the example doesn't include a parameterless constructor either. If i add one, the constructor with parameter is not hit and the IEventAggregator is still not injected correctly.

Here is my publisher VM after adding the parameterless constructor (without it, exception will be thrown):

    public MainViewModel() { }

    public MainViewModel(IEventAggregator ea) : this()
    {
        eventAggregator = ea;
    }

And here is the bootstrapper i am using:

public class AppBootstrapper : Bootstrapper<MainViewModel>
{
    private readonly SimpleContainer container = new SimpleContainer();

    protected override void Configure()
    {
        container.Singleton<IEventAggregator, EventAggregator>();
    }
}

Here is the example from CM:

// Creating the EventAggregator as a singleton.
public class Bootstrapper : BootstrapperBase {
    private readonly SimpleContainer _container =
        new SimpleContainer();

     // ... Other Bootstrapper Config

    protected override void Configure(){
        _container.Singleton<IEventAggregator, EventAggregator>();
    }

    // ... Other Bootstrapper Config
}

// Acquiring the EventAggregator in a viewModel.
public class FooViewModel {
    private readonly IEventAggregator _eventAggregator;

    public FooViewModel(IEventAggregator eventAggregator) {
        _eventAggregator = eventAggregator;
    }
}

I checked this post (Inject EventAggregator into ViewModel with Caliburn Micro) but it simply said nothing why the CM do not invoke the constructor with injection.

I also checked the CM's sample solution but it uses MEF as the DI solution.

What do I miss?

Wakashan answered 23/7, 2013 at 5:42 Comment(4)
It's been a while since I looked at Caliburn, but are you sure that your bootstrapper is actually started, can get instances, etc? Check out this caliburnmicro.codeplex.com/SourceControl/latest#samples/… simple container sample, it doesn't use MEF or anythingSelfsustaining
@MaksimS. The Configure() is executed; I tried to add the Start() to the bootstrapper constructor but no luck. Moreover, the example doesn't inject anything in the VM.Wakashan
Don't have anything to test your code on at the moment; however, just took a look at EventAggregator code in the latest build and it does contain parameterless constructor, at least in micro.wpf. Are you by any chance using EventAggregator from Prism, or something else instead of Caliburn's? I know it sounds silly, but still :)Selfsustaining
@MaksimS. Nah I don't use any DI.. I guess have to use other DI framework like prism, mef or ninject after all..Wakashan
C
10
// ... Other Bootstrapper Config

It's the other bootstrapper config that's important.

The best option is to install Caliburn.Micro via the Caliburn.Micro.Start NuGet package, and have a look at the provided bootstrapper which also uses the SimpleContainer provided by Caliburn.Micro.

Here it is in full:

public class AppBootstrapper : BootstrapperBase
{
     SimpleContainer container;

     public AppBootstrapper()
     {
         Start();
     }

     protected override void Configure()
     {  
         container = new SimpleContainer();
         container.Singleton<IWindowManager, WindowManager>();
         container.Singleton<IEventAggregator, EventAggregator>();
         container.PerRequest<IShell, ShellViewModel>();
     }

     protected override object GetInstance(Type service, string key)
     {
         var instance = container.GetInstance(service, key);
         if (instance != null)
             return instance;
         throw new InvalidOperationException("Could not locate any instances.");
     }

     protected override IEnumerable<object> GetAllInstances(Type service)
     {
         return container.GetAllInstances(service);
     }

     protected override void BuildUp(object instance)
     {
         container.BuildUp(instance);
     }

     protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
     {
         DisplayRootViewFor<IShell>();
     }
}
Chill answered 23/7, 2013 at 7:58 Comment(0)
I
12

devdigital is correct. He said

it is the other bootstrapper configuration that is important.

This is totally correct.

If you are going to use DI (which SimpleContainer does for you) then you must override the GetInstance(), GetAllInstances(), BuildUp() methods in your bootstrapper because CM calls those methods when it wants something from the container.

What is happening in your situation is like this:

  1. CM tries to show your MainViewModel because you specified it as the generic parameter in the Bootstrapper.
  2. CM notices that your MainViewModel has one constructor that takes an IEventAggregator.
  3. CM calls GetInstance() to get whatever is registered as an IEventAggregator.
  4. CM figures out that you haven't overriden GetInstance() so it tries to create an instance of MainViewModel using Activator.CreateInstance().
  5. Activator.CreateInstance() doesn't know how create an IEventAggregator so it throws an exception (the one you are getting).

To fix all of this you have to override the methods i told you about. You don't need to install the Nuget Caliburn.Start package (you can use it if you don't like typing and you want to save some keystrokes).

Essentially your final solution should look like this:

public class Bootstrapper : Bootstrapper<MainViewModel>
    {

        private readonly SimpleContainer _container = new SimpleContainer();

        protected override void Configure()
        {
            _container.Instance<IWindowManager>(new WindowManager());
            _container.Singleton<IEventAggregator, EventAggregator>();
            _container.PerRequest<MainViewModel>();
        }

        protected override object GetInstance(Type service, string key)
        {
            return _container.GetInstance(service, key);
        }

        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return _container.GetAllInstances(service);
        }

        protected override void BuildUp(object instance)
        {
            _container.BuildUp(instance);
        }

    }
Inessive answered 23/7, 2013 at 11:55 Comment(0)
C
10
// ... Other Bootstrapper Config

It's the other bootstrapper config that's important.

The best option is to install Caliburn.Micro via the Caliburn.Micro.Start NuGet package, and have a look at the provided bootstrapper which also uses the SimpleContainer provided by Caliburn.Micro.

Here it is in full:

public class AppBootstrapper : BootstrapperBase
{
     SimpleContainer container;

     public AppBootstrapper()
     {
         Start();
     }

     protected override void Configure()
     {  
         container = new SimpleContainer();
         container.Singleton<IWindowManager, WindowManager>();
         container.Singleton<IEventAggregator, EventAggregator>();
         container.PerRequest<IShell, ShellViewModel>();
     }

     protected override object GetInstance(Type service, string key)
     {
         var instance = container.GetInstance(service, key);
         if (instance != null)
             return instance;
         throw new InvalidOperationException("Could not locate any instances.");
     }

     protected override IEnumerable<object> GetAllInstances(Type service)
     {
         return container.GetAllInstances(service);
     }

     protected override void BuildUp(object instance)
     {
         container.BuildUp(instance);
     }

     protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
     {
         DisplayRootViewFor<IShell>();
     }
}
Chill answered 23/7, 2013 at 7:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.