Ok, this may get lengthy. I am trying to do two things:
I want to have a class that implements an interface by holding an instance of another class that every call is routed to.
I also want to intercept all method calls and do something.
Doing both on their own works great. Combining them seems to work only in one execution order and as Murphy has it, it's the wrong one (at least for me).
I'd like to inject the composition first so that the interception of all calls will also intercept those that were previously injected.
namespace ConsoleApplication13
{
using System;
using System.Reflection;
using PostSharp;
using PostSharp.Aspects;
using PostSharp.Aspects.Dependencies;
using PostSharp.Extensibility;
[Serializable]
[ProvideAspectRole("COMPOSER")]
public sealed class ComposeAspectAttribute : CompositionAspect
{
[NonSerialized]
private readonly Type interfaceType;
private readonly Type implementationType;
public ComposeAspectAttribute(Type interfaceType, Type implementationType)
{
this.interfaceType = interfaceType;
this.implementationType = implementationType;
}
// Invoked at build time. We return the interface we want to implement.
protected override Type[] GetPublicInterfaces(Type targetType)
{
return new[] { this.interfaceType };
}
// Invoked at run time.
public override object CreateImplementationObject(AdviceArgs args)
{
return Activator.CreateInstance(this.implementationType);
}
}
[Serializable]
[ProvideAspectRole("INTERCEPTOR")]
[MulticastAttributeUsage(MulticastTargets.Method)]
[AspectRoleDependency(AspectDependencyAction.Order, AspectDependencyPosition.After, "COMPOSER")]
public sealed class InterceptAspectAttribute : MethodInterceptionAspect
{
public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
{
base.CompileTimeInitialize(method, aspectInfo);
// Warning in VS output
Message.Write(method, SeverityType.Warning, "XXX", "Method: " + method.Name);
}
public override void OnInvoke(MethodInterceptionArgs args)
{
Console.WriteLine("Intercepted before");
args.Proceed();
Console.WriteLine("Intercepted after");
}
}
interface ITest
{
void Call();
}
class TestImpl : ITest
{
public void Call()
{
Console.WriteLine("CALL remote implemented");
}
}
[InterceptAspect(AspectPriority = 1)]
[ComposeAspect(typeof(ITest), typeof(TestImpl), AspectPriority = 2)]
class Test
{
// this should, after compilation, have all methods of ITest, implemented through an instance of TestImpl, which get intercepted before TestImpl is called
public void CallLocalImplementedTest()
{
Console.WriteLine("CALL local implemented");
}
}
class Program
{
static void Main()
{
var test = new Test();
ITest t = Post.Cast<Test, ITest>(test);
Console.WriteLine("TEST #1");
t.Call();
Console.WriteLine("TEST #2");
test.CallLocalImplementedTest();
Console.ReadLine();
}
}
}
I have tried to influence the execution order of the two aspects by
AspectRoleDependency, making the interceptor depend on the composer to run first
AspectPriority, also making the composer run first.
As the tests always yield
TEST #1
CALL remote implemented
TEST #2
Intercepted before
CALL local implemented
Intercepted after
it obviously doesn't work. Do you have a clue why my execution order has not changed? Did I do something wrong, did I miss a detail in the documentation? What can I do to intercept my composition-injected methods as well?
With the current aspects and your current setup you cannot achive your desired result.
The problem is in how Postsharp work: it does the IL waving in one step and it only applies the InterceptAspect
to the methods which are present at original compile time so it does not see the new interface implementations added with the ComposeAspect
.
So no ordering of the accepts or providing roles, priorities or other configuration would help here.
One workaround would be to add the InterceptAspect
on the injected TestImpl
class:
[InterceptAspect]
class TestImpl : ITest
{
public void Call()
{
Console.WriteLine("CALL remote implemented");
}
}
In this case the logging logic will be added directly TestImpl
so these method will contain the logging when it will be composed into your Test
class.
Or if you don't mark every implementation you can put your aspect on the interface itself with:
[InterceptAspect(AttributeInheritance = MulticastInheritance.Multicast)]
interface ITest
{
void Call();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With