【问题标题】:How to get list of currently instantiated instances of some dependency in Castle Windsor?如何获取 Castle Windsor 中某些依赖项的当前实例化实例的列表?
【发布时间】:2015-03-24 18:01:33
【问题描述】:

假设我有一个组件Bar 执行Foo 并通知所有实现IFooConsumer 接口的服务上调用FooHappened 方法。

我可以这样写Bar

class Bar
{
    public Bar(IEnumerable<IFooConsumer> fooConsumers) { ... }

    public void Foo()
    {
        // foo-ing
        foreach (var f in _fooConsumers) f.FooHappened();
    }
}

它会起作用,但实例化Bar 将实例化所有可能的IFooConsumers。如果我只需要通知Foo 发生时存在的那些IFooConsumer 怎么办?

有没有办法让某种跟踪器知道IFooConsumer 的所有实例化实例?

我可能可以通过订阅IWindsorContainer.Kernel.ComponentCreated 自己写一个,但是如果存在类似的东西我很感兴趣?或者也许还有其他方法可以解决我的问题?

【问题讨论】:

  • 我会说你应该避免让对象事件的订阅者成为对象的依赖项。如果您试图通知事件的侦听器,您应该有一个单独的订阅者机制。例如,要将侦听器实例与FooHappened 事件相关联,请在Bar 上声明FooHappenedHandler 方法委托并将其设置为侦听器上的方法。然后在Foo() 中调用FooHappened(this),这将允许执行由监听器定义的代码。
  • @moarboilerplate 给定温莎城堡的机制实现实际上是一个问题。具有事件或类似事件的机制是可能的,但需要显式订阅/取消订阅以避免内存泄漏。 Container 已经在内部监视对象的生命周期,所以我认为它可能在这里有一些现成的东西。
  • 你可以看看 Caliburn.Micro EventAggregator。这可以很好地与 Castle.Windsor 集成。你可以扮演类似的角色。查看此链接 (github.com/marwijn/Caliburn.Micro.Windsor/blob/master/content/…) 以了解与 Windsor 的集成。
  • @Marwijn,您应该提出您的评论作为解决方案,我不知道使用设施会这么简单(至少从我所看到的来看是这样)
  • @samy 我已经把它作为一个解决方案发布了,设施真的很强大,但是一旦你找到了正确的钩子就很简单。

标签: c# castle-windsor ioc-container


【解决方案1】:

您可以创建一个如下所示的简单工具,该工具将在每次实例化组件时进行事件注册。下面的代码用于将 Winsor 与 Caliburn.Micro 一起使用。这也将确保事件被取消注册,否则会导致奇怪的行为。在您的情况下,我不会让 Bar 直接将事件触发到所有类,而是使用单例组件(如下面的 IEventAggregator)将事件触发到多个类。这也将确保事件被取消注册,否则会导致奇怪的行为。在代码中,从 IHandle 派生的每个类都将接收事件。您可以根据需要更改此设置。

如果您有任何问题,请告诉我。

class EventRegistrationFacility : AbstractFacility
{
    private IEventAggregator _eventAggregator;
    protected override void Init()
    {
        Kernel.ComponentCreated += ComponentCreated;
        Kernel.ComponentDestroyed += ComponentDestroyed;
    }

    void ComponentCreated(Castle.Core.ComponentModel model, object instance)
    {
        if (!(instance is IHandle)) return;
        if (_eventAggregator == null) _eventAggregator = Kernel.Resolve<IEventAggregator>();
        _eventAggregator.Subscribe(instance);
    }

    void ComponentDestroyed(Castle.Core.ComponentModel model, object instance)
    {
        if (!(instance is IHandle)) return;
        if (_eventAggregator == null) return;
        _eventAggregator.Unsubscribe(instance);
    }

}

===编辑====

将它与 Sammy 描述的保镖结合起来:

public interface IBouncer {
    IEnumerable<IFooConsumer> WhoIsInside {get;}
    void WelcomeTo(IFooConsumer consumer);
    void EscortOut(IFooConsumer consumer);
}

public class Bouncer {
    private IList<IFooConsumer> _inside {get;}
    void WelcomeTo(IFooConsumer consumer) {
        _inside.Add(consumer);
}
    void EscortOut(IFooConsumer consumer);
        _inside.Remove(consumer);
    }
    IEnumerable<IFooConsumer> WhoIsInside {
        get {
            return _inside;
        }
    }



public Consumer: IFooConsumer {
    FooHappened() {
        // Do something.
}
    // no need to implement constructor/dispose
}


class Bar
{
    public Bar(IBouncer bouncer) { ... }

    public void Foo()
    {
        // foo-ing ==> alernatively create a function on Bouncer that does this. And keep WhoIsInside private.
        foreach (var f in bouncer.WhoIsInside) f.FooHappened();
    }
}


class BouncerRegistrationFacility : AbstractFacility
{
    private IBouncer _bouncer
    protected override void Init()
    {
        Kernel.ComponentCreated += ComponentCreated;
        Kernel.ComponentDestroyed += ComponentDestroyed;
    }

    void ComponentCreated(Castle.Core.ComponentModel model, object instance)
    {
        if (!(instance is IFooConsumer)) return;
        if (_bouncer == null) _bouncer = Kernel.Resolve<IEventAggregator>();
        _bouncer.WelcomeTo(instance);
    }

    void ComponentDestroyed(Castle.Core.ComponentModel model, object instance)
    {
        if (!(instance is IFooConsumer)) return;
        if (_bouncer == null) return;
        _bouncer.EscortOut(instance);
    }
}

尽管您需要更多代码来编写该工具,但 FooConsumers 无需自己注册/注销。由于注册码最初必须写在所有 FooConsumers 中,因此它往往会重复。这样订阅/退订作为佣金/退役要求完成,只需要处理一次。

附:代码是用记事本编写的,可能包含编译错误。

【讨论】:

  • 如果不是太麻烦,你能举一个例子来说明如何使用 Bar 类的工具吗?我想我已经在精神上确定了它(Bar 和 EventRegistrationFacility 都指向用于分发调用的同一个组件)但我想确定
【解决方案2】:

我认为将了解哪些对象实例化的关键点放在温莎城堡并不是最好的方法。你肯定需要访问一些容器方法,这样做会将你的组件链接到 Castle,这是不应该发生的。

我建议改为创建一个组件IBouncer。该组件将在所有 IFooConsumer 中作为单例注入,它会在创建和处置时调用它(处置是一种选择,您可以使用其他方法)

public interface IBouncer {
     IEnumerable<IFooConsumer> WhoIsInside {get;}
     void WelcomeTo(IFooConsumer consumer);
     void EscortOut(IFooConsumer consumer);
}

public Consumer: IFooConsumer {
    public Consumer(IBouncer bouncer) {
        bouncer.WelcomeTo(this);
    }

    public Dispose() {
        bouncer.EscortOut(this); // dispose pattern ommitted
    }
}

现在不要将IFooConsumer 的列表传递给您的Bar,只需将IBouncer 添加到其中并询问其中有哪些消费者。

class Bar
{
    public Bar(IBouncer bouncer) { ... }

    public void Foo()
    {
        // foo-ing
        foreach (var f in bouncer.WhoIsInside) f.FooHappened();
    }
}

【讨论】:

  • 随意改名:保镖/酒吧协会发生在我身上,如果我不把它拿出来休息,我的大脑不会放过它
  • +1。这是实现它的另一种方式。不过,我的具有业务逻辑的组件不知道容器——我说的是对 Windsor 容器本身的一些通用扩展——可能是一种能够跟踪请求类型实例的工具。然后配置一些像IInstanceTracker&lt;T&gt; 这样的接口,Bar 将依赖于类似于抽象工厂配置TypedFactoryFacility 的方式。它与您的IBouncer 基本相同。但是对于新手来说,你的方式肯定更容易理解:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-24
  • 1970-01-01
  • 1970-01-01
  • 2011-01-18
  • 1970-01-01
  • 2011-12-22
相关资源
最近更新 更多