Question

My C# project refers to an external .NET assembly. I would like to insert locking statements around every call from my project to that assembly. I've been trying to establish this with PostSharp but can't find a way to do it. I have the source to the external assembly and I probably could achieve my goal the easiest by inserting the aspect there, but I prefer a non-intrusive solution where I can leave the external assembly untouched.

Approach 1

I have found out that I can wrap calls to the external assembly. Sadly, PostSharp is unable to wrap calls to abstract methods, and interface members are abstract methods. Therefore this approach doesn't cover calls through interface types.

[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);
    }
}

Approach 2

Perhaps I could wrap all the methods in my project that refer to types in the external assembly. I'm thinking along the lines below. However, I cannot try this out because ReflectionSearch requires a PostSharp license that I don't currently have.

[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"));
    }
}

Questions

  1. Is there a non-intrusive way to wrap all calls to an external assembly, including calls to methods through an interface type?
  2. Would my second approach work; to detect which methods refer to the external assembly and wrap them?
  3. Are there other approaches to this problem?
Was it helpful?

Solution 2

Answering my own question number 1; Yes, there is. Here's how I did it, using PostSharp configuration files as @Mikee suggested. I used PostSharp 3.1.39.

In short, you can run PostSharp to weave code into a DLL without changing the source code of that DLL. The command might look like this (split into multiple lines for readability)

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

The $(variables) in this command come straight out of Visual Studio, e.g. if you run this in your post-build event. Beware trailing backslashes in Visual Studio variables; adding an extra space before the closing quote is a necessary precaution.

mylib.dll is the target assembly where the weaving will be done. The input and output DLL must be two different files, hence the input is in a temp folder.

The configuration file myconfig.psproj looks like this in my case:

<?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>

This configuration will apply the aspect MyApp.Aspects.MyAspect from the MyApp assembly into all public members in the target assembly. More configuration attributes can be found from the documentation of MulticastAttribute

To run PostSharp in a more complex scenario, more configuration parameters may be required. Running postsharp.4.0-x64.exe /? gives you a somewhat unhelpful list of command line parameters. To find out what kind of parameters PostSharp really uses when it's run as part of a Visual Studio project, you can do this:

  1. Add PostSharp to your C# project (as a NuGet).
  2. Add some dummy aspect to a method.
  3. Run the build with verbose output (in Visual Studio 2012: Tools -> Options -> Projects and Solutions -> Build and Run -> MSBuild project build output verbosity -> Diagnostic).
  4. After the build, search the build output window for a line containing Task "PostSharp30" and then browse down to find a line starting with "Connected to the pipe after XXX ms. Requesting with XX arguments".
  5. What follows on the line is a list of parameters to postsharp.4.0-x64.exe. Note that the parameters are separated by semicolons; remove the semicolons and quote the parameters to preserve meaningful spaces.

The list of parameters I got for my test project was much longer than the final command above. Many of the parameters weren't necessary.

Caveat: The free Express version of PostSharp doesn't support weaving iterator methods (ones that use return yield). You'll get warnings of them.

OTHER TIPS

Have you tried adding them via the XML approach? Straight from the PostSharp (slightly outdated) docs

Adding aspects through XML gives the advantage of applying aspects without modifying the source code, which could be an advantage in some legacy projects.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top