XN16
XN16

Reputation: 5879

Ninject ActivationException in WinForms application

I am working on a project to implement DI (using Ninject) into an existing WinForms application, however I have hit a few issues and my knowledge of working with DI in WinForms is limited at best, however I have successfully used it several times in MVC projects.

I get this message when trying to create a form that has a constructor that requires an interface of a repository:

Error activating IProductionRepository
No matching bindings are available, and the type is not self-bindable.
Activation path:
  2) Injection of dependency IProductionRepository into parameter 
     productionRepository of constructor of type Main
  1) Request for Main

I have searched on the web, but most of the articles I have read on this error either a dealing with much more complicated setups or are regarding parameter injection, which I am not sure is the issue here.

I have a form that is to launch the form that is to use DI (the error occurs on the kernel.Get call:

Private Sub txt_Click(sender As System.Object, e As System.EventArgs) Handles txt.Click

    Try
        Dim kernel As Ninject.IKernel = 
            New Ninject.StandardKernel(New NinjectFactory())
        Dim form As Main = kernel.Get(Of Main)() 
        form.ConnectionString = App.pstrConnectString
        form.ShowDialog(Me)
    Catch ex As Exception
        Support.ErrorHandler.ReportError(ex.Message, ex, True)
    End Try

End Sub

I have my NinjectFactory with the correct binding (and several other commented out attempts):

public class NinjectFactory : NinjectModule
{
    private IKernel _ninjectKernel;

    public NinjectFactory()
    {
        _ninjectKernel = new StandardKernel();
    }

    public override void Load()
    {
        //_ninjectKernel.Bind(typeof(IRepository<>))
        //    .To(typeof(GenericRepository<>));
        //_ninjectKernel.Bind(typeof(IProductionRepository))
        //    .To(typeof(ProductionRepository));
        _ninjectKernel.Bind<IProductionRepository>().To<ProductionRepository>();
    }
}

Finally I have the form I am trying to launch:

private IProductionRepository _productionRepository;

public string ConnectionString 
{
    get
    {
        return _connectionString;
    }
    set
    {
        _connectionString = value;
    } 

    [Inject]
    public Main(IProductionRepository productionRepository) : this()
    {   
        this._productionRepository = productionRepository;        
    }

    public Main()
    {
        InitializeComponent();
    }

This is how I have used Ninject in MVC projects before and I haven't had an issue, however obviously there is something different regarding WinForms.

Any help would be greatly appreciated.

Upvotes: 0

Views: 363

Answers (1)

Ilya Ivanov
Ilya Ivanov

Reputation: 23626

I suggest to use single point of dependency resolution, know as Composition Root, suggested by Mark Seemann (@mark-seemann) in his great book Dependency Injection in .NET. Your CompositionRoot might look like this:

public class CompositionRoot
{
    private static IKernel _ninjectKernel;

    public static void Wire(INinjectModule module)
    {
        _ninjectKernel = new StandardKernel(module);
    }

    public static T Resolve<T>()
    {
        return _ninjectKernel.Get<T>();
    }
}

public class ApplicationModule : NinjectModule
{
    public override void Load()
    {
        Bind(typeof(IRepository<>)).To(typeof(GenericRepository<>));
    }
}

You can wire them in Program

CompositionRoot.Wire(new ApplicationModule());

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

Application.Run(CompositionRoot.Resolve<Form1>());

Now your button handler could look like this:

private void button1_Click(object sender, EventArgs e)
{
    MessageBox.Show(_productionRepository.ToString());
}

Note: if you want to test your application with different dependencies, which is probably the case, then you create another module with different wiring configuration. In tests you will have another wiring logic with stubs and mocks. I also don't like NInject attributes on my model and if you use constructor injection - you can get rid of them. My entities are simple POCOs.

public interface IProductionRepository
{
}

public class ProductionRepository : IProductionRepository
{
    public override string ToString()
    {
        return "Foo";
    }
}

Mark also makes a great case for WinForms with DI pattern and how it can be implemented, i really suggest his book.

Upvotes: 2

Related Questions