【问题标题】:Performance issue using aop for .net project将 aop 用于 .net 项目的性能问题
【发布时间】:2026-01-04 16:55:02
【问题描述】:

我担心在一个大型 .net 项目(大约有 50 万活跃用户)中使用 PostSharp 和 Spring.NET 对性能的影响。

基本上我想要这样的东西:我不想在创建用户报告时生成数百万次的日志记录。但是当用户使用系统时,我想记录他/她的一些活动。大多数 AOP 工具都没有这种灵活性。

有没有办法将方面附加到单个对象?或者在运行时打开或关闭一个方面?

【问题讨论】:

    标签: .net aop


    【解决方案1】:

    我对 PostSharp 和 Spring.NET 的理解是,您可以在设计时为您的类定义方面、切入点或其他任何内容。当您在打开或关闭方面,或更改切入点等方面使用类的对象时,您无能为力。

    你会得到你在类设计时定义的东西。无论您使用一个对象,还是该类的一百万个对象。你应该非常小心地使用它们。否则,你可能会中枪。

    您真正想要的是一个在对象级别而不是类级别处理方面的 AOP 工具。有一篇文章Add Aspects to Object Using Dynamic Decorator

    对我来说,方面是系统要求。系统要求是操作要求,最好在使用对象时在运行时在对象级别解决。

    说实话,我不明白为什么大多数 AOP 工具都试图在设计时解决类级别的系统需求。这或许可以解释为什么这么多年后它们的采用率仍然如此有限。

    【讨论】:

      【解决方案2】:

      aop-logging-issues 日志记录的性能开销取决于它是如何完成的。

      我想您想将日志记录建议注入您的代码。 动态 aop 只能是可能的

      • 用于虚拟方法和
      • 用于接口。

      所以可能你需要compiletime aop

      我不知道 post-sharp 是怎么做 aop 的。使用编译时 linfu-aop,每个方法都会获得一个执行前和一个执行后,其中动态决定是否执行以及执行哪些 aop 方面。这个技巧实际上消除了对非虚拟方法的限制,使它们成为伪虚拟。

      我更喜欢使用 log4.net 提供程序的手动(= non-aop-) 使用 common.logging 进行日志记录。此解决方案如果禁用日志记录,则运行时开销最小。启用/禁用日志记录可以在不重新编译的情况下选择性地完成 - 它只是一个配置文件,可能会说“使用 sql 的所有数据层活动”而不是“模块 xyz 中的 sql”。

      高成本的堆栈跟踪分析(我在调试/跟踪、信息中为我登录或为我登录的类)每个类只进行一次。

      禁用的日志记录可以减少到一个廉价的变量布尔评估加上一个 if。这种尺寸优化的速度可以由

      logger.Debug(m => m("... costly string formatting "));
      

      编译成类似于

      的语法
      if (logger.IsDebugEnabled) 
           call anonymous method that does 
              the expensive string formatting
      

      【讨论】:

        【解决方案3】:

        PostSharp 和 Spring.NET 的性能不能一起讨论。 PostSharp 使用编译时编织,而 Spring.NET 使用运行时编织。这意味着 PostSharp 仅在编译时添加 AOP 开销,而 Spring.NET 仅在运行时添加 - 阅读一些 articles from SharpCrafters 以获得更多信息。

        关于附加方面 - AOP 的关键特性之一是切入点。切入点可以被认为是选择是否为给定类型/方法等启用方面的谓词。因此,您始终可以创建类型结构和切入点以仅在系统的特定点上使用日志记录方面 - 这就是 AOP 的工作原理。

        关于在运行时打开/关闭方面 - 对于编译到您的代码中的 PostSharp,我相信没有任何技巧是不可能的。对于 Spring.NET,它会更容易,但我仍然不明白为什么需要它。

        【讨论】:

        • 您好 A. 我对 Spring .NET 的切入点进行了一些研究,但仍然无法弄清楚如何使用它来解决我的问题。当我为系统中的所有用户创建报告时,我不想记录所有用户 ID。如果他/她执行某些受限操作,例如 CreditCardNumber,我确实想记录 userId。可能还有其他地方我想记录或不记录,我还不知道。如何使用切入点来解决这些情况?
        • 使用切入点,您可以选择方面将运行的方法。您甚至可以在切面中执行一些基于if 的逻辑。配置方面运行的方法有很多,我相信为所有调用打开日志记录会比基于方法的更难。
        【解决方案4】:

        我积极致力于NConcern .NET AOP Framework,这是一个性能良好的新开源项目。此运行时 AOP 框架可以在非虚拟方法(包括静态方法)上注入代码,并且可以在运行时附加或分离。

        大多数现有的 AOP 框架都基于相同的拦截技术。

        • 代理(带反射的装饰器)或 ContextBoundObject/RemotingRealProxy 用于运行时实现
        • 编译时 AOP 框架的编译后 IL 重写。

        这就是为什么它是一种简单的方法来总结关于运行时与编译时实现的限制和性能。

        然而,这不是致命的,AOP 框架可以使用专有的注入技术和现代消费者 API 来实现,非常易于使用和高效。

        我的实现在运行时工作,并且只有很少的限制,保持非常低的耦合并提供内置表达式树来定义建议,以通过避免反射开销来保持最佳性能。

        请看一下,我对您对这个主题的看法很感兴趣。它可以帮助我改进产品。

        【讨论】:

        • 为什么不把代码放到GitHub上呢?更多人会看到。
        • 没有理由,我最终会将项目迁移/复制到 GitHub。