前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >EnterLib PIAB又一个BUG?

EnterLib PIAB又一个BUG?

作者头像
蒋金楠
发布2018-01-16 18:13:51
4480
发布2018-01-16 18:13:51
举报
文章被收录于专栏:大内老A大内老A

在《这是EnterLib PIAB的BUG吗?》一文中我们讨论了PIAB关于抽象基类的BUG,今天又发现了一个新的问题。问题的起因源于《IoC+AOP的简单实现》这篇文章,因为文中给出的解决方案仅仅支持构造器注入(Constructor Injection),而不能支持属性注入(Property Injection)和方法注入(Method Injection)。这是由于EnterLib的PIAB设计本身就存在缺陷。

对EnterLib 5.0有一定了解的人都应该知道,在新版本的EnterLib中,原来的ObjectBuild已经完全被Unity代替。PIAB本身也是通过UnityContainer的一个扩展实现的。我们今天介绍的这个问题(也许是BUG,也许不是?自己去分辨吧。不然我说是BUG,又有人不爽了)就是Unity本身的机制导致。

一、当引入属性注入(Property Injection)之后

我们现在用最少的代码来模拟这个问题。现在我们创建如下三个类型:Foo、IBar和Bar。Foo继承MarshalByRefObject,类型为IBar的属性上应用了DependencyAttribute,从而Foo对象在被UnityContainer进行创建的过程中,该属性能能够根据注册的类型匹配进行初始化,而Bar实现了接口IBar。

代码语言:js
复制
   1: public class Foo : MarshalByRefObject
   2: {
   3:     [Dependency]
   4:     public IBar Bar { get; set; }
   5: }
   6: public interface IBar{}
   7: public class Bar : IBar
   8: {}

现在,我们直接通过PolicyInjection的Create方法来创建Foo对象

代码语言:js
复制
   1: static void Main(string[] args)
   2: {
   3:     var foo = PolicyInjection.Create<Foo>();
   4: }

当Create方法被执行的时候,系统会抛出如下图所示的ResolutionFailedException异常

image
image

下面是具体的出错信息:

代码语言:js
复制
   1: Microsoft.Practices.Unity.ResolutionFailedException was unhandled
   2:   Message=Resolution of the dependency failed, type = "Bug4Piab.Foo", name = "(none)".
   3: Exception occurred while: while resolving.
   4: Exception is: InvalidOperationException - The current type, Bug4Piab.IBar, is an interface and cannot be constructed. Are you missing a type mapping?
   5: -----------------------------------------------
   6: At the time of the exception, the container was:
   7:  
   8:   Resolving Bug4Piab.Foo,(none)
   9:   Resolving value for property Foo.Bar
  10:     Resolving Bug4Piab.IBar,(none)
  11:  
  12:   Source=Microsoft.Practices.EnterpriseLibrary.Common
  13:   TypeRequested=Foo
  14:   StackTrace:
  15:        at Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity.TransientPolicyBuildUpExtension.DoBuildUp(Type t, Object existing, String name, InjectionMember[] injectionMembers) in e:\Builds\EntLib\Latest\Source\Blocks\Common\Src\Configuration\Unity\TransientPolicyBuildUpExtension.cs:line 68
  16:        at Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity.TransientPolicyBuildUpExtension.BuildUp(Type t, Object existing, String name, InjectionMember[] injectionMembers) in e:\Builds\EntLib\Latest\Source\Blocks\Common\Src\Configuration\Unity\TransientPolicyBuildUpExtension.cs:line 47
  17:        at Microsoft.Practices.EnterpriseLibrary.PolicyInjection.PolicyInjector.Wrap(Type typeToReturn, Object instance) in e:\Builds\EntLib\Latest\Source\Blocks\PolicyInjection\Src\PolicyInjection\PolicyInjector.cs:line 137
  18:        at Microsoft.Practices.EnterpriseLibrary.PolicyInjection.PolicyInjector.Create(Type typeToCreate, Type typeToReturn, Object[] args) in e:\Builds\EntLib\Latest\Source\Blocks\PolicyInjection\Src\PolicyInjection\PolicyInjector.cs:line 196
  19:        at Microsoft.Practices.EnterpriseLibrary.PolicyInjection.PolicyInjector.Create(Type typeToCreate, Object[] args) in e:\Builds\EntLib\Latest\Source\Blocks\PolicyInjection\Src\PolicyInjection\PolicyInjector.cs:line 180
  20:        at Microsoft.Practices.EnterpriseLibrary.PolicyInjection.PolicyInjector.Create[TObject](Object[] args) in e:\Builds\EntLib\Latest\Source\Blocks\PolicyInjection\Src\PolicyInjection\PolicyInjector.cs:line 154
  21:        at Microsoft.Practices.EnterpriseLibrary.PolicyInjection.PolicyInjection.Create[TObject](Object[] args) in e:\Builds\EntLib\Latest\Source\Blocks\PolicyInjection\Src\PolicyInjection\PolicyInjection.cs:line 37
  22:        at Bug4Piab.Program.Main(String[] args)
  23:        at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
  24:        at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
  25:        at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
  26:        at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
  27:        at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
  28:        at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
  29:        at System.Threading.ThreadHelper.ThreadStart()
  30:   InnerException: System.InvalidOperationException
  31:        Message=The current type, Bug4Piab.IBar, is an interface and cannot be constructed. Are you missing a type mapping?
  32:        Source=Microsoft.Practices.Unity
  33:        StackTrace:
  34:             at Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy.ThrowForAttemptingToConstructInterface(IBuilderContext context) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity\Src\ObjectBuilder\Strategies\BuildPlan\DynamicMethod\Creation\DynamicMethodConstructorStrategy.cs:line 209
  35:             at BuildUp_Bug4Piab.IBar(IBuilderContext )
  36:             at Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity\Src\ObjectBuilder\Strategies\BuildPlan\DynamicMethod\DynamicMethodBuildPlan.cs:line 37
  37:             at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity\Src\ObjectBuilder\Strategies\BuildPlan\BuildPlanStrategy.cs:line 43
  38:             at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity\Src\ObjectBuilder\Strategies\StrategyChain.cs:line 110
  39:             at Microsoft.Practices.ObjectBuilder2.BuilderContext.NewBuildUp(NamedTypeBuildKey newBuildKey) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity\Src\ObjectBuilder\BuilderContext.cs:line 220
  40:             at Microsoft.Practices.Unity.ObjectBuilder.NamedTypeDependencyResolverPolicy.Resolve(IBuilderContext context) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity\Src\ObjectBuilderCustomization\NamedTypeDependencyResolverPolicy.cs:line 51
  41:             at BuildUp_Bug4Piab.Foo(IBuilderContext )
  42:             at Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity\Src\ObjectBuilder\Strategies\BuildPlan\DynamicMethod\DynamicMethodBuildPlan.cs:line 37
  43:             at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity\Src\ObjectBuilder\Strategies\BuildPlan\BuildPlanStrategy.cs:line 43
  44:             at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity\Src\ObjectBuilder\Strategies\StrategyChain.cs:line 110
  45:             at Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity.TransientPolicyBuildUpExtension.DoBuildUp(Type t, Object existing, String name, InjectionMember[] injectionMembers) in e:\Builds\EntLib\Latest\Source\Blocks\Common\Src\Configuration\Unity\TransientPolicyBuildUpExtension.cs:line 64
  46:        InnerException: 

二、这算是个BUG吗?

从错误信息我们不难看出,具体的异常发生在对属性Bar的初始化上面。因为上该属性上应用了DependencyAttribute特性,而PIAB内部就是采用了Unity的机制,所以当调用PolicyInejection的Create方法的时候会试图初始化Bar属性。但是该属性是接口,并且PolicyInection本身不具有基于该接口的匹配信息,毫无疑问,对属性Bar的注入将会失败。

但是这到底算不算BUG呢?我觉得这得两说。如果撇开PIAB的实现机制来说,PIAB本身是作为AOP框架,它的目的就是让被创建出来的对象具有被拦截(Interception)的能力。当我们调用这个对象的某个方法的时候,应用在该方法的一些以CallHandler体现横切关注点能够得以执行。至于DI或者IoC方面的实现(比如本例的属性注入)不应该是你干的事儿,就不要瞎掺和了。从这方面考虑,应用在属性Bar上的DependencyAttribute特性应该被忽略的。

但是,从另一方面讲,各种注入本应该在对象创建的时候进行。你现在通过PolicyInjection去初始化一个对象,属性注入机制自动生效,也不是什么坏事。但是如果PIAB硬是要兼职做IoC所做的事,它本身应该提供类型匹配的注册机制,但是没有。

三、PolicyInjection.Wrap同样有问题

由于各种注入都是在相应的对象被创建的时候进行的,所以当我们调用PolicyInjection.Create创建对象的时候,这些注入被默认地被执行,我们尚可以理解。但是,当我们已经成功构建一个对象,调用PolicyInjection.Wrap对其进行封装,使之具有能够被拦截的能力,PIAB在做“公鸡生蛋”的事貌似就不太适合了——因为很有可能在对象最初被构建的时候,各种注入工作就已经完成

在下面的代码中,我们通过构建的UnityContainer创建对象Foo。由于Foo对象被创建之前,我们已经注册了IBar和Bar之间的匹配关系,Foo的属性Bar会正常地被初始化。

代码语言:js
复制
   1: static void Main(string[] args)
   2: {
   3:     var contianer = new UnityContainer();
   4:     contianer.RegisterType<IBar, Bar>();
   5:     var foo = contianer.Resolve<Foo>();
   6:     var proxy = PolicyInjection.Wrap<Foo>(foo);
   7: }

但是,当我们调用PolicyInjection.Wrap方法对已经创建出来的Foo对象进行封装的时候,一样的异常被抛出。换句话说,PolicyInjection.Wrap还试图去完成各种注入工作,这就不只是“公鸡下蛋、牡鸡司晨”的问题,这纯属添乱——去完成一件已经被完成的事情。实际上,PolicyInjection.Create方法实现的本质上讲,它先做对象的创建,然后做对象的封装。这个问题的本质还是PIAB对创建出来的对象进行封装的时候,总是试图去完成各种注入工作而导致的

image
image

四、采用接口可以解决这个问题

上面我们采用的对象类型为继承自MarshalByRefObject类型,但是经过我的实验,如果采用接口就不会出现这个问题。为此我们对上面的程序进行如下的改动,让Foo类实现一个新的接口IFoo,并基于该接口进行对象的封装。这样的话就不会出现上面的异常。

代码语言:js
复制
   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         var contianer = new UnityContainer();
   6:         contianer.RegisterType<IBar, Bar>();
   7:         contianer.RegisterType<IFoo, Foo>();
   8:         var foo = contianer.Resolve<IFoo>();
   9:         var proxy = PolicyInjection.Wrap<IFoo>(foo);
  10:     }
  11: }
  12: public interface IFoo { }
  13: public class Foo : IFoo
  14: {
  15:     [Dependency]
  16:     public IBar Bar { get; set; }
  17: }
  18: public interface IBar{}
  19: public class Bar : IBar
  20: {}

具体的原因,我还没有来得及深究。通过这一阵子对EnterLib以及Unity的深入应用,发现了很多问题。虽然我个人对EnterLib还算推崇——从很早的版本一直用过来,EnterLib从设计上确实在不断的完善。但是,每一个版本总是会出现很多细节方面的问题,很难让我满意——也许是我个人太较真了吧。

EnterLib PIAB又一个BUG?[续]——这是一个致命的BUG

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2010-09-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、当引入属性注入(Property Injection)之后
  • 二、这算是个BUG吗?
  • 三、PolicyInjection.Wrap同样有问题
  • 四、采用接口可以解决这个问题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档