Unity:将两个接口注册为一个带有拦截的单例

我有一个实现两个接口的类,我想将拦截应用于类的方法。

我遵循Unity中的建议将两个接口作为一个单独的接口 ,但我对结果感到惊讶。 简而言之,似乎我的CallHandler被调用了两次。 我有的最简单的例子是:

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(); container.RegisterType(new ContainerControlledLifetimeManager()); container.RegisterType( new Interceptor(), new InterceptionBehavior()); container.RegisterType( new Interceptor(), new InterceptionBehavior()); container.Resolve().Method1(); } 

这给出了这个输出:

 Entering Method1 Entering Method1 Leaving Method1 Leaving Method1 

删除“container.RegisterType I2,C”行使日志只出现一次。 添加第三个接口I3(类似于I2)会导致日志出现三次。

我本来希望只调用一次Log。 我可以通过让LogCallHandler检测它是否从另一个LogCallHandler调用来实现这一点,但这看起来不太优雅。

最初我想将拦截行为应用于C而不是分别应用于I1和I2,但这需要CinheritanceMarshalByRefObject,这是我不愿意强加的约束。

还有另一种方法吗?

问题是您正在将透明代理应用于每个接口。 相反,如果将它应用于具体类,则只能获得一个代理。 此外,除非您希望共享实例,否则不需要将其设为单例。

我在测试控制台项目中运行此配置并获得了所需的结果。 感谢您提供隔离问题的工作片段!

 var container = new UnityContainer() .AddNewExtension() .RegisterType() .RegisterType() .RegisterType( new ContainerControlledLifetimeManager(), new Interceptor(), new InterceptionBehavior() ); 

事实certificate,对我原始代码段的一个小修改提供了一个解决方案:

 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(); container.RegisterType(new ContainerControlledLifetimeManager()); container.RegisterType(); container.RegisterType(); container.Configure().SetInterceptorFor(new TransparentProxyInterceptor()); container.Configure().SetInterceptorFor(new TransparentProxyInterceptor()); container.Resolve().Method1(); container.Resolve().Method2(); container.Resolve().Method2(); } 

唯一的区别是我在调用RegisterType之外设置了I1和I2的拦截。 以上的输出是:

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

这给了我以下内容:

  • 我拦截了C的I1和I2的实现。
  • 只有一个C实例(由Data成员的修改certificate)。
  • 我可以到达C的单个实例(即container.Resolve()工作,虽然没有拦截)。

我的用例是unit testing:我的代码依赖于拦截它的一些function(特别是事务管理)。 C是我提供给测试类的模拟对象,它实现了测试类所需的两个接口。 当unit testing运行时,我想访问模拟对象以validation其上的内容。

上述解决方案的缺点是我只能在这里应用PolicyInjectionBehavior(事实上,上面的代码没有指定任何InterceptionBehavior:当使用container.Configure ,Unity会自动使用PolicyInjectionBehavior,尽我所能告诉我。对于我的用例,这是可以接受的。

我不得不承认,我很惊讶在RegisterType期间设置拦截会产生与单独配置不同的结果。 如果有人能够解释这一点,我很乐意理解为什么会这样。