【问题标题】:How to wrap all calls to an assembly with PostSharp?如何使用 PostSharp 包装对程序集的所有调用?
【发布时间】:2014-05-12 18:02:29
【问题描述】:

我的 C# 项目引用了一个外部 .NET 程序集。我想在从我的项目到该程序集的每个调用周围插入锁定语句。我一直在尝试使用 PostSharp 来建立这一点,但找不到办法。我有外部程序集的源代码,我可能可以通过在其中插入方面来最简单地实现我的目标,但我更喜欢非侵入式解决方案,我可以保持外部程序集不受影响。

方法 1

我发现我可以包装对外部程序集的调用。遗憾的是,PostSharp 无法包装对抽象方法的调用,接口成员是抽象方法。因此这种方法不包括通过接口类型的调用。

[assembly: WrappingAspect(
    AttributeTargetAssemblies = "Library",
    AttributeTargetExternalMemberAttributes = MulticastAttributes.NonAbstract)]

[Serializable]
internal class WrappingAspect : OnMethodBoundaryAspect {
    public override void OnEntry(MethodExecutionArgs args) {
        Monitor.Enter(SyncRoot);
    }

    public override void OnExit(MethodExecutionArgs args) {
        Monitor.Exit(SyncRoot);
    }
}

方法2

也许我可以包装我的项目中所有引用外部程序集中类型的方法。我正在按照下面的思路思考。但是,我不能尝试这个,因为 ReflectionSearch 需要我目前没有的 PostSharp 许可证。

[assembly: WrappingAspect]

[Serializable]
internal class WrappingAspect : OnMethodBoundaryAspect {
    public override void OnEntry(MethodExecutionArgs args) {
        Monitor.Enter(SyncRoot);
    }

    public override void OnExit(MethodExecutionArgs args) {
        Monitor.Exit(SyncRoot);
    }

    public override bool CompileTimeValidate(MethodBase method) {
        return ReflectionSearch.GetDeclarationsUsedByMethod(method)
            .Any(r => r.UsedType.Assembly.FullName.StartsWith("Library"));
    }
}

问题

  1. 是否有一种非侵入式的方式来包装对外部程序集的所有调用,包括通过接口类型对方法的调用?
  2. 我的第二种方法可行吗?检测哪些方法引用了外部程序集并包装它们?
  3. 还有其他方法可以解决这个问题吗?

【问题讨论】:

    标签: postsharp


    【解决方案1】:

    您是否尝试过通过 XML 方法添加它们?直接来自 PostSharp(有点过时)docs

    通过 XML 添加方面具有无需修改源代码即可应用方面的优势,这在某些遗留项目中可能是一个优势。

    【讨论】:

    • 这听起来很有希望,但我需要更多帮助。我想知道我是否可以获取外部程序集(一个 DLL 文件)并为其编写一个 psproj 文件,然后在我的构建过程中 PostSharp 会将方面应用到 DLL 中。您链接到的文档没有提到如何执行此操作。看起来 PostSharp 通过查看与 psproj 位于同一目录中的 csproj 来找到目标 DLL。
    • 这是我今天要进一步研究的事情清单。 PS 有大量的文档,但我发现你必须做大量的挖掘工作,而且翻译成英文也会引起一些混乱。
    【解决方案2】:

    回答我自己的问题 1;就在这里。这是我的做法,使用 @Mikee 建议的 PostSharp 配置文件。我使用的是 PostSharp 3.1.39。

    简而言之,您可以运行 PostSharp 将代码编入 DLL,而无需更改该 DLL 的源代码。该命令可能看起来像这样(为了便于阅读,分成多行)

    postsharp.4.0-x64.exe temp\mylib.dll /P:Output=mylib.dll /NoLogo
        /X:myconfig.psproj
       "/P:ReferenceDirectory=$(ProjectDir) "
       "/P:SearchPath=$(OutDir) "
       "/P:Configuration=$(Configuration)"
       "/P:Platform=$(Platform)"
       "/P:MSBuildProjectFullPath=$(ProjectPath) "
        /P:TargetFrameworkIdentifier=.NETFramework
    

    此命令中的 $(variables) 直接来自 Visual Studio,例如如果您在构建后事件中运行它。注意 Visual Studio 变量中的尾随反斜杠;在结束引号之前添加一个额外的空格是必要的预防措施。

    mylib.dll 是完成编织的目标程序集。输入和输出 DLL 必须是两个不同的文件,因此输入位于临时文件夹中。

    我的配置文件 myconfig.psproj 如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <Project xmlns="http://schemas.postsharp.org/1.0/configuration">
      <Multicast xmlns:my="clr-namespace:MyApp.Aspects;assembly:MyApp">
        <my:MyAspect AttributeTargetMemberAttributes="Public"/>
      </Multicast>
    </Project>
    

    此配置会将 MyApp 程序集中的切面 MyApp.Aspects.MyAspect 应用到目标程序集中的所有公共成员。更多配置属性可参考documentation of MulticastAttribute

    要在更复杂的场景中运行 PostSharp,可能需要更多的配置参数。运行postsharp.4.0-x64.exe /? 会为您提供一些无用的命令行参数列表。要了解 PostSharp 在作为 Visual Studio 项目的一部分运行时真正使用的参数类型,您可以这样做:

    1. 将 PostSharp 添加到您的 C# 项目(作为 NuGet)。
    2. 为方法添加一些虚拟方面。
    3. 使用详细输出运行构建(在 Visual Studio 2012 中:工具 -> 选项 -> 项目和解决方案 -> 构建和运行 -> MSBuild 项目构建输出详细度 -> 诊断)。
    4. 构建后,在构建输出窗口中搜索包含Task "PostSharp30" 的行,然后向下浏览找到以"Connected to the pipe after XXX ms. Requesting with XX arguments" 开头的行。
    5. 下面一行是postsharp.4.0-x64.exe 的参数列表。注意参数用分号隔开;删除分号并引用参数以保留有意义的空格。

    我为我的测试项目获得的参数列表比上面的最终命令长得多。许多参数不是必需的。

    警告:PostSharp 的免费 Express 版本不支持编织迭代器方法(使用 return yield 的方法)。你会收到关于它们的警告。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多