C#学习教程:SimpleInjector-RegisterDecoratorwithAnotherDependencywithAnotherDependencyofSameGenericType它甚至出现在最近的2.5.0中。我有一种情况,我想装饰一个封闭的泛型类型,比如ICommandHandler,一个装饰器(通过构造函数注入)一个ICommandHandler类型的内部处理程序,以及另一种类型的处理程序,比如ICommandHandler。尽管这些命令处理程序类型是不同的,但当我在此类装饰器类型上调用RegisterDecorator时,SimpleInjector似乎感到困惑并抛出异常:ArgumentException:为了让容器使用MessageLogger作为装饰器,其构造函数必须包含一个ICommandHandler单个类型参数(或Func)——即被装饰实例的类型。参数类型ICommandHandler在类MessageLogger的构造函数中定义了多次。...即使装饰器显然只有一个ICommandHandler参数。以下是抛出异常的完整工作示例:publicinterfaceICommandHandler{voidExecute(Tcommand);}publicclassLogCommand{publicstringLogMessage{get;放;}publicDateTimeTime{get;放;}}publicclassLogger:ICommandHandler{publicvoidExecute(LogCommandcommand){Debug.WriteLine(string.Format("Message"{0}"sentat{1}",command.LogMessage,command.Time));}}publicclassMessageCommand{publicstringMessage{get;放;}}publicclassMessageSender:ICommandHandler{publicvoidExecute(MessageCommandcommand){Debug.WriteLine(command.Message);}}//记录发送消息的消息命令处理程序装饰器publicclassMessageLogger:ICommandHandler{privateICommandHandlerinnerHandler;私有ICommandHandler记录器;//注意这些依赖是两个不同的封闭泛型类型publicMessageLogger(ICommandHandlerinnerHandler,ICommandHandlerlogger){this.innerHandler=innerHandler;this.logger=记录器;}publicvoidExecute(MessageCommandcommand){innerHandler.Execute(command);varlogCommand=newLogCommand{LogMessage=command.Message,Time=DateTime.Now};记录器.执行(logCommand);}}//这按预期工作,但在现实世界的应用程序中很乏味ICommandHandlerResolveManually(){ICommandHandlersender=newMessageSender();ICommandHandler记录器=newLogger();ICommandHandlerloggerSender=newMessageLogger(sender,logger);返回loggerSender;}//这就是我想要的工作-看起来很简单?ICommandHandlerResolveWithSimpleInjector(){varcontainer=newContainer();container.Register();container.Register();//下一行抛出异常container.RegisterDecorator(typeof(ICommandHandler),typeof(MessageLogger));返回container.GetInstance();}voidMain(){//ICommandHandlersender=ResolveManually();ICommandHandlersender=ResolveWithSimpleInjector();varcommand=newMessageCommand{Message="HelloWorld!"};发件人。执行(命令);我找不到关于这种情况的任何信息这是一个错误还是我遗漏了什么?编辑我正在寻找有关SimpleInjector的开发人员反馈,以查明此限制是否有技术原因,或者它只是被忽略了......除非有人可以说服我这个设计在逻辑上有缺陷并且有理由我不应该最终以这种方式做事,这是迄今为止所有答案都无法做到的。非常感谢您的反馈。在我看来,核心问题是RegisterDecorator()将两种不同的封闭泛型类型视为同一类型。可能有基于其内部运作的技术原因,但也许不是?我不得不在代码库中四处寻找,看看发生了什么。您可能会称这是SimpleInjector实现中的一个小故障,但在我看来,这是一个公平的权衡。SimpleInjector的装饰器子系统基于使用开放泛型类型和开放泛型装饰器的思想。它在装饰器注册时所做的检查是查看装饰器的构造函数是否只有一个装饰。此检查是使用必须应用装饰器的开放通用抽象完成的;在你的情况下ICommandHandler。由于此时只有泛型ICommandHandler可用,因此两个构造函数参数匹配此类型。这些先决条件检查可以改进,但它实际上非常讨厌,而且此功能的用处非常有限。它是有限的,因为它只对非通用装饰器有用。例如,看看下面的装饰器:publicclassGenericDecorator:ICommandHandler{publicGenericDecorator(ICommandHandlerdecoratee,ICommandHandlerdependency){}}这个装饰器是通用的,允许你将它应用到任何装饰器上,这样比较好用。但是当您解析ICommandHandler时会发生什么?这将导致循环依赖图,SimpleInjector将(显然)创建失败并抛出异常。它必须抛出,因为在这种情况下装饰器将有两个ICommandHandler参数。第一个是decoratee,将被注入到您的Logger中,第二个将是一个普通的依赖项,将被注入到GenericDecorator中,当然是递归的。所以我认为问题出在你的设计上。通常,我建议不要将命令处理程序与其他命令处理程序组合在一起。ICommandHandler应该是业务层之上的抽象,它定义了表示层如何与业务层通信。它不是业务层内部使用的机制。如果你开始这样做,你的依赖配置会变得非常复杂。下面是一个使用DeadlockRetryCommandHandlerDecorator和TransactionCommandHandlerDecorator的图表示例:但是看看当我们应用你的MessageLogger装饰器时会发生什么:第二个TransactionCommandHandlerDecorator。在一个事务中有一个事务并且有嵌套的死锁重试(在一个事务中)是什么意思。这可能会导致您的应用程序出现严重的可靠性问题(因为数据库死锁会导致您的操作在无事务连接中继续)。虽然可以以检测到嵌套的方式创建装饰器,以便在嵌套时可以正常工作,但这会使实现它们变得更加困难和脆弱。国际海事组织浪费你的时间。因此,不是允许嵌套的命令处理程序,而是让命令处理程序和命令处理程序装饰器依赖于其他抽象。在您的情况下,通过使用某种类型的ILogger接口更改装饰器可以轻松解决问题:私有ILogger记录器;publicMessageLogger(ICommandHandlerinnerHandler,ILogger记录器){this.innerHandler=innerHandler;this.logger=记录器;}publicvoidExecute(MessageCommandcommand){innerHandler.Execute(command);logger.Log(command.Message);如果表示层需要直接记录日志,你仍然可以有一个ICommandHandler实现,但在这种情况下,实现也可以简单地依赖于ILogger:publicLogCommandHandler(ILoggerlogger){this.logger=logger;}publicvoidExecute(LogCommandcommand){logger(string.Format("Message"{0}"sentat{1}",command.LogMessage,DateTime.Now));这是一个边缘案例,你可以无论如何争论,但事实上YesSimpleInjector明确不支持你正在尝试做的事情。在您的示例ICommandHandler中,装饰器通常需要对所有(或部分)特定抽象应用通用逻辑。也就是说,MessageLogger是用来装饰ICommandHandler的,因为它是ICommandHandler的装饰器,它的构造函数中只能使用一个ICommandHandler。此外,允许这样的事情需要大量可怕的循环检查,最好通过更简洁的设计来避免!所以你通常会定义一个装饰器,它与装饰类型具有相同的接口(和通用参数)publicclassMessageLogger:ICommandHandlerwhereTCommand:{//....}我能想到的第一个解决方案来缓解这个问题解决方案是创建一个中介来移除直接依赖:publicclassLoggerMediator{privatereadonlyICommandHandlerlogger;publicLoggerMediator(ICommandHandlerlogger){this.logger=logger;}publicvoidExecute(LogCommandcommand){this.logger.Execute(command);}}并更改您的MessageLogger以使用中介。publicclassMessageLogger:ICommandHandler其中TCommand:MessageCommand{privateICommandHandlerinnerHandler;私有LoggerMediator记录器;publicMessageLogger(ICommandHandlerinnerHandler,LoggerMediator记录器){this.innerHandler=innerHandler;this.logger=记录器;.执行(命令);varlogCommand=newLogCommand{LogMessage=command.Message,Time=DateTime.Now};记录器.执行(logCommand);}}顺便说一句,你可以像这样简化注册varcontainer=newContainer();container.RegisterManyForOpenGeneric(typeof(ICommandHandler),typeof(ICommandHandler).Assembly);容器.Register();container.RegisterDecorator(typeof(ICommandHandler),typeof(MessageLogger));容器.验证();代码库,我发现我有一个类似的要求,我用一个额外的类解决了它——一个通用的命令中介:publicCommandHandlerMediator(ICommandHandlerhandler){this.handler=处理程序;}publicvoidExecute(TCommandcommand){this.handler.Execute(command);}}注册如下:container.RegisterOpenGeneric(typeof(CommandHandlerMediator),typeof(CommandHandlerMediator));参考如下:publicclassMessageLogger:ICommandHandlerwhereTCommand:{privateICommandHandlerdecorated;私人CommandHandlerMediator记录器;publicMessageLogger(ICommandHandlerdecorated,CommandHandlerMediatorlogger){this.innerHandler=innerHandler;this.logger=记录器;}//....}您为所有处理程序排序创建的新类您可以将装饰器更改为publicMessageLogger(ICommandHandlerinnerHandler){this.innerHandler=innerHandler;}将必要的构造函数签名与“ICommandHandler(或Func>)类型的单个参数”相匹配。并将记录器作为属性而不是ctor参数注入。我没有使用SimpleInjector,但查看您的异常消息,这是最明显的解决方案,因为装饰器构造函数签名限制。您的解决方案似乎有点尴尬,因为它是装饰器和构造函数注入/组合(某物)的组合。虽然这不是您的问题,但它可能会解决您的问题(我会说是一种更好的方式):publicclassLoggingHandler:ICommandHandler{publicLoggingHandler(ICommandHandlerinnerHandler){this.innerHandler=innerHandler;}publicvoidExecute(MessageCommandcommand){innerHandler.Execute(command);Debug.WriteLine(string.Format("消息"{0}"在{1}发送",command.Message,DateTime.Now));我认为不需要为LogMessage提供单独的CommandHandler。您只需记录装饰实际命令处理程序的对象。它的目的是什么?使用这种方法,您将拥有纯装饰,这是IMO更好的解决方案,因为它节省了两个额外的类。以上就是C#学习教程分享的全部内容:SimpleInjector-另一个使用相同泛型类型的依赖注册装饰器。如果对大家有用,需要进一步了解C#学习教程,希望大家多加关注——本文来自网络收藏,不代表立场,如涉及侵权,请右击联系管理员删除。如需转载请注明出处: