Unity: Register two interfaces as one singleton with interception

怎甘沉沦 提交于 2019-12-04 06:25:41

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

As it turns out, a small modification of my original snippet 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 the call to RegisterType. The output of the above is:

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

Which gives me the following:

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

My use case here is unit-tests: my code relies on interception for some of its functionality (specifically, transaction management). C is a mock object that I provide to my tested class, and it implements two interfaces that the tested class needs. When the unit-test runs, I want to access the mock object to verify stuff on it.

The downside to the above solution is that I can only apply PolicyInjectionBehavior here (in fact, the code above doesn't specify any InterceptionBehavior: when using container.Configure<Interception>, the PolicyInjectionBehavior is automatically used by Unity, as far as I could tell. For my use case, it is acceptable.

I have to admit that I'm surprised that setting up interception during RegisterType gives different results than configuring it separately. If anyone can explain this, I'd be happy to understand why that is.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!