Service Locator 1.0

Think of a service locator as a phone book. There are potentially hundreds of mechanics you could use, but there's only a select few that specialize in your specific type of car. A service locator is intended to find the best mechanic for you. In 2 months, there may be a new mechanic, but the service locator would realize this and give you that one, instead.

If you know anything about dependency injection (DI), that's exactly what it does: allow you to lookup a specific instance of an abstract interface. If you later need a different implementation, you simply register a different type with the container.

What Synergy gives you is one single location to access your DI container. The SharedServiceLocator class is a static class that has 2 methods of interest: Initialize() and Lookup<T>(). You can probably guess what each of these does.

IUnityContainer container = new UnityContainer();
container.RegisterType<IUserGateway, UserGateway>();

Yep, that's right. SharedServiceLocator.Initialize() assigns the desired container to the service locator.

IUserGateway gateway = SharedServiceLocator.Lookup<IUserGateway>();
UserCollection users = gateway.GetUsers();

When you break this example down, SharedServiceLocator.Lookup<IUserGateway>() is essentially the same as container.Resolve<IUserGateway>() in the Unity world and new UserGateway() in standard .NET. What's the difference? Let's start with comparing the DI approach to the standard (new object) approach. The key here is that your code doesn't need to know that there is a UserGateway class. Instead, your code references the abstract interface and only references the concrete implementation at the point in which you register the class with the container. This will virtually eliminate the dependencies within your application, making each layer much more testable. Flexibility is also a key benefit, but I'd argue that very few applications actually need the ability to replace these classes on the fly. Now that we're on the same page and know why we're using DI, let's touch on what Synergy gives you on top of Unity.

As I mentioned, SharedServiceLocator.Lookup<IUserGateway>() is the same as container.Resolve<IUserGateway>(). You most likely see the issue with the latter approach. If you add all of your dependencies to one container, you'll need to pass this container object down from the UI to business to data access layers. I don't know about you, but that just seems dirty to me. The service locator simply acts as a single store for your container.

What About Constructor Injection?

Admittedly, there are ways around passing a container around your application; but these don't work everywhere. Constructor injection, for one, is very valuable, if you prefer that approach; but the use of a service locator doesn't stop you from also using that approach. Feel free to go crazy with your DI implementation. Just think of the service locator as the one known place you can always go back to, if you need it.

When Do I Initialize()?

You have a few options, but they can all be summed up by saying: initialize the service locator before you need to lookup any types. (Duh!) Some prefer a static constructor in a custom service locator, others may prefer a service locator initializer. Whatever approach you choose, just be consistent. If helpful, we'll dig into some of these options. Just let us know.

Why Unity?

There are quite a few DI frameworks. As a matter of fact, we created our own before adopting Unity. The decision to use Unity was simply made because of the use of Enterprise Library. There have been thoughts about re-introducing the custom DI framework, which has better performance, but Unity provides a much broader feature set. In the future, we plan on looking into a provider model to support any DI framework. At this point, we'll consider adding a simpler, but more performant container.

Why "Shared" Service Locator?

We've only talked about the idea of a "service locator," but the class is a "shared" service locator. What's up with that!? One deficiency we've noticed with the use of the service locator as a simple container-holder is that you have to know about all dependencies at one time. If you have a plug-in framework, that's obviously not going to work. Heck, if you have a multi-tiered application that needs the flexibility to run both in and out of process, you're going to have the same problem of conflicting containers. The idea of a shared service locator is to support component-level containers, yet hide the complexity of that to the classes within those components that rely on component-level service locators.

Yes, this is somewhat vague and seems to be more complex than you may care for. We're hopeful this won't overcomplicate the current implementation, but would love your feedback as we work on it.

Last edited Oct 6, 2011 at 1:12 PM by flanakin, version 3


No comments yet.