Ninject: How do I inject into a class library?
Asked Answered
F

2

6

To start I'm using Ninject 1.5. I have two projects: Web project and a Class library. My DI configuration is within the Web project. Within my class library I have the following defined:

    public interface ICacheService<T>
    {
            string Identifier { get; }
            T Get();
            void Set( T objectToCache, TimeSpan timeSpan );
            bool Exists();
    }

And then a concrete class called CategoryCacheService.

In my web project I bind the two:

Bind( typeof( ICacheService<List<Category>> ) ).To( typeof(CategoryCacheService)).Using<SingletonBehavior>();

In my class library I have extension methods for the HtmlHelper class, for example:

public static class Category
 {
  [Inject]
  public static ICacheService<List<Category>> Categories { get; set; }

  public static string RenderCategories(this HtmlHelper htmlHelper)
  {
   var c = Categories.Get();

   return string.Join(", ", c.Select(s => s.Name).ToArray());
  }
 }

I've been told that you cannot inject into static properties, instead I should use Kernel.Get<>() - However... Since the code above is in a class library I don't have access to the Kernel. How can I get the Kernel from this point or is there a better way of doing this?

Forwent answered 30/10, 2009 at 17:18 Comment(0)
U
8

Good question to ask.

Half the idea of using DI is to remove the concern/fine tuning of the instancing behavior from the code under injection. Therefore, it may make more sense to change the Category class to no longer be static, declare its dependencies in a ctor and let the client code stitch it together.

Regarding how to access it if you're really sure its a good idea... Generally the idea in your case would be to create a CacheServiceResolver and register it [in your Web Project]. Then pass it the Kernel instance as its being constructed. That way your DLL is only bound to the interface of your CacheServiceResolver.

The other approach often used is to have a last-resort 'Service Locator' facility somewhere global, which exposes a 'GlobalGet'. But that's generally A Bad Idea and should only be used for temporary Duct Taping purposes.

The other thing to look at is the Common Service Locator, which will allow one to make a library container-neutral, though outside of EL, you wont find a lot of usage as you shouldnt really show your container.

The other option is to demand a Func<T> factory method and Bind that to a lambda that resolves it, extracting that lookup from your code.

EDIT: In Ninject 2, there's no need to explicitly pass in Kernel instances as I've said - one can simply ask for an IKernel in your ctor and you'll get it, regardless of whether the resolution request expclicitly passes one in.

EDIT 2: Really unhappy with my answer, have tried to make it more general without butchering it too much. Summary is that the desirable options are generally in the following in order:

  1. no container artifacts, leave stitching to client
  2. provide a container neutral extension point, tailored to specifically accomplish something in the context of your library using terminology in the Ubiquitous Language of your Library's Domain in lieu of neutralised abstract container terms
  3. provide a container-neutral integration approach a la Common Service Locator
  4. only then consider having people needing to
    • know your container
    • understand your container
Unquote answered 31/10, 2009 at 7:38 Comment(2)
Ruben, the CacheServiceResolver seems like the right idea for me since I can re-use that pattern in the future. Do you have any examples on setting up a resolver?Forwent
Sadly I havent got one to hand. The general idea would be that wherever you're creating your new StandardKernel, you stash a reference to it. Then you have something as simple as void RegisterCacheServiceResolver(Kernel kernel) {Bind<ICacheServiceResolver>().ToConstant( new CacheServiceResolver( kernel));} on one of the modules, to which you pass the kernel. Or you could create the Kernel, and then pass it to the constructor of any Module that needs it, after which you add the Module [as opposed to passing them all into the constructor of Kernel as one typically does].Unquote
F
4

In the web project run the following command from the Package Manager Console.

Install-Package Ninject.MVC3

You should end up with a NinjectWebCommon module in the App_Start folder.

Towards the bottom you can add your dependencies like below:

private static void RegisterServices(IKernel kernel)
{   
 kernel.Bind<IPeopleRepository>().To<PeopleRepository>();
}

From your class library project run the following command:

Install-Package Ninject

This is how to inject a service with a repository from the class library:

public class PeopleService : IPeopleService
{
 private readonly IPeopleRepository _peopleRepository;

 [Inject]
 public PeopleService(IPeopleRepository peopleRepository)
 {
    this._peopleRepository = peopleRepository;
 }
}
Farnese answered 24/4, 2013 at 12:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.