views:

160

answers:

2

I just started using the Unity Application Block to try to decouple my classes and make it easier for unit testing. I ran into a problem though that I'm not sure how to get around. Looked through the documentation and did some Googling but I'm coming up dry. Here's the situation:

I have a facade-type class which is a chat bot. It is a singleton class which handles all sort of secondary classes and provides a central place to launch and configure the bot. I also have a class called AccessManager which, well, manages access to bot commands and resources. Boiled down to the essence, I have the classes set up like so.

public class Bot
{
    public string Owner { get; private set; }
    public string WorkingDirectory { get; private set; }

    private IAccessManager AccessManager;

    private Bot()
    {
       // do some setup

       // LoadConfig sets the Owner & WorkingDirectory variables
       LoadConfig();

       // init the access mmanager
       AccessManager = new MyAccessManager(this);
    }

    public static Bot Instance()
    {
       // singleton code
    }

    ...
}

And the AccessManager class:

public class MyAccessManager : IAccessManager
{
    private Bot botReference;

    public MyAccesManager(Bot botReference)
    {
        this.botReference = botReference;
        SetOwnerAccess(botReference.Owner);
    }

    private void LoadConfig()
    {
        string configPath = Path.Combine(
            botReference.WorkingDirectory, 
            "access.config");
        // do stuff to read from config file
    }

    ...
}

I would like to change this design to use the Unity Application Block. I'd like to use Unity to generate the Bot singleton and to load the AccessManager interface in some sort of bootstrapping method that runs before anything else does.

public static void BootStrapSystem()
{
   IUnityContainer container = new UnityContainer();

   // create new bot instance
   Bot newBot = Bot.Instance();

   // register bot instance 
   container.RegisterInstance<Bot>(newBot);

   // register access manager
   container.RegisterType<IAccessManager,MyAccessManager>(newBot);
}

And when I want to get a reference to the Access Manager inside the Bot constructor I can just do:

IAcessManager accessManager = container.Resolve<IAccessManager>();

And elsewhere in the system to get a reference to the Bot singleton:

// do this
Bot botInstance = container.Resolve<Bot>();
// instead of this
Bot botInstance = Bot.Instance();

The problem is the method BootStrapSystem() is going to blow up. When I create a bot instance it's going to try to resolve IAccessManager but won't be able to because I haven't registered the types yet (that's the next line). But I can't move the registration in front of the Bot creation because as part of the registration I need to pass the Bot as a parameter! Circular dependencies!! Gah!!!

This indicates to me I have a flaw in the way I have this structured. But how do I fix it? Help!!

+1  A: 

First of all you should let the container manage your singleton lifetime instead of writing the singleton code yourself. To remove your circular dependency, you can remove the Bot from the access manager constructor. Instead, you use an initialize method.

container.RegisterType<Bot>(new ContainerControlledLifecycleManager()); // from my memory...
container.RegisterType<IAccessManager,MyAccessManager>();

var bot = container.Resolve<Bot>();

// Bot.cs
public Bot(IAccessManager manager)
{
   manager.InitializeFor(this);
}

For testability reasons you should never call your IOC container from a constructor.

Ryan
Thanks that makes a lot of sense. Just needed someone with more experience to say...hey, just do it this way! I understand now that the object that is getting its dependencies injected shouldn't control the injection (shouldn't have an IOC call from a constructor). That should be handled elsewhere. Made the change and it's working great.
Onisemus
A: 

You can make your life easier by changing the design in the following ways:

  • Don't implement the Singleton pattern yourself. The DI Container should manage the lifetime of all components, including the Bot class. If you only want a single instance in your application, configure Unity to always return the same instance.
  • Do everything in your power to remove circular dependencies. You can often do that by changing one of the communication directions to use events instead of direct calls. Another option is to introduce a Mediator.

Notice that none of these recommendations particularly involve Unity. Unity (or any other DI Container) is not a silver bullet that will magically make your code loosely coupled. You must first understand the principles behind DI, and then you can use any DI Container as a tool to help you wire up the dependency graph.

Mark Seemann