Can't get dependency injection through constructors to work

Topics: Questions
Mar 4, 2013 at 10:48 PM
I am unable to get dependency injection through ServiceLocator working. I start a new Catel WPF application in VS2012, use Package Manager Console to install "catel.extensions.controls," register my interface and class in App.xaml.cs, and create an overloaded constructor which takes a variable of my interface in my viewmodel.

What am I doing wrong?
MainWindowViewModel.cs:
using Catel.MVVM;
 
namespace DITest.ViewModels
{
    public class MainWindowViewModel : ViewModelBase
    {
        public MainWindowViewModel(IMyClass mc)
        {
            // Never gets here
        }
    }
}
 
MyClass.cs:
namespace DITest
{
    public class MyClass : IMyClass
    {
        public string Text { get { return "hi";} }
    }
 
    interface IMyClass
    {
        string Text { get; }
    }
}
 
App.xaml.cs:
using System.Windows;
using Catel.IoC;
using Catel.Logging;
using Catel.Windows;
 
namespace DITest
{
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
#if DEBUG
            LogManager.RegisterDebugListener();
#endif
 
            StyleHelper.CreateStyleForwardersForDefaultStyles();
 
            ServiceLocator.Default.RegisterType<IMyClass, MyClass>(RegistrationType.Transient, true);
 
            base.OnStartup(e);
        }
    }
}
Coordinator
Mar 5, 2013 at 5:25 AM
I don't know the parameter names out of my head, but can you please try the RegisterType without the RegistrationType and true (I am not sure whether the true value might be seen as tag, and then it will not work).
Mar 5, 2013 at 5:13 PM
Mr. van Horrik,

Thank you for such a prompt reply!

I did as you asked, which resulted in the following line:
            ServiceLocator.Default.RegisterType<IMyClass,MyClass>();
But it did not change the behavior. I can, however, get an instantiated object of my class using ResolveType(). So
            var myClassObj = Catel.IoC.ServiceLocator.Default.ResolveType(typeof(IMyClass));
works in the parameterless constructor.

I have downloaded the WPF examples and seen dependency injection working with Unity as an external container. Is there a working example of DI using only ServiceLocator I can download and study?

Thanks!
Developer
Mar 6, 2013 at 1:30 PM
Edited Mar 7, 2013 at 8:32 PM
[+] The only thing you have to do is let Catel do its job.

Take a look into this sample PoC.ViewModel.IoC.zip

Dependency Injection (DI) "via View-Model constructor" using Catel ServiceLocator works out of the box. The usage of third party containers like Unity is no longer required. Catel ServiceLocator supports DI.

The easy way to get DI (via View-Model constructor) works following this steps:

1) Make your window inherit from DataWindow (from Catel)

2) Follow the Catel naming convention for the View and its ViewModel for auto-initialization via ViewModelLocator.

3) Register the type into the default service locator instance before ViewModel initialization (for instance: Application Startup event)

[+] In a "tipical" (non View-Model) scenario you can try this:
public interface IClass1 
{
}

public class Class1 : IClass1
{
}

public interface IClass2 
{
}

public class Class2 : IClass2
{
   public Class2(IClass1 class1)
   {
   }
}

var serviceLocator = new ServiceLocator();
serviceLocator.RegisterType<IClass1, Class1>();
serviceLocator.RegisterType<IClass2, Class2>();

var class2 = serviceLocator.ResolveType<IClass2>();
and you should notice how an instance of Class1 is injected.
Mar 7, 2013 at 8:07 PM
Alex,

The link to the sample doesn't work. Would you please repost it?

I have done all three steps that you described, and the default debugging output from Catel includes
[Catel.MVVM.ManagedViewModel] Added view model instance, currently containing '1' instances of type 'CatelDITest.ViewModels.MainWindowViewModel'
[Catel.MVVM.ViewModelFactory] Constructed view model 'CatelDITest.ViewModels.MainWindowViewModel' using dependency injection or empty constructor
The overloaded constructor for MainWindowViewModel that takes an IClass1 argument is never called. If I break in the default constructor and query ServiceLocator for registered types, I see IClass1 and IClass2 registered, and I can manually fetch instances of them from ServiceLocator with no problem. My main window is of type DataWindow, and it's named MainWindowView, and its viewmodel is named MainWindowViewModel.

Why is
        public MainWindowViewModel(IClass1 class1)
            : base()
        {
            Debugger.Break();
        }
never called though IClass1 is registered properly?
Developer
Mar 7, 2013 at 8:33 PM
Edited Mar 7, 2013 at 8:41 PM
Sorry, link fixed
Mar 7, 2013 at 11:13 PM
Edited Mar 7, 2013 at 11:15 PM
Remove this entry, please.
Mar 8, 2013 at 12:31 AM
Alex,

Thank you! The link problem was on my end having to do with my browser running some scripts but not all.

I was able to download your example and get it running in Visual Studio, but on my box it doesn't work. There is one constructor in MainWindowViewModel which takes an IMyClass parameter, and if I set a breakpoint in there, it's never hit. There is also a method in MainWindowViewModel called Initialize() which has a line intending to show that DI worked, but that method never executes.

I added the project to an existing solution and set it to be the startup project. I made no other changes before running.
Developer
Mar 8, 2013 at 1:47 PM
Please, check your Catel packages version.

I shared a new 7z file including the binaries here