PostSharp替代

我只是想了解PostSharp,说实话,我认为这太棒了。

但有一件事我很难在PostSharp方面无法完成纯dependency injection(不是服务定位器),也许是因为编译时编织的结果。

来自PHP背景,Symfony有JMSAopBundle ,它仍然允许dependency injection它的拦截器。

.Net有一些具有相同function的库吗?

或者我错过了PostSharp的东西?

我不认为你在这里遗漏任何东西,而且限制确实是使用编译时编织的结果。

虽然我认为编译时编织工具在软件开发中占有一席之地,但我觉得它们经常被过度使用。 我经常看到它们被用来修补应用程序设计中的缺陷。 在我构建的应用程序中,我将通用接口应用于某些架构概念。 例如,我定义:

  • 一个ICommandHandler接口,用于实现某个用例的服务;
  • 一个IQueryHandler接口,用于执行查询的服务;
  • 一个IRepository接口作为存储库的抽象;
  • 用于执行消息validation的组件的IValidator接口;
  • 等等等等。

这允许我为这样的工件组创建单个通用装饰器(例如,允许在其自己的事务中运行每个用例的TransactionCommandHandlerDecorator )。 装饰器的使用具有许多优点,例如:

  • 这些通用装饰器完全与工具无关,因为没有对代码编织工具或拦截框架的引用。 PostSharp方面完全依赖于PostSharp,拦截器总是依赖于拦截框架,例如Castle.Proxy。
  • 由于装饰器只是一个普通的组件,因此可以将依赖关系注入到构造函数中,并且在使用dependency injection构建对象图时它们可以起到正常作用。
  • 装饰器代码非常干净,因为缺乏与任何第三方工具的依赖关系。
  • 因为它们与工具无关并且允许dependency injection,所以装饰器可以轻松地进行unit testing,而不必回复特殊技巧。
  • 需要应用横切关注点的应用程序代码也可以单独进行测试,因为装饰器不会在编译时编织。 在编译时编译装饰器时,您总是被迫执行应用程序代码测试的集成方式,或者需要恢复特殊构建技巧以防止它们应用于unit testing项目中。
  • 装饰器可以在运行时动态地和有条件地应用,因为没有编译时代码编织。
  • 性能与代码编织相同(甚至更快),因为在对象构造期间没有reflection。
  • 没有必要使用属性标记组件,以指出必须应用某些方面。 这可以使您的应用程序代码不受任何关于这种交叉问题的了解,并且可以更容易地替换它。 (请注意,您可以在PostSharp中使用此无属性方法,但您需要使用商业版本来解决此问题)。

关于这种应用程序设计已经写了很多; 这里有一些我自己写的文章:

  • 同时……在我的架构的命令端
  • 同时……在我的架构的查询方面
  • 编写高度可维护的WCF服务

UPDATE

装饰器很棒,但我喜欢AOP的是它的建议和连接点的概念。 有没有办法用装饰器模拟相同的function? 我现在只能想到反思。

加入点是“一个关注点将要附加的类中明确定义的位置”。 当您使用装饰器应用AOP时,您将“限制”加入方法边界上的点。 但是,如果您遵守SRP , OCP和ISP ,您将拥有非常精简的接口(通常使用单一方法)。 这样做时,您会注意到几乎没有理由在课程中的任何其他地方设置连接点。

建议是“可能会改变目标方法的输入和/或输出的关注点”。 当使用装饰器和基于消息的设计(我在这里宣传的东西)时,您的建议需要更改消息(或用更改的值替换完整的消息)或更改输出值。 事情与代码编织没有太大的不同,因为如果您应用建议,则应用建议的所有代码之间必须存在共同点。 因为我非常喜欢编译时支持,所以我通常用接口定义这种通用性。 这允许我为该特定接口编写具有generics类型约束的装饰器,这意味着装饰器只能在那里应用。 例:

 public class AddUserContextCommandHandlerDecorator : ICommandHandler where TCommand : IUserContextCommand { private readonly ICommandHandler decoratee; private readonly IUserContext userContext; public AddUserContextCommandHandlerDecorator( ICommandHandler decoratee, IUserContext userContext) { this.decoratee = decoratee; this.userContext = userContext; } public void Handle(TCommand command) { command.UserId = this.userContext.UserId; command.UserRoles = this.userContext.UserRoles; this.decoratee.Handle(command); } } 

这里, AddUserContextCommandHandlerDecorator可以应用于TCommand实现IUserContextCommand所有ICommandHandler实现。 IUserContextCommand包含UserIdUserRoles属性。 在这里,您可以看到装饰器在将command消息发送到其decoratee之前更改了command消息。 请注意,在这种情况下IMO通常最好将IUserContext注入真实的命令处理程序本身,而不是将此上下文信息传递给消息。