Windsor:如何在涉及运行时依赖项时配置容器

时间:2012-01-22 17:58:40

标签: c# dependency-injection inversion-of-control castle-windsor ioc-container

我有这个(虚构的)桌面应用程序,它允许项目经理搜索空闲的开发人员并分配任务。该模型是:

interface IDeveloperFactory
{
   IDeveloper CreateDeveloper(string name);
}

interface IDeveloper 
{
    void WriteCode();
    void WriteUnitTest();
}

interface ITask
{
    void Complete();
}

class Developer : IDeveloper
{
   public Developer(string name) { ... }

   public void WriteCode()
   {
       Console.WriteLine("Developer {0} is writing code... done", name);
   }
   public void WriteUnitTest()
   {
       Console.WriteLine("Developer {0} is writing unit test... done", name);
   }
}

class WriteCodeTask : ITask
{
   public Task(Lazy<IDeveloper> lazyDeveloper) { ... }

   public void Complete()
   {
       var dev = lazyDeveloper.Value;
       dev.WriteCode();
   }
}

Developer具有运行时依赖性,因此我将使用DeveloperFooFactory(配置为类型化工厂)来获取IDeveloper。虽然正在使用IDeveloper,但我还需要ITask使用开发人员实例,如果我可以让容器解决它们,我的工作会更容易:

var developerFactory = container.Resolve<IDeveloperFactory>();
var dev1 = developerFactory.CreateDeveloper("dev 1");
var task1 = container.Resolve<ITask>();
task1.Complete(); // uses dev1
var task2 = container.Resolve<ITask>();
task2.Complete(); // uses dev1
container.Release(dev1); // also releases task1 and task2

var dev2 = developerFactory.CreateDeveloper("dev 2");
var task3 = container.Resolve<ITask>();
task3.Complete(); // uses dev2
// task1, task2 and task3 are always different instances

问题:

  1. 如何配置容器来实现此目的? (我打算使用3.0版本)
  2. 理想情况下,我不会在创建dev1之前释放dev2(例如,在不同的线程上同时创建dev)但我仍然需要容器来解析{{1}正确...我是否需要使用子容器,或者3.0中的新ITask方法?
  3. 开发人员也可以在不同的线程上同时处理多个任务。因此,每当我需要一个任务时,我只要求容器解决它并使用“上下文开发者”实例......但是如何?
  4. 我知道如果不是循环依赖,事情可能会更容易。然而,似乎这个模型,我的意思是真实的,这种方式更自然......

1 个答案:

答案 0 :(得分:0)

最终想出来......看起来并不复杂,但我对温莎来说真的很新。

集装箱登记:

container.AddFacility<TypedFactoryFacility>();
container.Register(
    Component.For<IDeveloperFactory>().AsFactory(),
    Component.For<IDeveloper>().ImplementedBy<Developer>().LifestyleScoped(),
    Component.For<ITask>().ImplementedBy<WriteCodeTask>().LifestyleTransient(),
    Component.For<Lazy<IDeveloper>>().LifestyleTransient() // i don't use 4.0 so this is my own implementation of Lazy<> which depends on a Func<> on ctor
);

测试代码:

var developerFactory = container.Resolve<IDeveloperFactory>();
using (container.BeginScope())
{
    var developer = developerFactory.Create("John"); // this creates a new Developer and caches it in the current scope
    var task1 = container.Resolve<ITask>();
    var task2 = container.Resolve<ITask>();
    Assert.Same(task1.Developer, task2.Developer);
    Assert.Equal("John", task1.Developer.Name);

    task1.Complete();
    task2.Complete();
    container.Release(task1);
    container.Release(task2);
}

这证明可以使用相同的上下文开发人员实例解决这两个任务。

我的另一个要求就是在不同的线程上工作。这也没关系,因为如果using (container.BeginScope()) { ... }代码在一个线程上运行,那么容器会为每个线程创建并缓存一个不同的开发人员实例。此外,我可以启动子线程来解决任务,并仍然可以获得正确的上下文开发人员实例。