【问题标题】:How to determine if a class is decorated with a PostSharp aspect at build or run time如何确定一个类是否在构建或运行时使用 PostSharp 方面进行装饰
【发布时间】:2014-06-05 21:03:45
【问题描述】:

我有一系列形式的 WCF 服务

[WCFEndpoint]
public class MyWCFEndpoint : WCFSvcBase, IMyWCFEndpoint
{

}    

其中 WCFEndpoint 是 PostSharp OnMethodBoundaryAspect:

[Serializable]
public class WCFEndpointAttribute : OnMethodBoundaryAspect
{

}

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

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

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

Assembly assm = Assembly.GetAssembly(typeof(ReferenceSvc));

Type[] types = assm.GetTypes();

IEnumerable<Type> 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](或类似的自定义属性)“打印”到最终编译的程序集元数据中。这是迄今为止概念上最简单的答案:它在代码中可见,并且在程序集元数据中可见。 有没有办法做到这一点?

我发现this article 描述了自动注入自定义属性的 TypeLevelAspect 的使用,如果我可以从 TypeLevelAspect 和 OnMethodBoundaryAspect 派生 WCFEndpointAttribute(是的,我知道为什么我不能这样做),这将非常有用。 :)

我考虑过的其他解决方法是:

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

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

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

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

【问题讨论】:

    标签: c# .net postsharp


    【解决方案1】:

    我能够通过在方面定义中包含 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()
    {
    
    }
    

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

    验证码变成

    IEnumerable<Type> 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 方面(因此我可以直接查找所有类),但是这个元数据足以解决我的直接问题。

    感谢大家帮助缩小范围!

    【讨论】:

      【解决方案2】:

      如果你的 WcfEndpoints 真的都需要用 Aspect 编织,你可以:

      • 相信您的开发人员会将 [WCFEndpoint] 添加到每个服务中
      • 编写一个程序来检查 [WCFEndpoint] 属性的来源(我建议使用 Rosyln)
      • 假设您的程序员很笨,容易出错,而是以编程方式添加方面。 (请注意,我实际上并不是说笨,我的 CIO 今天做了一个很好的演讲,关于您应该如何让计算机去做不会增加价值的事情,并让您的开发人员将所有时间都花在增加价值上)。

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

      【讨论】:

      • 程序化小费看起来类似于程序集级多播(问题中的选项#2)。这些“自动附件”看起来是迄今为止最好的选择,我开始尝试这些。您的第三个要点是我们如何处理容易忘记但绝对需要的代码元素。人类可以忘记平凡的脚步;计算机(和构建工具)没有;将我们所能做的一切自动化,以尽量减少人为因素。
      猜你喜欢
      • 2015-02-25
      • 1970-01-01
      • 2021-06-19
      • 1970-01-01
      • 2011-07-09
      • 2012-08-21
      • 2012-02-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多