I have a class that implements two interfaces, and I want to apply interception to the class's methods.
I'm following the advice in Unity Register two interfaces as one singleton, but I'm surprised by the results. In a nutshell, it seems that my CallHandler is called twice. The shortest example I have is this:
public interface I1
{
void Method1();
}
public interface I2
{
void Method2();
}
public class C : I1, I2
{
[Log]
public void Method1() {}
public void Method2() {}
}
public class LogAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new LogCallHandler();
}
}
public class LogCallHandler : ICallHandler
{
public IMethodReturn Invoke(
IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("Entering " + input.MethodBase.Name);
var methodReturn = getNext().Invoke(input, getNext);
Console.WriteLine("Leaving " + input.MethodBase.Name);
return methodReturn;
}
public int Order { get; set; }
}
void Test()
{
IUnityContainer container = new UnityContainer();
container.AddNewExtension<Interception>();
container.RegisterType<C>(new ContainerControlledLifetimeManager());
container.RegisterType<I1, C>(
new Interceptor<TransparentProxyInterceptor>(),
new InterceptionBehavior<PolicyInjectionBehavior>());
container.RegisterType<I2, C>(
new Interceptor<TransparentProxyInterceptor>(),
new InterceptionBehavior<PolicyInjectionBehavior>());
container.Resolve<I1>().Method1();
}
Which gives this output:
Entering Method1
Entering Method1
Leaving Method1
Leaving Method1
Removing the "container.RegisterType I2, C" line makes the log appear only once. Adding a third interface, I3, which is similar to I2, causes the log to appear three times.
I would have expected the Log to be called only once. I can probably achieve this by having the LogCallHandler detect if it's being invoked from another LogCallHandler, but this seems inelegant.
Originally I wanted to apply the interception behavior to C rather than to I1 and I2 separately, but this requires C to inherit from MarshalByRefObject which is a constraint I'm not yet willing to impose.
Is there an alternative way?
The problem is that you are applying the transparent proxy to each interface. Instead, if you apply it to the concrete class you get only one proxy. Also, you don't need to make it a singleton unless you want the instance to be shared.
I ran this configuration in a test console project and got the desired result. Kudos for including a working snippet that isolated your problem!
var container = new UnityContainer()
.AddNewExtension<Interception>()
.RegisterType<I1, C>()
.RegisterType<I2, C>()
.RegisterType<C>(
new ContainerControlledLifetimeManager(),
new Interceptor<TransparentProxyInterceptor>(),
new InterceptionBehavior<PolicyInjectionBehavior>()
);