LP on .NET

August 29, 2011

Dependency Injection and Delegates

Filed under: .NET,C#,Software Development — Larry Parker @ 10:46 pm
Tags: , ,

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.

Advertisements

1 Comment »

  1. Great timing – I just needed to implement this very trick the other day, as a way to allow Dependency Injection in a case where not all required information is available at the time of the injection. It worked beautifully.

    Comment by Wayne S. — September 12, 2011 @ 11:32 am | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: