3

First, here's how I understand these two concepts: Service Locators are not Dependency Injection. Both Service Locators and Dependency Injection are applications of Inversion of Control. This is the understanding I reached after reading Martin Fowler's write-up on the topic. If my understanding is incorrect, please explain how.

In many places online, I have seen what are clearly Service Locators referred to as Dependency Injection libraries or frameworks. For example, Simple Injector calls itself "an open-source dependency injection (DI) library for .NET." However, in its quick start page, an example is shown which clearly demonstrates that Simple Injector is a Service Locator. Microsoft provides the .NET Core namespace "Microsoft.Extensions.DependencyInjection," which is clearly a Service Locator as well. An Essential .NET blog post from 2016 titled "Dependency Injection with .NET Core" explains in detail how to use the ".NET Core DI framework" to register services!

There are true (as I understand them) dependency injection containers around, of course, such as Ninject, which actually performs automated dependency injection.

Am I misunderstanding the distinction between these concepts? If not, how did they come to be so thoroughly mixed up?

3

You've got to get the knowledge of your services into your framework, whether that be a "dependency injection" framework or a "service locator" framework, and you do that some variant of

framework.register(abstractType, concreteType)

no matter what framework you're using. The difference is in how you get things out of the framework again:

  • With a service locator, every time you want an instance of a service, you call framework.get(abstractType).
  • With dependency injection, you pass the dependencies down from one layer to the next, and let your framework do the job of wiring up those dependencies. (You may need a few calls to framework.get at the top level, but you shouldn't have one every time you want a service).

Certainly the latter is how Microsoft.Services.DependencyInjection is intended to be used, and it's not really any different from how NInject is used.

| improve this answer | |
  • I'm not sure you're correct about Microsoft.Services.DependencyInjection being the latter. In the blog post that I linked, you can see that Application requests its dependency in the line Logger = Services.GetRequiredService<ILoggerFactory>.()CreateLogger<Application>();. This looks to me like a clear "service locator" paradigm. I do appreciate the point you and Christophe made (I believe you're saying the same thing) about both service locators and dependency injectors requiring a "service composer" component. – Snail Cadet Aug 23 at 15:40
  • 1
    You can absolutely use M.S.DI as a service locator if you want to, but it can certainly do "proper" DI as well. – Philip Kendall Aug 23 at 15:44
  • Do you have any examples of M.S.DI being used as a proper DI container? Would be very helpful – Snail Cadet Aug 23 at 15:45
  • 2
    @SnailCadet Straight from MS docs, here's the first thing I found by searching for .NET Core dependency injection example. As you can see, there's even an example with the Logger itself – gcali Aug 24 at 12:49
  • 1
    @SnailCadet What do you mean by "proper"? I mean, in a .NET Core web app the dependency injection framework is used to create a controller when a request is received; this is similar to what happens in other languages and frameworks, as far as I know. In other cases the usual flow is to use the Service Locator pattern for the root object; but it's not a weakness of .NET Core only, at some point all frameworks need to create a root object, in a web app that point is simply hidden by the framework with the construction of the controller. Care to give an example of how to avoid it in other cases? – gcali Aug 25 at 7:35
3

A first difference is: Not every service locator is a dependency injection container; a service locator can be used for another purpose than dependency injection. This can even create some issues such as hidden dependencies (see Fowler’s explanation on SL vs DI), to the point that some consider SL to be an anti-pattern (but again imho it depends on how it is used in the end).

A second and even more important difference is that dependency injection containers are more than just service locators: they are also service composers.

For example, in the SimpleInjector example to which you referred, you can see that the container is also informed about how to instantiate the service (singleton vs factory), and that it itself composes the service CancelOrderService, automatically resolving and injecting its known dependency with the help of the dependency map configured in the container. And this last feature is the real benefit of a DI framework that saves you from writing you a lot of boring and tedious boilerplate code every time you want to instantiate an object that relies on dependencies!

| improve this answer | |
  • I appreciate the distinction you're making with "service composers." It's an (I think) nuanced bit that I missed in my research. You said that "Not every service locator is a dependency injection container." Do you have any examples of service locators that are also DI containers? Also, good point you make about CancelOrderService - I had misunderstood the example. On further review (correct me if I'm wrong), it looks like CancelOrderService is being constructed by the DI container when it is "service located" in main(). – Snail Cadet Aug 23 at 15:54
  • 1
    @SnailCadet Examples of locators that do not DI? There are for example a lot of ad-hoc locators that are used just to replace some global service objects and therefore only facilitate configuration with the risk of hidden couplings. In the same category you can find the service locators that act as proxy for remote web-services. Maybe we should add also to that categories the locators that can’t automatically compose services based on the dependency map? And yes, CancelOrderService is constructed by the DI container, avoiding you to manually inject the dependencies. – Christophe Aug 23 at 16:16
  • Agreed on "locators that can’t automatically compose services based on the dependency map" being a possibly separate category. Perhaps this is a biased view based on my background with "Pure" DI, but I think there is an important practical distinction between Service Locators that don't automatically compose services, and ones that do, the distinction being whether or not object dependencies are exposed via constructors or not. Hidden object dependencies (requiring review of documentation) are bad form compared to obvious dependencies in the constructor signature. – Snail Cadet Aug 24 at 18:18
1

Service location vs dependency injection

Service Locators are not Dependency Injection.

I disagree, but I think you intend to say/focus on something different than what I'm disagreeing with.

First of all, Philip Kendall's answer is correct that abstract-to-concrete type registration is the same in either case, the only real difference is how to access those registrations.

Internally, a DI framework uses a service locator to find the injectable instance/type. In essence, a service locator is the dictionary that contains the abstract-to-concrete type registrations that have been registered. Logically, any DI framework is required to keep track of those registrations so that it can then retrieve them when necessary.

Why are Service Locator frameworks often called Dependency Injection Containers?

What you call Dependency Injection is really just Service Location, but with the added feature of automatically locating services based on a constructor signature1 and acting as a factory for all of its registered types, instead of forcing the developer (= user of your framework) to directly request the services from your framework's service locator and being required to manually construct the dependency graph.

Note: whether the developer locates services inside the constructor body, or calls the constructor manually after having located the services, is an irrelevant distinction here. In either case, they've had to manually construct the dependency graph by supplying the services to classes that depend on them.

So the short answer to your question is "because DI frameworks are essentially service locators with a few extra features that abstract the location of registered services".


Caveat

1 Due to an objection raised in the comments, I agree that DI can be done without a service locator, but I have yet to come across a single DI framework that does so. I suspect doing DI as a framework without SL is going to put so much effort on the developer that the framework doesn't really add meaningful value anymore.

I'd be interested in seeing an example of DI without SL in a DI framework.

Since this question focuses on DI frameworks and what we call them; I stand by my claim within the context that the question defines.

| improve this answer | |
  • 2
    @SnailCadet: "This feature is the distinction between DI and SL as I understand it." My point is that they are not distinctly different (i.e. completely different and unrelated things), but rather than one (DI) is a derivation of the other (SL). The constructor signatur argument is an implementation detail. It is not the driving factor leading you to decide between DI or SL , it's the other way around: deciding between DI/SL leads to needing a specific kind of constructor implementation. Architecture comes before implementation, otherwise your priorities get muddied. – Flater Aug 24 at 13:26
  • 2
    One can use dependency injection without a framework, and also without a service locator. When building a DI framework to automate the dependency resolution, you may use a service locator to do so (and that's most common), but you don't have to. But here's the biggest difference to me: If you were to describe a system as having been built with a service locator, I would assume you mean no DI. If you describe a system as having been built with a DI framework, I wouldn't care whether the resolution mechanism is a service locator or something else. They are two different architectural styles. – Eric King Aug 24 at 17:54
  • 1
    @EricKing: If I tell you I eat 2 eggs a day, do you expect me to eat 2 eggs exactly the way they are, shell and all? Or do you think it's reasonable that I still prepare those eggs before eating them? Similarly you would expect me to eat exactly 2 eggs a day, but logically I could be eating 3 a day and my statement would still be correct. To translate this into programming, if class B : A and myVar is A == true, you cannot just conclude that myVar is B must then be false. There's a huge gap between humans inferring things when communicating with one another and what makes logical sense – Flater Aug 24 at 22:55
  • 1
    @EricKing: I agree that when you tell me you use SL, I'd infer you use SL and nothing more, but that reasoning simply does not hold up in logical terms. That's a human convention, not a logical conclusion. So I have no issue with statements like "I use SL instead of DI", but I still consider most (if not all, at least the ones I've seen) DI frameworks to internally use some sort of service location. The external contract to the consumer might not reveal it (though .Net Core certainly exposes an IServiceProvider when you want it to), but it's in there. – Flater Aug 24 at 22:59
  • 3
    @Flater I just mean that "Service Locator" is a loaded term... It has more than one meaning. It can describe an architectural pattern, and it can describe a type of class construct. Sure, many (most?) DI frameworks might incidentally use a SL class as the resolver, but that's very different from saying that a codebase uses a "service locator pattern" (where the consumer pulls in its dependencies), which is practically the opposite of Dependency Injection (where the consumer gets the dependencies pushed into it). So in at least that respect, DI and SL are quite distinct. – Eric King Aug 24 at 23:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.