Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to tell Ninject to bind to an implementation it doesn't have a reference to

I'm using NinjectMVC3 in my ASP.NET MVC3 project.

I have 3 layers

  • Foo.Web
  • Foo.Services
  • Foo.Data

Foo.Web references Foo.Services but not Foo.Data. One of my services looks like this

public class FooService : IFooService
{
    private readonly IFooRepository _fooRepository;

    public FooService(IFooRepository fooRepository)
    {
        _fooRepository = fooRepository;
    }

    // ...
}

NinjectMVC3 executes this bootstrapping method in the Foo.Web startup

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<IFooService>().To<FooService>();
    kernel.Bind<IFooRepository>().To<FooRepository>();
    // Foo.Web doesn't know what FooRepository is
}        

How can I inject FooServices dependencies from Foo.Web?

like image 372
MVCDatabaseInitializer Avatar asked Jun 05 '11 04:06

MVCDatabaseInitializer


1 Answers

To get you pointed in the right direction, I'd suggest you take a look at the onion architecture.

It's basic premise is that any code can depend on layers more central. In your scenario (and it's a common one for MVC3 apps using the Repository pattern) your UI should have a reference to the services layer and it's acceptable to have a reference to the data layer.

If you're willing to accept that (it's a hard pill to swallow if you're coming from a classic N-tier setup, I know), then your situation becomes much simpler.

With Ninject you now do something as follows:

In your NinjectMVC3.cs file your CreateKernel becomes

    /// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    private static IKernel CreateKernel()
    {
        var modules = new INinjectModule[]
                          {
                              new ServiceDIModule(),
                              new RepositoryDIModule()
                          };

        var kernel = new StandardKernel(modules);

        //RegisterServices(kernel); <-- Only if you have some custom binding 
        //                              that are UI specific
        return kernel;
    }

Now, in your services layer, you add a reference to Ninject (just plain Ninject via NuGet, not the MVC3 dropin via NuGet) and add what I called above the ServiceDIModule which looks like this:

using Ninject.Modules;

namespace MyServiceLayer
{
    public class ServiceDIModule : NinjectModule
    {
        public override void Load()
        {
            //Bind Services
            Bind<IPracticeService>().To<PracticeService>().InRequestScope();
        }
    }
}

and you repeat the same process for the Data Tier injections you may have (UnitofWork, DatabaseFactory, IFooRepository, etc.)

namespace MyDataLayer
{
    public class RepositoryDIModule : NinjectModule
    {
        public override void Load()
        {
            //Bind Repos
            Bind<IFooRepository>().To<FooRepository>().InRequestScope();
        }
    }
}

Now, you have access to all of the bindings you need way upfront. So, your question to me really boils down to a shift in thinking. If you can accept (begrudgingly or otherwise) the onion concept, your scenario resolves itself cleanly.

You can also check out Project Silk and CodeCampServer. They both implement the "onion" concept to some degree (Silk for sure, I'm looking at the solution right now and the UI web project contains a ref to the Data proj which contains all the Repos)

Let me know what you think.

like image 101
Khepri Avatar answered Oct 18 '22 14:10

Khepri