1

I've got this app that uses Ninject for DI and I've got the following construction:

public class SomeServicHost
{
    private ISomeService service;

    public SomeServicHost(ISomeService service)
    {
        this.service = service;
    }

    public void DoSomething(int id)
    {
        // Injected instance
        this.service.DoWhatever("Whatever");

        // Without injection - this would be how it would be instantiated
        var s = new SomeService(new Repo(id));
        s.DoWhatever("Whatever");
    }
}

interface ISomeService
{
    void DoWhatever(string id);
}

class SomeService : ISomeService
{
    private IRepo SomeRepo;

    public SomeService(IRepo someRepo)
    {
        this.SomeRepo = someRepo;
    }

    public void DoWhatever(string id)
    {
        SomeRepo.DoSomething();
    }
}

interface IRepo
{
    void DoSomething();
}

class Repo : IRepo
{
    private int queueId;

    public Repo(int queueId)
    {
        this.queueId = queueId;
    }

    public void DoSomething()
    {
        // Whatever happens
    }

So normal injection is not going to work. I'm going to need a factory. So I could do something like:

interface IServiceFactory
{
    ISomeService GetService(int id)
    {
        return new SomeService(new Repo(id));
    }
}

Which I acually have already. But if I do that, I lose all of the DI goodness, like lifecycle management, or swap out one implementation with another. Is there a more elegant way of doing this?

Jochen van Wylick
  • 5,303
  • 4
  • 42
  • 64
  • ..have you looked at the [Ninject.Extensions.Factories](https://github.com/ninject/ninject.extensions.factory) extensions? – Simon Whitehead May 09 '14 at 11:09
  • hi Simon, yes I did. According to the site 'This Ninject extension allows to create factory implementations automatically.' the factory is not the problem. – Jochen van Wylick May 09 '14 at 11:31
  • The factories that the extension creates participate completely in the Ninject lifecycle management/scoping/etc. If the factory isn't the problem then I am confused as to what your actual issue is? Can you re-phrase your question? – Simon Whitehead May 09 '14 at 11:32

2 Answers2

0

Instead of doing

new SomeService(new Repo(id));

you can do

IResolutionRoot.Get<Repo>(new ConstructorArgument("id", id));

IResolutionRoot is the type resolution interface of the kernel, and you can have it constructor injected. This will allow you to benefit from the "DI goodness", as you called it. Please note that you might also want to use the ninject.extensions.contextpreservation extension.

The ninject.extensions.factory, that @Simon Whitehead pointed out already, helps you eliminate boiler plate code and basically does exactly what i've described above.

By moving the IResolutionRoot.Get<> call to another class (.. factory...) you unburden SomeService from the instanciation. the ninject.extension.factory does exactly that. Since the extension does not need an actual implementation, you can even save yourself some unit tests! Furthermore, what about if SomeService is extended and requires some more dependencies, which the DI manages? Well no worries, since you are using the kernel to instanciate the class, it will work automatically, and the IoC will manage your dependencies as it should. If you wouldn't be using DI to do that instanciation, you might end up with using very little DI at the end.

As @zafeiris.m pointed out this is called Service Locator. And is also often called an anti pattern. Rightly so! Whenever you can achieve a better solution, you should. Here you probably can't. Suffice it to say it's better so stick with the best solution no matter what people call it.

There may be ways to cut down on the usage of service location. For example, if you have a business case, like "UpdateOrder(id)", and the method will create a service for that ID, then another service for that ID, then a logger for that ID,.. you may want to change it to creating an object which takes the ID as inheritable constructor argument and inject the ID-specific services and logger into that object, thus reducing the 3 factory/service locator calls to 1.

BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
  • Not a good idea. This way `SomeServiceHost` not only uses [service location](http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/), but also has the extra responsibility of new-ing up `SomeService`, along with its real responsibility which is hosting/using the service. – zafeiris.m May 10 '14 at 19:44
0

Keeping the factory as you describe it, seems fine to me. That's what they are for, when you have services that can only be instantiated at runtime depending on some runtime value (like id in your case), factory is the way to go.

But if I do that, I lose all of the DI goodness, like lifecycle management, or swap out one implementation with another.

DI is not an alternative for factories. You need both worlds which lead to a flexible, loosely coupled design. Factories are for new-ing up dependencies, they know how to create them, with what configuration and when to dispose them, and if you want to swap implementations, you still change only one place: only this time it's the factory, not your DI configuration.

As a last note, you might be tempted to use the service locator anti-pattern, as proposed in another answer. You'll just end up with worse design, less testable, less clear, possibly tightly coupled with you DI container and so on. Your idea for using a factory is far better. (here are more examples, similar to yours).

Community
  • 1
  • 1
zafeiris.m
  • 4,339
  • 5
  • 28
  • 41
  • So what if `SomeService` has other dependencies managed by the IoC - or if at a latter time it gets some more dependencies? Should he `new` all of them? What if the dependencies in turn have other dependencies? I don't think that `new`-ing is a better pattern than the *occasional* service locator (but that's debatable and also depends on how often you change your IoC...). Also ninject features it's factory extension which helps you creating factories with IoC support very simply. So there is just a minimum of dependency on the IoC. – BatteryBackupUnit May 11 '14 at 05:39
  • @BatteryBackupUnit the factory may use as well constructor injection to get all the dependencies that are known before runtime and new-ing up only the rest that need some runtime value. E.g the factory method could be like `return new SomeService(new Repo(id), _otherService, _otherRepo)` with those `other dependencies` being injected to the factory – zafeiris.m May 11 '14 at 08:56
  • good point, but things like ninject scope still won't work that way. Also, unit testing of that new instruction is going to be a real hassle. Either your tests are going to be less precise than they should or your design is going to be bad (bigger class interface than necessery for the actual consumer). Is it really worth it? One usually doesn't change IoC as often as one's undergarments. I'm still not convinced that one has to settle with this level of maintainability. – BatteryBackupUnit May 12 '14 at 05:28