§ February 20, 2009 09:18 by
beefarino |
I've worked with a lot of code that uses IServiceProvider as a way to disconnect an object from its dependencies. I've come to loathe this interface for many reasons and have opted for a systematic pattern of dependency injection.
First reason IServiceProvider sucks: it hides the dependencies of an object while decoupling from them. What do I mean by that? Pretend you're using this blackbox component from your code:
public class BlackBoxComponent
{
public BlackBoxComponent( IServiceProvider services );
public void DoAllTheWork();
}
Can you tell what services are going to be requested from the service provider? Me neither. Now you need another way to discover what dependencies need to be available to the BlackBoxComponent - documentation, source code, something out-of-band that takes you away from your work at hand.
Compare that with some simple constructor injection:
public class BlackBoxComponent
{
public BlackBoxComponent( IRepository< Thing > thingRepository, ILogManager logManager );
public void DoAllTheWork();
}
With this, you know exactly what a BlackBoxComponent needs to do its job just from looking at the constructor.
Second reason IServiceProvider sucks: it adds a lot of code. Fetching the services is cumbersome at best:
// ...
public BlackBoxComponent( IServiceProvider services )
{
thingRepository = ( IRepository< Thing > ) services.GetService( typeof( IRepository< Thing > ) );
logManager = ( ILogManager ) services.GetService( typeof( ILogManager ) );
}
// ...
Sure you can use some syntactic sugar to work around the typeof'ing and naked casting:
public static class ServiceProviderExtension
{
public static T GetService< T >( this IServiceProvider serviceProvider )
{
return ( T ) serviceProvider.GetService( typeof( T ) );
}
}
which cleans up the code a bit:
// ...
public BlackBoxComponent( IServiceProvider services )
{
thingRepository = services.GetService< IRepository< Thing > >();
logManager = services.GetService< ILogManager >();
}
// ...
but you're still stuck having to reach out and grab every dependency you need from the service container - which implies that somewhere, some other piece of code is responsible for filling up that service container:
//...
ServiceContainer services = new ServiceContainer();
services.AddService(
typeof( ILogManager ),
new Log4NetLogManager()
);
services.AddService(
typeof( IRepository< Thing > ),
new ThingRepository()
);
//...
More code to write, all of it unnecessary and obsolete given the state of the art in dependency injection frameworks.