C# 温莎-装饰所有找到的实现

C# 温莎-装饰所有找到的实现,c#,decorator,castle-windsor,C#,Decorator,Castle Windsor,我有一个获取数据的接口,IGetter,它有几个实现(每个实现用于从其他类型的源获取数据)。此接口是通用的,因为它的实现可以返回不同类型的事件(SourceEvent和StatisticsEvent,但这两种都基于IEvent接口) 下面是一个示例代码(带有隐藏的细节以缩短文章的篇幅-LINQPad的完整工作演示)。装饰程序正在注册,但尚未解决。只解析普通的getter,没有日志记录和缓存功能 我应该如何注册此代码以使decorator正常工作 // An interface defining

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

下面是一个示例代码(带有隐藏的细节以缩短文章的篇幅-LINQPad的完整工作演示)。装饰程序正在注册,但尚未解决。只解析普通的
getter
,没有日志记录和缓存功能

我应该如何注册此代码以使decorator正常工作

// 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\"");
}
//一个定义事件的接口,“在发生时发生的事情”
公共接口IEvent{DateTime When{get;set;}}
//它们来自多个来源(例如Windows事件日志、发送的电子邮件、磁盘活动、问题跟踪程序中记录的操作等)
公共类SourceEvent:IEEvent{public DateTime When{get;set;}公共字符串What{get;set;}}
//一些实现:
公共类MailEvent:SourceEvent{}
公共类FileEvent:SourceEvent{}
//从所有实现的源返回的所有事件的列表最终将在任何给定时间计算到活动级别
public类StatisticsEvent:IEEvent{public DateTime When{get;set;}public int HowMany{get;set;}
//能够从一个源读取事件的类的接口
公共接口IGetter,其中TData:IEvent{/*…*/}
//一些实现
公共类邮件获取程序:IGetter{/*…*/}
公共类FileGetter:IGetter{/*…*/}
//它的实现也是基于IGetter的(但它从所有其他IGetter获取数据-在本例中简化)
公共类StatisticsGetter:IGetter{/*…*/}
//IGetters的装饰师-我希望Getter能够做的所有神奇的事情
公共类缓存:IGetter where-TData:IEvent{/*…*/}
公共类记录器:IGetter where-TData:IEvent{/*…*/}
void Main(字符串[]args)
{
//依赖注入注册
var ioc=新的WindsorContainer();
国际奥委会,登记(
组件。For(typeof(IGetter))。由(typeof(Cache))实现,
Component.For(typeof(IGetter)).ImplementedBy(typeof(Logger)),//此处注册的装饰器未被注入
类。FromThisAssembly()
.BasedOn(类型(IGetter))
.WithServiceBase()//所有源的getter都已注册
);
var source1=ioc.Resolve();
source1.Query().Dump(“source1-\“查询邮件”);
source1.Query().Dump(“Source 1-\”From cache\”);//不从缓存读取,而是再次查询
var source2=ioc.Resolve();
source2.Query().Dump(“source2-\“查询文件”);
source2.Query().Dump(“源2-\”来自缓存\“”);
var stats=ioc.Resolve();//必须成功解析IGetter的所有实现和IEvent的所有实现
stats.Query().Dump(“Statistics-\”查询stats\”);
stats.Query().Dump(“来自缓存的统计-\”);
//这是可行的,但我们需要温莎帮我们做到这一点
//var manual=新缓存(新记录器(新MailGetter());
//manual.Query().Dump(“手动-\“查询邮件”);
//manual.Query().Dump(“manual-\”来自缓存\“”);
}

您遇到了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
            );
ioc.Register(
Component.For().ImplementedBy(),
Component.For().ImplementedBy(),
Component.For().ImplementedBy(),
Component.For().ImplementedBy(),
Component.For().ImplementedBy(),
Component.For().ImplementedBy(),
类。FromThisAssembly()
.BasedOn(类型(IGetter))
.WithServiceBase()//所有源的getter都已注册
);
缺点是很容易忘记注册另一个decorator,但这会起作用

拦截器(根据其用途)
您的里程数可能会因这些而有所不同,因为它们有效地代理了您的实现,并在执行之前或之后进行操作。他们的行为有点像装饰师,但不是纯粹意义上的装饰师


根据公认的答案,手动执行
ISubDependencyResolver

经过一段时间的思考,我决定在我的情况下,详细的手动注册是最不邪恶的事情。