EnterLib PIAB又一个BUG?

简介:

在《这是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。

   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对象

   1: static void Main(string[] args)
   2: {
   3:     var foo = PolicyInjection.Create<Foo>();
   4: }

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

image

下面是具体的出错信息:

   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会正常地被初始化。

   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

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

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

   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


作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
13天前
|
存储 弹性计算 人工智能
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
2025年9月24日,阿里云弹性计算团队多位产品、技术专家及服务器团队技术专家共同在【2025云栖大会】现场带来了《通用计算产品发布与行业实践》的专场论坛,本论坛聚焦弹性计算多款通用算力产品发布。同时,ECS云服务器安全能力、资源售卖模式、计算AI助手等用户体验关键环节也宣布升级,让用云更简单、更智能。海尔三翼鸟云服务负责人刘建锋先生作为特邀嘉宾,莅临现场分享了关于阿里云ECS g9i推动AIoT平台的场景落地实践。
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
|
5天前
|
云安全 人工智能 安全
Dify平台集成阿里云AI安全护栏,构建AI Runtime安全防线
阿里云 AI 安全护栏加入Dify平台,打造可信赖的 AI
|
12天前
|
人工智能 自然语言处理 自动驾驶
关于举办首届全国大学生“启真问智”人工智能模型&智能体大赛决赛的通知
关于举办首届全国大学生“启真问智”人工智能模型&智能体大赛决赛的通知
|
8天前
|
人工智能 运维 Java
Spring AI Alibaba Admin 开源!以数据为中心的 Agent 开发平台
Spring AI Alibaba Admin 正式发布!一站式实现 Prompt 管理、动态热更新、评测集构建、自动化评估与全链路可观测,助力企业高效构建可信赖的 AI Agent 应用。开源共建,现已上线!
772 23
|
7天前
|
人工智能 Java Nacos
基于 Spring AI Alibaba + Nacos 的分布式 Multi-Agent 构建指南
本文将针对 Spring AI Alibaba + Nacos 的分布式多智能体构建方案展开介绍,同时结合 Demo 说明快速开发方法与实际效果。
495 37
|
7天前
|
机器学习/深度学习 人工智能 搜索推荐
万字长文深度解析最新Deep Research技术:前沿架构、核心技术与未来展望
近期发生了什么自 2025 年 2 月 OpenAI 正式发布Deep Research以来,深度研究/深度搜索(Deep Research / Deep Search)正在成为信息检索与知识工作的全新范式:系统以多步推理驱动大规模联网检索、跨源证据。
478 41
|
1天前
|
文字识别 监控 物联网
这是我写的实施一地两检的跨境高铁站旅客资料预报系统的系统架构
本文设计了一套基于IAPIS理念的高铁跨境旅客预报与边检联动系统,覆盖青青草原内地与喜羊羊特别行政区间“一地两检”场景。系统在旅客购票后即采集证件、生物特征及行程信息,通过Advance Passenger Info Checker等模块,向出发地和目的地移民管理机构实时推送数据,实现出入境许可预审。支持线上/线下购票、检票、退票全流程管控,结合面部识别、行为追踪技术监控旅客状态,防止滞留或非法通行。列车发车前进行最终核验,确保所有跨境旅客获边检许可。若旅行被中途取消,系统自动改签、退票并通知各方,保障安全与效率。(239字)