Unity: register two interfaces as one singleton with interception

I have a class that implements two interfaces, and I want to apply interception to class methods.

I follow the tips in Unity Register two interfaces as one singleton , but I am surprised by the results. In a nutshell, it seems that my CallHandler is being called twice. The shortest example:

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

What gives this conclusion:

 Entering Method1 Entering Method1 Leaving Method1 Leaving Method1 

Removing the string "container.RegisterType I2, C" makes the log appear only once. Adding a third interface, I3, which is similar to I2, makes the log appear three times.

I would expect Log to be called only once. Perhaps I can achieve this by discovering that the LogCallHandler detects whether it is called from another LogCallHandler, but that seems inelegant.

Initially, I wanted to apply the interception behavior to C, and not to I1 and I2 separately, but this requires C to inherit from MarshalByRefObject, which is a constraint that I do not want to impose.

Is there an alternative way?

+6
c # unity-container unity-interception
source share
2 answers

The problem is that you are applying a transparent proxy to each interface. Instead, if you apply it to a specific class, you will only get one proxy. In addition, you do not need to do this with 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 to include 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>() ); 
+5
source share

As it turned out, a small modification of my source fragment gives a solution:

 public interface I1 { void Method1(); } public interface I2 { void Method2(); } public class C : I1, I2 { public int Data = 0; [Log] public void Method1() { Console.WriteLine("Method1 " + Data); Data = 1; } [Log] public void Method2() { Console.WriteLine("Method2 " + Data); Data = 2; } } 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>(); container.RegisterType<I2, C>(); container.Configure<Interception>().SetInterceptorFor<I1>(new TransparentProxyInterceptor()); container.Configure<Interception>().SetInterceptorFor<I2>(new TransparentProxyInterceptor()); container.Resolve<I1>().Method1(); container.Resolve<I2>().Method2(); container.Resolve<C>().Method2(); } 

The only difference is that I set the interception for I1 and I2 outside of the RegisterType call. The output is higher:

 Entering Method1 Method1 0 Leaving Method1 Entering Method2 Method2 1 Leaving Method2 Method2 2 

Which gives me the following:

  • I intercept the C implementation of both I1 and I2.
  • There is only one instance of C (as evidenced by the modification of the Data element).
  • I can achieve a single instance of C (i.e. container.Resolve<C>() works, albeit without interception).

My use case is unit tests: my code uses interception for some of its functions (in particular, for transaction management). C is a mock object that I provide to the test class, and it implements two interfaces that need the test class. When a unit test is running, I want to access the layout of the object to test it on it.

The disadvantage of the above solution is that I can apply a PolicyInjectionBehavior here (in fact, in the above code no InterceptionBehavior is indicated: when using container.Configure<Interception> PolicyInjectionBehavior, Unity is automatically used, as far as I could tell. For my use case, this is acceptable.

I have to admit that I am surprised that setting interception during RegisterType gives different results than setting it separately. If anyone can explain this, I will be happy to understand why this is so.

+4
source share

All Articles