配色: 字号:
EnterLib PIAB又一个BUG?——这是一个致命的BUG
2016-10-08 | 阅:  转:  |  分享 
  
EnterLibPIAB又一个BUG?[续]——这是一个致命的BUG

在《EnterLibPIAB又一个BUG?》这篇文章中我们谈到:当我们通过应用DependencyAttribute特性定义需要自动注入的属性的时候,当这个属性为接口、抽象类或者没有定义无参的构造函数,无论我们调用PolicyInjection的Create方法去创建一个新的对象,还是调用Wrap方法对现有对象进行封装,都会抛出一个ResolutionFailedException异常。之后根据园友韦恩卑鄙的评论,又进行了后续的验证。如果说在前文中,我们还对这是否是个BUG抱着“谨慎”的态度,那么在这篇文章中,可以肯定地告诉你:这是一个BUG,而且是一个“致命”的BUG。



一、前景回顾

我们在重新回顾一下在《EnterLibPIAB又一个BUG?》中描述的问题。如果我们定义如下几个类型,Foo继承于MarshalByRefObject,里面具有一个类型为IBar的Bar属性,上面应用了DependencyAttribute特性使之成为一个“注入属性”。



1:publicclassFoo:MarshalByRefObject

2:{

3:[Dependency]

4:publicIBarBar{get;set;}

5:}

6:publicinterfaceIBar{}

7:publicclassBar:IBar{}

当我们调用PolicyInjection.Create静态方法构建Foo对象的时候,会抛出如下图所示的ResolutionFailedException异常,错误信息表明缺乏对接口IBar的类型匹配所致。



image



二、如何“解决”这个问题?

要解决这个问题就得解决对接口IBar的类型注册问题,但是PolicyInjection没有什么提供任何的API共我们进行类型的注册。不过,我们可以看看PolicyInjection的Create或者Wrap具体的实现原理。



1:publicstaticclassPolicyInjection

2:{

3://Methods

4:publicstaticTInterfaceCreate(paramsobject[]args)whereTObject:TInterface

5:{

6:using(PolicyInjectorpolicyInjector=newPolicyInjector(EnterpriseLibraryContainer.Current))

7:{

8:returnpolicyInjector.Create(args);

9:}

10:}

11:

12:publicstaticTObjectCreate(paramsobject[]args)

13:{

14:using(PolicyInjectorpolicyInjector=newPolicyInjector(EnterpriseLibraryContainer.Current))

15:{

16:returnpolicyInjector.Create(args);

17:}

18:}

19:

20:publicstaticobjectCreate(TypetypeToCreate,paramsobject[]args)

21:{

22:using(PolicyInjectorpolicyInjector=newPolicyInjector(EnterpriseLibraryContainer.Current))

23:{

24:returnpolicyInjector.Create(typeToCreate,args);

25:}

26:}

27:

28:publicstaticobjectCreate(TypetypeToCreate,TypetypeToReturn,paramsobject[]args)

29:{

30:using(PolicyInjectorpolicyInjector=newPolicyInjector(EnterpriseLibraryContainer.Current))

31:{

32:returnpolicyInjector.Create(typeToCreate,typeToReturn,args);

33:}

34:}

35:

36:publicstaticTInterfaceWrap(objectinstance)

37:{

38:using(PolicyInjectorpolicyInjector=newPolicyInjector(EnterpriseLibraryContainer.Current))

39:{

40:returnpolicyInjector.Wrap(instance);

41:}

42:}

43:

44:publicstaticobjectWrap(TypetypeToReturn,objectinstance)

45:{

46:using(PolicyInjectorpolicyInjector=newPolicyInjector(EnterpriseLibraryContainer.Current))

47:{

48:returnpolicyInjector.Wrap(typeToReturn,instance);

49:}

50:}

51:}





我们可以清楚地看到:最终完成对象的创建和封装的是通过一个叫做PolicyInjector的对象完成的。从下面的代码片断我们可以看出,PolicyInjector自己也定义了一系列Create和Wrap方法。



1:publicclassPolicyInjector:IDisposable

2:{

3:privateIUnityContainercontainer;

4:

5:publicPolicyInjector(IConfigurationSourceconfigurationSource);

6:publicPolicyInjector(IServiceLocatorserviceLocator);

7:publicTInterfaceCreate(paramsobject[]args)whereTObject:TInterface;

8:publicTObjectCreate(paramsobject[]args);

9:publicobjectCreate(TypetypeToCreate,paramsobject[]args);

10:publicobjectCreate(TypetypeToCreate,TypetypeToReturn,paramsobject[]args);

11:publicvoidwww.baiyuewang.netDispose();

12:publicTInterfaceWrap(objectinstance);

13:publicobjectWrap(TypetypeToReturn,objectinstance);

14:}

我们说,PIAB完全Unity的机制进行对象的创建和封装,实际上体现在PolicyInjector通过一个UnityContainer对象来完成对象的创建和封装工作。如果我们直接通过PolicyInjector对象,而不是通过PolicyInjection这个外观类进行对象的封装,并且通过UnityContainer对象进行类型的注册,就可以避免上述异常的出现。具体实现如下:



1:staticvoidMain(string[]args)

2:{

3:using(PolicyInjectorinjector=newPolicyInjector(ConfigurationSourceFactory.Create()))

4:{

5:varcontainerField=typeof(PolicyInjector).GetField("container",BindingFlags.Instance|BindingFlags.NonPublic);

6:IUnityContainercontainer=(IUnityContainer)containerField.GetValue(injector);

7:container.RegisterType();

8:varfoo=injector.Create();

9:Console.WriteLine("foo.Bar.GetType().Name:{0}",foo.Bar.GetType().Name);

10:}

11:}

输出结果:



1:foo.Bar.GetType().Name:Bar

三、这真的是解决方案吗?

如果你足够仔细的话,在上面一节的标题中“解决”二字是加上引号的。我实际上在挖一个坑,诱使你往里跳:)。如果你真采用这个解决方案的话,一个“致命”的错误将会产生。



我们说过,PIAB的Create方法最终也是调用Wrap方法,而Wrap方法就会自作主张地去完成相应的注入工作——这本质上就是PIAB的BUG。我们说这个BUG足以致命,我可以通过一个简单的例子来说明这一点。假设我们具有如下的类型定义,Foo和IBar定义没有改变,现在我们定义两个具体的类Bar1和Bar2去实现IBar这个接口。



1:publicclassFoo:MarshalByRefObject

2:{

3:[Dependency]

4:publicIBarBar{get;set;}

5:}

6:publicinterfaceIBar{}

7:publicclassBar1:IBar{}

8:publicclassBar2:IBar{}

现在我们执行如下的代码:首先我们对IBar这个接口注册了匹配关系,让它直接映射到Bar1。然后我们创建一个Foo对象,并将Bar属性初始化成一个Bar2类型的对象,然后调用PolicyInjector的Wrap方法对Foo对象进行封装。通过输出结果,我们清楚地看到:Wrap方法的执行会按照我们注册的类型匹配关系重新设置了注入属性Bar的值,即类型为Bar1的对象。



1:staticvoidMain(string[]args)

2:{

3:using(PolicyInjectorinjector=newPolicyInjector(ConfigurationSourceFactory.Create()))

4:{

5:varcontainerField=typeof(PolicyInjector).GetField("container",BindingFlags.Instance|BindingFlags.www.wang027.comNonPublic);

6:IUnityContainercontainer=(IUnityContainer)containerField.GetValue(injector);

7:container.RegisterType();

8:varfoo=newFoo();

9:foo.Bar=newBar2();

10:Console.WriteLine("foo.Bar.GetType().Name:{0}",foo.Bar.GetType().Name);

11:foo=injector.Wrap(foo);

12:Console.WriteLine("foo.Bar.GetType().Name:{0}",foo.Bar.GetType().Name);

13:}

14:}

输出结果:



1:foo.Bar.GetType().Name:Bar2

2:foo.Bar.GetType().Name:Bar1

四、方法注入一样有问题

依赖诸如具有三种典型的表现形式:构造注入、属性(设置)注入和方法(调用)注入。既然PolicyInjection的Wrap方法会自作主张地完成属性注入的工作,对于方法(调用)注入也不能幸免。对于这个问题,我们直接调用PolicyInjection的Wrap方法就可以模拟。



重新定义类型Foo,让它具有一个Int类型的属性Count,该属性通过一个应用了InjectionMethodAttribute特性的方法Initialize被初始化成-1。



1:publicclassFoo:MarshalByRefObject

2:{

3:publicintCount{get;set;}

4:[InjectionMethod]

5:publicvoidInitialize()

6:{

7:Count=-1;

8:}

9:}

现在,我们执行如下的代码,我们发现Wrap方法的调用篡改了我们实现设置的值(100-〉-1)。



1:staticvoidMain(string[]args)

2:{

3:varfoo=newFoo();

4:foo.Count=100;

5:Console.WriteLine("BeforeWrapping:foo.Count={0}",foo.Count);

6:foo=PolicyInjection.Wrap(foo);

7:Console.WriteLine("AfterWrapping:foo.Count={0}",foo.Count);

8:}

输出结果:



1:BeforeWrapping:foo.Count=100

2:AfterWrapping:foo.Count=-1

献花(0)
+1
(本文系thedust79首藏)