A couple of weeks ago I blogged about writing testable code using dependency injection. This was done by injecting interface references into a class’s constructor:
public UserManager(IUserPersistence userPersistence, IEmailManager emailManager)
{
_userPersistence = userPersistence;
_emailManager = emailManager;
}
Assuming we have concrete classes called UserPersistence and EmailManager that implement these interfaces, we could simply inject the dependencies like this:
public void DoIt()
{
UserManager userManager = new UserManager(
new UserPersistence(), new EmailManager());
}
But what happens if the dependencies cannot actually be injected during construction because it’s too early to create them? A good example of this is when the concrete classes themselves are constructed with a parameter that is not yet available.
Let’s say that when we register a user we get a guid back from our persistence layer that identifies the user. Now let’s also say that our IEmailManager interface is written to apply to a single user at a time, and there’s a concrete class called EmailManager with a constructor that looks like this:
public class EmailManager : IEmailManager
{
public EmailManager(Guid userId)
{
_userId = userId;
}
...
}
Since we don’t know the guid until we register the user (via the UserManager class), we can’t inject an IEmailManager into the UserManager class’s constructor.
public void DoIt()
{
UserManager userManager = new UserManager(
new UserPersistence(), new EmailManager(<oops! What’s the guid?>));
}
The solution is to change UserManager’s constructor to take a delegate instead of an interface:
public class UserManager
{
public UserManager(IUserPersistence userPersistence,
Func<Guid, IEmailManager> emailManagerFactory)
{
_userPersistence = userPersistence;
_emailManagerFactory = emailManagerFactory;
}
private IUserPersistence _userPersistence;
private Func<Guid, IEmailManager> _emailManagerFactory;
Instead of the UserManager constructor directly taking an IEmailManager, it now takes a delegate that expects a guid and returns an IEmailManager.
Since the delegate is only supplied during UserManager’s construction and not executed, we don’t need to know the guid at this time:
public void DoIt()
{
UserManager userManager = new UserManager(
new UserPersistence(), o => new EmailManager(o));
}
Our delegate can be passed in several ways, but a lambda expression is a nice easy way to do it (“o” is a local variable of type Guid).
With this new approach, our RegisterUser method from our previous post now looks like this:
public Boolean RegisterUser(String lastName, String firstName, String emailAddress)
{
try
{
Boolean result = false;
Guid userId = _userPersistence.AddUser(lastName, firstName, emailAddress);
IEmailManager emailManager = _emailManagerFactory(userId);
result = emailManager.SendLoginInfo();
return result;
}
catch (Exception ex)
{
// Log the exception...
return false;
}
}
Note that when we execute our delegate (i.e. the _emailManagerFactory field) we pass in the user ID guid as a parameter (i.e. the value we just obtained from calling the AddUser method) and get back our IEmailManager interface reference. Now we can call its methods and they will all act on the newly registered user.
Delegates are a great way to defer execution until needed, and this is a very useful technique to let you follow the dependency injection pattern even when you don’t have everything you need during object construction.
Hope this helps.