Ayende @ Rahien

Unnatural acts on source code

Service locator for optional dependencies

I just found myself doing an optional dependency service location, which got on my nerve the second that I had to deal with it. I added this tiny tidbit to make it easier:

ILogger logger = IoC.TryResolve<ILogger>(new NullLogger());
logger.WarnFormat("Could not find a enities group for entity: {0}",
          typeof(TEntity).FullName);

Comments

Joshua McKinney
01/12/2008 08:25 AM by
Joshua McKinney

I'm not sure I understand. Wouldn't it have been better to just set put NullLogger in the IoC container in the first place? Doesn't this kinda defeat the purpose of IoC? Perhaps better would be to extend the container much like the auto mocking container to provide a NullService for non resolvable dependencies.

Ayende Rahien
01/12/2008 09:03 AM by
Ayende Rahien

Joshua,

Yes, it would.

The problem is that I am writing a library, and I don't want to force the users of the library to define certain things on the container.

Andrey Shchekin
01/12/2008 10:25 AM by
Andrey Shchekin

Can you explain the use cases of IoC.Resolve<>?

The thing I do not completely understand is which classes get their dependencies through dependency injection and which use Resolve?

Ayende Rahien
01/12/2008 11:07 AM by
Ayende Rahien

It is used for service location, mostly.

I don't like it, but it is really useful when you want to use IoC on things like static classes.

Francois Tanguay
01/12/2008 03:31 PM by
Francois Tanguay

If I understand correcly, you end up with two different mechanisms to get your dependencies depending whether they're optional or not.

That's why I prefer to fo with a service locator all the way.

Ayende Rahien
01/12/2008 03:42 PM by
Ayende Rahien

Francois,

Absolutely not.

If I want to use optional dependencies using DI (like I should), I just put a property.

This is for infrastructure concerns more than anything else

Francois Tanguay
01/13/2008 03:07 PM by
Francois Tanguay

Ok, I probably don't have the same definition of "optional" dependency.

Let's say logging is something I just do when exceptions are thrown. If exceptions never happen, I don't need an instance of ILogger.

With Ioc, dependencies are resolved at config time. WIth service locator, they are resolved at runtime, on demand.

Ioc:

try { ...}

catch (Exception ex) { MyInjectedLogger.Log(ex); }

ServiceLocator:

try { ...}

catch (Exception ex) { SerivceLocator.Resolve.Log(ex); }

Ayende Rahien
01/13/2008 03:41 PM by
Ayende Rahien

Francois,

I really don't like this approach. It is opaque, it hides the dependencies and it makes it easier to fail late rather than early.

Francois Tanguay
01/13/2008 05:30 PM by
Francois Tanguay

Given, it hides dependencies. They aren't enforced.

So are method calls on dynamic languages.

For dynamic languages, people argue that it is good test coverage that ensures success...

Couldn't it be the same thing for service locator?

There are some pros and cons to any approach.

Cons of Service Locator are definitely related to how explicit are the dependencies.

Pros are lazy creation of depencies (I find app initialization time to be generaly better)

Also, I've been using extensions methods on IServiceLocator to ease cross-cutting concern usage.

e.g.

interface IServiceLocator { T Resolve(); }

interface IServiceLocatorClient { IServiceLocator Locator { get; set; }}

public static class LoggerExtensions

{

public static void Log(this IServiceLocatorClient client, string msg)

{

client.Locator.Resolve<ILogger>().Log(msg);

}

}

With IoC:

public class Foo : IServiceLocatorClient

{

public IServiceLocator { get; set; } //THE only dependency that has to be injected.

void Do()

{

try { ... } 

catch (Exception ex) { this.Log(exception.Message); }

}

}

Comments have been closed on this topic.