使用Unity Framework的依赖类的构造函数参数

时间:2010-03-20 20:40:14

标签: c# dependency-injection unity-container ioc-container

我刚开始使用Unity应用程序块来解耦我的类并使单元测试更容易。但是,我遇到了循环依赖的问题。

我有一个Facade类型的聊天机器人。它是一个单例类,它处理所有类型的辅助类,并提供启动和配置机器人的中心位置。我还有一个名为AccessManager的类,它可以管理对bot命令和资源的访问。归结为本质,我设置了这样的类:

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
    }

    ...
}

和AccessManager类:

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
    }

    ...
}

我想将此设计更改为使用Unity应用程序块。我想使用Unity来生成Bot单例并在某种类型的引导方法中加载AccessManager接口,该方法在其他任何操作之前运行。

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);
}

当我想在Bot构造函数中获取对Access Manager的引用时,我可以这样做:

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

在系统的其他地方获取对Bot单例的引用:

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

问题是方法BootStrapSystem()会爆炸。当我创建一个bot实例时,它将尝试解析IAccessManager但是无法解决,因为我还没有注册类型(这是下一行)。但是我无法在Bot创建之前移动注册,因为作为注册的一部分,我需要将Bot作为参数传递!循环依赖!!尔加!!!

这告诉我,我有这种结构的方式存在缺陷。但是我该如何解决呢?帮助!

2 个答案:

答案 0 :(得分:1)

首先,您应该让容器管理您的单身生命周期,而不是自己编写单例代码。要删除循环依赖项,可以从访问管理器构造函数中删除Bot。相反,您使用初始化方法。

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);
}

出于可测试性原因,您不应该从构造函数中调用您的IOC容器。

答案 1 :(得分:1)

您可以通过以下方式更改设计,让您的生活更轻松:

  • 不要自己实现Singleton模式。 DI容器应管理所有组件的生命周期,包括Bot类。如果您只想在应用程序中使用单个实例,请将Unity配置为始终返回相同的实例。
  • 尽力掌握删除循环依赖。您通常可以通过更改其中一个通信方向来使用事件而不是直接呼叫。另一种选择是引入Mediator

请注意,这些建议都不涉及Unity。 Unity(或任何其他DI容器)不是银弹,它会神奇地使您的代码松散耦合。您必须首先了解principles behind DI,然后您可以使用任何DI容器作为工具来帮助您连接依赖图。