Windsor - 装饰所有找到的实现

Windsor - decorate all found implementations

我有一个获取数据的接口,IGetter<IEvent>,它有几个实现(每个都用于从其他类型的源获取数据)。这个接口是通用的,因为它的实现可以 return 不同类型的事件(SourceEventStatisticsEvent,但是两者都基于 IEvent 接口)。

这是一个示例代码(包含隐藏的细节以缩短 post - LINQPad 的完整工作演示是 here)。正在注册装饰器,但未解决。仅解析 Getters,没有日志记录和缓存功能。

我应该如何注册此代码以使装饰器工作?

// An interface defining an event, "something happened at time When"
public interface IEvent { DateTime When { get; set; }}
// They come from a number of sources (e.g. Windows Event Log, sent e-Mails, disk activity, actions logged in issue tracker etc.)
public class SourceEvent     : IEvent { public DateTime When { get; set; } public string What { get; set; }}
// Some implementations:
public class MailEvent       : SourceEvent { }
public class FileEvent       : SourceEvent { }
// List of all events returned from all implemented sources will be ultimately calculated into activity level at any given time
public class StatisticsEvent : IEvent { public DateTime When { get; set; } public int HowMany{ get; set; }}


// An interface for a class capable of reading IEvents from one source
public interface IGetter<TData> where TData : IEvent { /*...*/ }
// Some implementations
public class MailGetter       : IGetter<MailEvent> { /*...*/ }
public class FileGetter       : IGetter<FileEvent> { /*...*/ }
// Implementation of this is also based on IGetter (but it gets data from all other IGetters - simplified in this example)
public class StatisticsGetter : IGetter<StatisticsEvent> { /*...*/ }


// Decorators for IGetters - all the magical things I want Getters to be able do
public class Cache<TData>  : IGetter<TData> where TData : IEvent { /*...*/ }
public class Logger<TData> : IGetter<TData> where TData : IEvent { /*...*/ }


void Main(string[] args)
{
    // Dependency Injection registration
    var ioc = new WindsorContainer();
    ioc.Register(
        Component.For(typeof(IGetter<>)).ImplementedBy(typeof(Cache<>)),
        Component.For(typeof(IGetter<>)).ImplementedBy(typeof(Logger<>)), // Decorators registered here are not being injected
        Classes.FromThisAssembly()
            .BasedOn(typeof(IGetter<>))
            .WithServiceBase() // Getters for all sources are registered fine
    );

    var source1 = ioc.Resolve<IGetter<MailEvent>>();
    source1.Query().Dump("Source 1 - \"Querying mails\"");
    source1.Query().Dump("Source 1 - \"From cache\""); // Doesn't read from cache, instead it queries again

    var source2 = ioc.Resolve<IGetter<FileEvent>>();
    source2.Query().Dump("Source 2 - \"Querying files\"");
    source2.Query().Dump("Source 2 - \"From cache\"");

    var stats = ioc.Resolve<IGetter<StatisticsEvent>>(); // All implementations of IGetter and all implementations of IEvent must be successfully resovled
    stats.Query().Dump("Statistics - \"Querying stats\"");
    stats.Query().Dump("Statistics - \"From cache\"");

//  This works, but we need Windsor to do it for us
    //  var manual = new Cache<MailEvent>(new Logger<MailEvent>(new MailGetter()));
    //  manual.Query().Dump("Manual - \"Querying mails\"");
    //  manual.Query().Dump("Manual - \"From cache\"");
}

您 运行 对 Castle Windsor 决定解决依赖关系的顺序和优先级的方式相当固执己见。

因为你有一个开放的泛型注册,或者更确切地说你有两个,而且还有大量的具体类型注册,所以它会优先考虑具体的实现。

关于如何解决这个问题有几种思路。

所有泛型装饰器的手动显式注册

ioc.Register(
    Component.For<IGetter<MailEvent>>().ImplementedBy<Logger<MailEvent>>(),
    Component.For<IGetter<FileEvent>>().ImplementedBy<Logger<FileEvent>>(),
    Component.For<IGetter<StatisticsEvent>>().ImplementedBy<Logger<StatisticsEvent>>(),

    Component.For<IGetter<MailEvent>>().ImplementedBy<Cache<MailEvent>>(),
    Component.For<IGetter<FileEvent>>().ImplementedBy<Cache<FileEvent>>(),
    Component.For<IGetter<StatisticsEvent>>().ImplementedBy<Cache<StatisticsEvent>>(),

    Classes.FromThisAssembly()
                       .BasedOn(typeof(IGetter<>))
                       .WithServiceBase() // Getters for all sources are registered fine
            );

缺点是很容易忘记注册另一个装饰器,但它可以解决问题。

拦截器(根据他们的 documentation
您的里程可能会因这些而异,因为它们有效地代理了您的实施并在执行之前或之后采取行动。它们有点像装饰器,但不是纯粹意义上的装饰器。

根据已接受的答案 here.

手动实施 ISubDependencyResolver