2
votes

I'm trying to apply some advice at call-site pointcuts in external/3rd party assemblies using PostSharp 4.3.24 but I'm running into some issues. In the simplest example, I'm trying to record some information about all SQL executed by the application. My approach was to apply OnMethodBoundaryAdvice to all SQLCommand.Execute* methods (we don't use the legacy async pattern supplied by the BeginExecute* methods, so I'm intentionally not including them). I've tried this using both the Multicast section of the PostSharp solution configuration file (MySolution.pssln file) as well as the following equivalent assembly-wide multicast attribute application:

[assembly:Blah.Telemetry.Aspects.SqlCommandTelemetryAspect(
    AttributeTargetAssemblies = "System.Data", 
    AttributeTargetTypes = "System.Data.SqlClient.SqlCommand", 
    AttributeTargetMembers = "Execute*"
)]

My Aspect declaration currently looks as such:

[MulticastAttributeUsage(AllowExternalAssemblies = true, Inheritance = MulticastInheritance.Multicast)]
[PSerializable]
public sealed class SqlCommandTelemetryAspect : OnMethodBoundaryAspect { /*...*/ }

I have tried various combinations of target types, inheritance values, etc. in the MulticastAttributeUsage, but get the same confusing results every time.

The issue I'm having is that the advice is only applied to a single SqlCommand method (ExecuteReader). I also need it applied to all the other Execute methods (ExecuteNonQuery, for example) and all overloads. I have also tried targeting DbCommand.Execute* which contains the abstract definition of some of these overloads. When I taget ExecuteNonQuery directly (instead of using wildcards or regex) I get the following warning implying that no advice was applied (and decompilation confirms this):

POSTSHARP : postsharp warning PS0131: The project 'Blah.Framework.dll' does not contain any aspect or other transformation[...]

Am I doing something wrong or maybe is this a bug in PostSharp?

1
When applying aspect at a call-site pointcut, you need to pay attention to how that method call is emitted by the C# compiler. In this case it's a virtual call to System.Data.Common.DbCommand.ExecuteNonQuery(). But another problem here is that OnMethodBoundaryAspect cannot be applied to abstract methods. While this limitation is correct when applying the aspect to method bodies, it would also make sense to lift it for call-site pointcuts. We'll get back to you once we have more info on this issue. - AlexD
@AlexD Ah, I see; thanks for the info. So it's applying the same constraints to call-site as it does for weaving at declaration pointcuts. I was kinda thinking it might be related to how external weaving appeared to be an expansion on what was originally not designed for external targets. Is there another way to declare call-site pointcuts in the meantime? I'm trying to use this in a project at work and this is a big near-term goal to demonstrate the time savings of AOP - TheXenocide
@AlexD and sorry, perhaps this isn't the best place to ask that, but I think we are just getting our licensing approved now so I've yet to get information I need for correct support channels - TheXenocide
No problem, we monitor questions on SO as well. The response time may be slower. Anyway, there's no another way to advise on call-site pointcuts. We will implement a fix that will enable some use cases with abstract method targets. - AlexD
@AlexD excellent, thanks. Is there an issue tracker or somewhere I can pay attention to to see when I can start using this fix? Now that the holidays are over I'll try to figure out where our licensing is and such, but I'm also having some issues with a project.json based csproj and wondering if there's anywhere I can see about release schedules and such? Thanks again - TheXenocide

1 Answers

3
votes

The related bug has been fixed in PostSharp 4.3.26. It is now possible to apply OnMethodBoundary and MethodInterception aspects to abstract methods in external assemblies using aspect provider.

For example:

[assembly:MyAspectProvider(
    AttributeTargetAssemblies = "System.Data", 
    AttributeTargetTypes = "System.Data.Common.DbCommand", 
    AttributeTargetMembers = "Execute*"
)]

[PSerializable]
public class MyAspectProvider : MethodLevelAspect, IAspectProvider
{
    public IEnumerable<AspectInstance> ProvideAspects(object targetElement)
    {
        yield return new AspectInstance(targetElement, new MyAspect());
    }
}

[PSerializable]
public class MyAspect : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        // ...
    }
}