Showing posts with label Dependency Injection. Show all posts
Showing posts with label Dependency Injection. Show all posts

Monday, March 23, 2009

Dependency Injection, is it for you?

People have been trying to convince you how great Dependency Injection is. Maybe they're calling it Inversion of Control. Either way, you're not buying it.

If you haven't been able to see the benefit, try reading Justin's blog post: To Inject or Not To Inject (warning: it is long, but worth it).

Justin focuses on complex configuration scenarios, but I like to recommend DI for its reusability. Self-configuring and "service locator" configuring tends to constrain you to using a particular type of configuration infrastructure, making it difficult to reuse your components, not to mention unit testing.

Friday, October 3, 2008

The Common Service Locator

I have argued against making 'hard' references to an underlying Dependency Injection (or IoC) container; rather, allow the container to do the work for you, namely, inject the dependencies. I realize that it can be difficult in practice, and apparently so have others.

A group of DI container authors have come together to define a common interface for using the underlying containers without creating a hard reference to a particular implementation. This is useful when you are writing code that needs to use a container to resolve a dependency (i.e. get an instance) but you don't know or care what container is being used.

So now we have the Common Service Locator library. Thanks to Ayende for blogging it. UPDATE: Also Glenn Block.

Each container needs an adapter to implement the interface and do the translation from the common method calls to the native container calls. This allows all container references to be declared as Microsoft.Practices.ServiceLocation.IServiceLocator.

Your code has two ways of getting the container:

  • The IServiceLocater reference can be supplied by the calling code and you just use it, or
  • You code can use a static property, ServiceLocator.Current.

Here is the interface that you will use (API Reference):

using System;
using System.Collections.Generic;

namespace Microsoft.Practices.ServiceLocation
{
// Summary:
// The generic Service Locator interface. This interface is used to retrieve
// services (instances identified by type and optional name) from a container.
public interface IServiceLocator : IServiceProvider
{
// Summary:
// Get all instances of the given TService currently registered in the container.
//
// Type parameters:
// TService:
// Type of object requested.
//
// Returns:
// A sequence of instances of the requested TService.
//
// Exceptions:
// Microsoft.Practices.ServiceLocation.ActivationException:
// if there is are errors resolving the service instance.
IEnumerable<TService> GetAllInstances<TService>();
//
// Summary:
// Get all instances of the given serviceType currently registered in the container.
//
// Parameters:
// serviceType:
// Type of object requested.
//
// Returns:
// A sequence of instances of the requested serviceType.
//
// Exceptions:
// Microsoft.Practices.ServiceLocation.ActivationException:
// if there is are errors resolving the service instance.
IEnumerable<object> GetAllInstances(Type serviceType);
//
// Summary:
// Get an instance of the given TService.
//
// Type parameters:
// TService:
// Type of object requested.
//
// Returns:
// The requested service instance.
//
// Exceptions:
// Microsoft.Practices.ServiceLocation.ActivationException:
// if there is are errors resolving the service instance.
TService GetInstance<TService>();
//
// Summary:
// Get an instance of the given named TService.
//
// Parameters:
// key:
// Name the object was registered with.
//
// Type parameters:
// TService:
// Type of object requested.
//
// Returns:
// The requested service instance.
//
// Exceptions:
// Microsoft.Practices.ServiceLocation.ActivationException:
// if there is are errors resolving the service instance.
TService GetInstance<TService>(string key);
//
// Summary:
// Get an instance of the given serviceType.
//
// Parameters:
// serviceType:
// Type of object requested.
//
// Returns:
// The requested service instance.
//
// Exceptions:
// Microsoft.Practices.ServiceLocation.ActivationException:
// if there is an error resolving the service instance.
object GetInstance(Type serviceType);
//
// Summary:
// Get an instance of the given named serviceType.
//
// Parameters:
// serviceType:
// Type of object requested.
//
// key:
// Name the object was registered with.
//
// Returns:
// The requested service instance.
//
// Exceptions:
// Microsoft.Practices.ServiceLocation.ActivationException:
// if there is an error resolving the service instance.
object GetInstance(Type serviceType, string key);
}
}



Here are the containers that have adapters at the moment:





I recommend IServiceLocator be used in all components that might need a reference to a container. Note that they are no common calls for configuring the container because this is a container-specific task that should be handled in the calling code.

Saturday, July 12, 2008

Ninject revisited

In a previous post, I looked at Ninject, and based on a cursory look at the documentation, dismissed it as requiring me to decorate my component classes with attributes in order to use multi-parameter constructor injection. Thanks to a comment by ninject's author, Nate Kohari, I found that with a slight configuration change, we can duplicate the behavior of Unity.

The proof's in the pudding. Let's see if we can wire up the same components with both Unity and Ninject without having to make any changes to our component classes.

Here are some interfaces:

public interface IDataService
{
}

public interface ISecurityService
{
}

public interface ILogger
{
}


And here are some components that use them. The Business Logic requires all three, the Security service needs a DataService and Logger, and the DataService needs a Logger.



public class BusinessLogic
{
protected IDataService DataService { set; get; }
protected ILogger Logger { set; get; }
protected ISecurityService SecurityService { set; get; }

public BusinessLogic()
{
}

public BusinessLogic(ILogger logger, IDataService dataService, ISecurityService securityService)
:this()
{
this.Logger = logger;
this.DataService = dataService;
this.SecurityService = securityService;
}


public override string ToString()
{
return base.ToString() + '\n' +
Logger.ToString() + '\n' +
DataService.ToString() + '\n' +
SecurityService.ToString() + '\n'
;
}
}


public class DataService : IDataService
{
protected ILogger Logger { set; get; }

public DataService()
{
}

public DataService(ILogger logger)
:this()
{
this.Logger = logger;
}
}

public class SecurityService : ISecurityService
{

protected IDataService DataService { set; get; }
protected ILogger Logger { set; get; }

public SecurityService()
{
}

public SecurityService(ILogger logger, IDataService dataService)
: this()
{
this.Logger = logger;
this.DataService = dataService;
}
}

public class Logger : ILogger
{
}


If we don't want to use an auto-wring container, we need to know all the dependecies and create the Business object like so:



static BusinessLogic NormalWay()
{
var logger = new Logger();
var dataService = new DataService(logger);
var securityService = new SecurityService(logger, dataService);
var bizLogic = new BusinessLogic(logger, dataService, securityService);
return bizLogic;
}



How do we configure and create a BusinessLogic object with Unity? Like so:



static BusinessLogic UnityWay(UnityContainer container)
{
UnityContainer container = new UnityContainer();
container
.RegisterType<ILogger, Logger>()
.RegisterType<IDataService, DataService>()
.RegisterType<ISecurityService, SecurityService>()
;
var bizLogic = container.Resolve<BusinessLogic>();
return bizLogic;
}
}


Likewise, we can create a method to create a BusinessLogic object using Ninject, all auto-wired are ready to go. This however, requires a configuration class (MyModule):



static BusinessLogic NinjectWay()
{
var kernel = new StandardKernel(new MyModule(), new AutoWiringModule());
var bizLogic = kernel.Get<BusinessLogic>();
return bizLogic;

}
private class MyModule : StandardModule
{
public override void Load()
{
Bind<ILogger>().To<Logger>();
Bind<IDataService>().To<DataService>();
Bind<ISecurityService>().To<SecurityService>();
}
}



We can run the following to show that the objects get create and populate the services:



static void Main(string[] args)
{
BusinessLogic bz;

bz = NormalWay();
System.Console.WriteLine(bz);

bz = UnityWay();
System.Console.WriteLine(bz);

bz = NinjectWay();
System.Console.WriteLine(bz);

}

So the end result is that we can design our components and use constructor-injection. So long as we keep container-dependencies out of our business components, an app can be assembled using the container of your choice.

Thursday, July 3, 2008

Looking at Ninject

Not to be too Microsoft-centric, I decided to look at the DI container Ninject, which has recently been released by Nate Kohari. After all, if we write our components correctly (POCOs), then swapping out containers should be a trivial exercise.

I looked at the docs on Ninject's constructor injection and found the following:

You also have the option to leave off the [Inject] attribute completely. This can help if you don't have access to the source code of a class, but you still want to inject dependencies into it. Here's the logic Ninject follows to choose which constructor to call, if none have an [Inject] attribute:

  1. If the type only has a single constructor, Ninject will call it.
  2. If the type has more than one constructor, but has a default (parameterless) constructor available, Ninject will call it. (This also applies to types that have no explicit constructors defined.)

I think that's a deal-breaker. Adding Attributes to my classes is not an option, because I want to maintain container-independency. In order to work correctly with constructor-injection, I need the container to call the container with as many parameters as it can satisfy. I will often have a parameterless constructor in my component to supply default values for the object for legacy code which is not using a container.

I'll take another look at Ninject if the constructor-injection strategy changes. The site has really sweet icons, by the way. Here's a sample:

Wednesday, June 18, 2008

Container Dependency anti-pattern

It is good design to minimize the number of dependencies between components in a system. Dependency Injection is used to decouple components from each other. A component should not specify, or require, a particular implementation of a Data Service, Logging Service or any other cross-cutting concern.

One solution to this problem is to use a DI container, such as Unity. This localizes all the dependencies in one place, the shell, or configuration point for your application. The components and services can be assembled and provided to the components as needed.

However, sometimes this leads to an anti-pattern in that components are built with a dependency on the container. This counteracts much of the advantage of using the container. It makes it difficult to share your components with another team that might be using a different container. Do not create container dependencies in your components!

The picocontainer example shows how to solve the problem with constructor injection, but assumes that the dependent container (B) only needs one instance of the A object. The question arises how can B create A objects on the fly as needed?

The solution is to inject a Factory into B via the constructor.

using System;
using Microsoft.Practices.Unity;

namespace DI
{
public class A
{
}

public class B
{
Factory<A> Afactory;

public B(Factory<A> f) { Afactory = f; }

public void SomeMethod()
{
A a = Afactory.Create();
Console.WriteLine(a.ToString());
}
}

public class Factory<T> where T : new()
{
public T Create() { return new T(); }
}
class Program
{
static void Main(string[] args)
{
UnityContainer unity = new UnityContainer();
B b = unity.Resolve<B>();
b.SomeMethod();
}
}
}


If you want to have the factory use a specific container, that could be an acceptable dependency, since it would be a trivial change to write a new Factory class for a different container. I will post this later.

See: http://www.codeplex.com/unity/Thread/View.aspx?ThreadId=29697 for a discussion about this.

UPDATE: See http://initializecomponent.blogspot.com/2008/10/common-service-locator.html