0

I have a controller that looks something like this:

public class MyController : MyController
{
    private readonly IMyService service;

    public MyController(IMyService _service)
    {
        service = _service;
    }

    [HttpGet]
    public BaseFoo GetFoo(Guid id)
    {
        return service.GetFoo(id);
    }
}

Ninject will inject IMyService for me. Now in my implementation of IMyService, I would like to be able to create objects derived from BaseFoo. So I originally had something like this:

public MyService : IMyService
{
    public BaseFoo GetFoo(Guid id)
    {
        Type t = // some code that determines which type we actually need
        return (BaseFoo) Activator.CreateInstance(t);
    }
}

Which worked fine. But now what I'd like to do is have different implementations of BaseFoo be dependent on different services (or no services). So for example, I might have:

public SomeFoo : BaseFoo
{
    public SomeFoo(IOtherService _service)
    {

    }
}

And, ideally, I'd like to have Ninject handle that dependency injection, but I'm not sure what the right way to handle this is. MyService needs to handle creating BaseFoo objects, so I thought add a constructor like this:

public MyService(IKernel kernel)

Which gets the kernel injected and then in GetFoo instead of using Activator I can:

return (BaseFoo) kernel.Get(t);

This works. But doesn't feel right. It requires MyService to know about Ninject and that seems wrong.

Is there a better pattern to use here?

Edit: BatteryBackupUnit suggests using the ninject factory extension, which sounds like that's probably a better solution, but I don't quite see how to make it work in this case. If I create a IFooFactory, I would imagine it would need to look like this:

public interface IFooFactory
{
    BaseFoo CreateFoo(Type t);
}

But this will try to create an instance of BaseFoo passing in a type to the constructor, which isn't what I need. BaseFoo is abstract, I need a factory that will create a concrete instance of type t that is derived from BaseFoo.

Matt Burland
  • 44,552
  • 18
  • 99
  • 171

2 Answers2

1

A first improvement would be to use IResolutionRoot instead of IKernel. IResolutionRoot is the "service locator" part of IKernel. The IKernel derives from IResolutionRoot and IBindingRoot. Passing IKernel would allow the user to create new bindings, which you don't want.

A second improvement on your suggest design is to use the ninject factory extension instead of IKernel / IResolutionRoot. It automagically creates factories from an interface, for example if you have:

public class BaseFoo
{
    public BaseFoo(IServiceDependency service, string parameter) { }
}

you can have a factory:

public interface ISpecialFooFactory
{
    BaseFoo Create(string parameter);
}

where as the factories method's argument names need to match constructor parameter names. All ctor-parameters which are not passed to the factory are injected/resolved automatically.

Now this still shares some disadvantages with the ServiceLocator (anti-) pattern. See Mark Seeman's blog post on the matter (I recommend to read the comments, too).

Ideally you could adapt your design so that the whole object graph is instantiated in one go and that "run time parameters" are passed as method arguments instead of constructor arguments.

If that's not possible, i'd still try to keep object creation to a minimum and do it using ninject's factory extension.

BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
  • What's the advantage of `IResolutionRoot` versus `IKernel`? As for the other suggestions, I'm trying to figure out how I can make that fit in with what I have. – Matt Burland Aug 27 '14 at 13:38
  • 1
    Oh i accidentally removed the explanation regarding `IResolutionRoot` from the answer. I just re-added it. Basically it's good practise to use a more specific interface over a more general one. with `IKernel` you could also create bindings. `IResolutionRoot` is just ninject's "service locator" interface. – BatteryBackupUnit Aug 27 '14 at 14:02
1

Answering my own question based on the very helpful feedback from BatteryBackupUnit, this is what I ended up doing. I used the ninject factory extension as suggested and created an IFooFactory that looks like this:

public interface IFooFactory
{
    T CreateFoo<T>();
}

And is bound like so:

kernel.Bind<IFooFactory>().ToFactory();

Now I can make my service accept an IFooFactory:

public MyService(IFooFactory _factory)
{
    factory = _factory;
}

The actual implementation of GetFoo is a little tricky because I need to supply a generic type. Luckily Jon Skeet has the answer:

public BaseFoo GetFoo(Guid id)
{
    Type t = // some code that determines which type we actually need
    MethodInfo mi = factory.GetType().GetMethod("CreateFoo");
    MethodInfo generic = mi.MakeGenericMethod(type);
    return (BaseFoo)generic.Invoke(factory, null);
}

And surprisingly this all automagically works!

Community
  • 1
  • 1
Matt Burland
  • 44,552
  • 18
  • 99
  • 171