如何确定在构建或运行时是否使用PostSharp方面修饰了类

我有一系列表格的WCF服务

[WCFEndpoint] public class MyWCFEndpoint : WCFSvcBase, IMyWCFEndpoint { } 

其中WCFEndpoint是PostSharp OnMethodBoundaryAspect:

 [Serializable] public class WCFEndpointAttribute : OnMethodBoundaryAspect { } 

[WCFEndpoint]的主要目的是通过OverEntry和OnExit的覆盖以及其他诊断信息提供有关WCF调用的持续时间信息。

问题是开发人员偶尔忘记将[WCFEndpoint]添加到新的WCF服务(编辑:和其他开发代码评论的开发人员忘记提及它!)。

我的目标是保证从WCFSvcBase派生的每个类都使用[WCFEndpoint]属性进行修饰。 我的计划是编写一个自动(NUnit)测试来查找从WCFSvcBase派生的所有类,然后查看自定义属性并确认WCFEndpointAttribute在该集合中(为便于阅读而简化):

 Assembly assm = Assembly.GetAssembly(typeof(ReferenceSvc)); Type[] types = assm.GetTypes(); IEnumerable serviceTypes = types.Where(type => type.IsSubclassOf(typeof(WCFSvcBase)) && !type.IsAbstract ); foreach (Type serviceType in serviceTypes) { if (!serviceType.GetCustomAttributes(true).Any(x => x.GetType() == typeof(WCFEndpointAttribute))) { Assert.Fail( "Found an incorrectly decorated svc!" ) } } 

我的问题是WCFEndpointAttribute没有出现在GetCustomAttributes(true)中 – 即使它派生自System.Attribute。 我通过查看.Net Reflector中的程序集来证实了这一点。

理想情况下,因为OnMethodBoundaryAspect派生自Attribute,所以我想以某种方式将[WCFEndpoint](或类似的自定义属性)“打印”到最终编译的程序集元数据中。 到目前为止,这是概念上最简单的答案:它在代码中可见,并且在程序集元数据中可见。 有没有办法做到这一点?

我发现这篇文章描述了一个自动注入自定义属性的TypeLevelAspect的使用,如果我可以从TypeLevelAspect和OnMethodBoundaryAspect派生WCFEndpointAttribute,那将会很有效(是的,我知道为什么我不能这样做)。 🙂

我考虑解决这个问题的其他方法是:

1)执行代码解析以确认[WCFEndpoint]是“接近”(上面一行) : WCFSvcBase 。 这在可维护性/稳健性方面存在明显问题。

2)通过多播自动将[WCFEndpoint]附加到从WCFSvcBase派生的所有类。 我不喜欢这个,因为它在检查服务代码时模糊了PostSharp /属性的细节,尽管如果没有更优雅的解决方案,它是可能的。

3)创建一个AssemblyLevelAspect,在构建时使用[WCFEndpoint]属性吐出所有类的列表。 然后,我可以将此静态列表与从WCFBaseSvc派生的reflection生成的类列表进行比较。 AssemblyLevelAspect详细信息在这里 。

我还应该指出,我只限于PostSharp的Free / Express版本。

我可以通过在方面定义中包含PersistMetadata = true来持久化WCFEndpoint属性:

 [Serializable] [MulticastAttributeUsage(PersistMetaData = true)] public class WCFEndpointAttribute : OnMethodBoundaryAspect { } 

看一下.Net Reflector中的元数据,我现在看到了

 public class MyWCFEndpoint : WCFSvcBase, IMyWCFEndpoint { [WCFEndpoint] static MyWCFEndpoint(); [WCFEndpoint] public MyWCFEndpoint(); [WCFEndpoint] public void EndpointFn1(); [WCFEndpoint] public void EndpointFn2(); } 

存储在元数据中的WCFEndpointAttribute的定义是

 public WCFEndpointAttribute() { } 

从这里开始,我可以将validation规则调整为“如果从WCFSvcBase派生的类中至少有一个方法具有WCFEndpoint属性,则validation通过;否则它将失败。”

validation码变为

 IEnumerable serviceTypes = types.Where(type => type.IsSubclassOf(typeof(WCFSvcBase)) && !type.IsAbstract ); foreach (Type serviceType in serviceTypes) { var members = serviceType.GetMembers(); if (!members.Exists( member => member.GetCustomAttributes(true).Any(attrib => attrib.GetType() == typeof(WCFEndpointAttribute)))) { Assert.Fail( "Found an incorrectly decorated svc!" ) } } 

我当然没有意识到OnMethodBoundaryAspect正在被多播到所有成员(私有,公共,静态等),尽管事后看来肯定有意义。 我最终可能会创建一个复合方面,其中包含类的TypeLevelAspect和成员的OnMethodBoundaryEntry方面(因此我可以直接查找所有类),但此元数据足以解决我的直接问题。

感谢所有人帮助缩小范围!

如果您的WcfEndpoints确实需要使用Aspect进行编织,您可以:

  • 相信您的开发人员将[WCFEndpoint]添加到每个服务
  • 编写一个程序来检查[WCFEndpoint]属性的来源(我建议使用Rosyln)
  • 假设您的程序员愚蠢,容易出错,并以编程方式添加方面。 (注意我实际上并不是说愚蠢,我的CIO今天发表了一篇很好的演讲,讲述了如何让计算机做一些没有增值的事情,并让你的开发人员花费他们所有的时间来增加价值)。

Postsharp有一个function调用Programmatic Tipping ,您可以使用C#编写程序来描述添加方面的位置。 将其中一个添加到构建工具链中,完全忘记[WCFEndpoint]属性,让您有更多时间编写代码。