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

时间:2014-06-05 21:03:45

标签: c# .net 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<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派生自属性,我想以某种方式&#34; print&#34;将[WCFEndpoint](或类似的自定义属性)放入最终编译的程序集元数据中。到目前为止,这是概念上最简单的答案:它在代码中可见,并且在程序集元数据中可见。 有没有办法做到这一点?

我发现this article描述了自动注入自定义属性的TypeLevelAspect的使用,如果我可以从TypeLevelAspect和OnMethodBoundaryAspect派生出WCFEndpointAttribute,那将会很有效(是的,我知道为什么我可以&#t; t去做)。 :)

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

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

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

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

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

2 个答案:

答案 0 :(得分:2)

我能够通过在方面定义中包含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方面(所以我可以直接查找所有类),但这个元数据足以解决我的直接问题。

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

答案 1 :(得分:1)

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

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

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