Dependency Injection: How to configure interface bindings for wrapping
Asked Answered
S

3

5

So, let's say I have an interface IThingFactory:

public interface IThingFactory
{
    Thing GetThing(int thingId);
}

Now, let's say I have a concrete implementation that retrieves Things from a database. Now, let's also say I have a concrete implementation that wraps an existing IThingFactory and checks for a Thing's presence in, say, an in-memory cache before hitting the wrapped IThingFactory. Something like:

public class CachedThingFactory : IThingFactory
{
    private IThingFactory _wrapped;
    private Dictionary<int, Thing> _cachedThings;

    public CachedThingFactory(IThingFactory wrapped)
    {
        this._wrapped = wrapped;
        _cachedThings = new Dictionary<int,Thing>();
    }

    public Thing GetThing(int thingId)
    {
        Thing x;
        if(_cachedThings.TryGetValue(thingId, out x))
            return x;

        x = _wrapped.GetThing(thingId);

        _cachedThings[thingId] = x;

        return x;
    }
}

How would I deal with a scenario like this using dependency injection with something like, say, Ninject, so that I could configure the DI container so that I can inject or remove a caching proxy like this, or, say, something that does logging, or (insert here)?

Suffix answered 19/7, 2011 at 19:6 Comment(0)
E
5

You can do something along the lines of:

Bind<IThingFactory> ().To<DefaultThingFactory> ().WhenInjectedInto<CachedThingFactory> ();
Bind<IThingFactory> ().To<CachedThingFactory> ();

This will let consumers not need to specify a name attribute, and is still relatively easy to further enhance. If you later wanted to add an additional "decorator" layer for logging, you could do:

Bind<IThingFactory> ().To<DefaultThingFactory> ().WhenInjectedInto<LoggingThingFactory> ();
Bind<IThingFactory> ().To<LoggingThingFactory> ().WhenInjectedInto<CachedThingFactory> ();
Bind<IThingFactory> ().To<CachedThingFactory> ();

Not the prettiest, but it works.

Esperanzaespial answered 19/7, 2011 at 20:18 Comment(1)
Didn't even know that a WhenInjectedInto() method existed. Thanks!Suffix
B
2

One of the benefits of DI framework is that you don't have to do things like these. Ninject has various scopes that you can use to specify the lifetime of your objects. It'll handle caching and stuff for you.

Read more here: http://kohari.org/2009/03/06/cache-and-collect-lifecycle-management-in-ninject-20/

Benildas answered 19/7, 2011 at 19:15 Comment(3)
Not exactly what I'm asking. I'm familiar with Ninject's object scopes, and I'm leveraging request scope and singleton scope heavily in my MVC-based app. Your suggestion solves my contrived example, but misses the point of the question. How do I configure a binding so that I can "wrap" an interface, like above?Suffix
Something like this: BindToFactory<IThing, CachedThingFactory>().InRequestScope(); Benildas
Forgot to mention, if you use BindToFactory(), you need to wire up your own provider - tweak your CachedThingFactory to implement IProvider (more in here github.com/ninject/ninject/wiki/…). You can also use .ToMethod() approach.Benildas
D
2

I suppose you are searching for named binding, documented here:

https://github.com/ninject/ninject/wiki/Contextual-Binding

Bind<IThingFactory>().To<CachedThingFactory>().Named("TheCachedThing");
Bind<IThingFactory>().To<DefaultThingFactory >().Named("ThePureThing");

and then

public CachedThingFactory([Named("ThePureThing")] IThingFactory wrapped)
{
    this._wrapped = wrapped;
    _cachedThings = new Dictionary<int,Thing>();
}

and for the consumer of the CachedThingFactory

public ThingFactoryConsumer([Named("TheCachedThing")] IThingFactory thingFactory)
{
   _thingFactory = thingFactory;
}
Disannul answered 19/7, 2011 at 19:37 Comment(3)
I was already familiar with named bindings; however, it feels dirty to decorate my classes with stringly-typed attributes.Suffix
Was not quite clear form your question :-). Worth reading: eeichinger.blogspot.com/2009/12/… from a "decorator" to "aop".Disannul
Interesting read, thanks! The IMethodInterceptor interface that he specs out in the next blog feels even dirtier, though =)Suffix

© 2022 - 2024 — McMap. All rights reserved.