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

下面是具体的出错信息:

   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对创建出来的对象进行封装的时候,总是试图去完成各种注入工作而导致的

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

上面我们采用的对象类型为继承自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
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文链接

EnterLib PIAB又一个BUG?相关推荐

  1. 这是EnterLib PIAB的BUG吗?

    在默认的情况下,EnterLib的PIAB采用基于TransparentProxy/RealProxy的机制实现对方法调用的拦截,进而实现了对横切关注点(Crosscutting Concern)的动 ...

  2. 软件测试培训分享:做软件测试工作如何清楚的描述一个bug

    一名合格的软件测试工程师是需要清楚的交代自己的工作任务的,必须要清楚的告诉技术员出现的bug,那么做软件测试工作如何清楚的描述一个bug呢?来看看下面的详细介绍. 软件测试培训分享:做软件测试工作如何 ...

  3. 一个Bug能有多大影响:亏损30亿、致6人死亡、甚至差点毁灭世界...

    欢迎关注方志朋的博客,回复"666"获面试宝典 作者:博雯   来源:量子位(QbitAI) 一个Bug就地蒸发5亿美元: 软件设计层面出Bug致6人死亡: DeBug不成功直接世 ...

  4. 从一个Bug开始,重新认识一个强大的 Gson

    点击上方"方志朋",选择"置顶公众号" 技术文章第一时间送达! 作者:Mafly, 地址:www.cnblogs.com/mafly/p/gson.html 从 ...

  5. 关于MySQL count(distinct) 逻辑的另一个bug

    背景          上一篇博文(链接)介绍了count distinct的一个bug.解决完以后发现客户的SQL语句仍然返回错误结果(0), 再查原因,发现了另外一个bug.也就是说,这个SQL语 ...

  6. sdcms的一个bug,总是提示,该文件不允许被上传

    之前通过sdcms做过一个网站,最近在上传文件时,总是提示,该文件不允许被上传.于是跟踪了一下,发现了一个bug 上传附近是通过admin目录下的Sdcms_Editor_Up.asp来提交的,当文件 ...

  7. CSSFriendlyAdapter 的一个Bug

    因为要使用树控件的客户端脚本功能,发行asp.net 2.0中默认提供的树控件,并没有开放这方面的接口.要实现客户端的编程还是很有难度的. 查找了一些第三方的控件,不是需要收费,就是功能想对较弱. 后 ...

  8. QQ超市模拟排配2D版1.13 (XNA4.0) (修正双格货架移动的一个bug和3-5地图)

    抱歉,更新了一个地图-- 下载地址:(版本过期了,请下新版) 1.13:更新日期:2012-3-22 更新3店5口地图错误问题.启动程序前请手动删除旧版地图数据. 地址:C:\(我的文档路径)\Sav ...

  9. VS2002 与 IIS6.0的一个bug

    今天遇到一个问题,就是在win2003上用vs2002打开一个asp.net project.总是提示: The default Web access mode for this project is ...

最新文章

  1. GUI_DOWNLOAD加列名行
  2. Vue+Openlayers+el-radio实现切换地图显示
  3. JS跨域(ajax跨域、iframe跨域)解决方法及原理详解(jsonp)
  4. 于连生性聪颖的飞鸽传书
  5. 什么是HTML5前端开发?HTML5前端要学哪些技术?
  6. httpd-2.4.9.tar.bz2的编译安装配置以及CGI、虚拟主机、https、mod_deflate、mod_status的实现。...
  7. 查看mysql 主从日志_MySQL 主从
  8. ADNI数据集下载方法(完全步骤)
  9. 冶金、水泥、化工行业自动化通信产品介绍
  10. android 工具 Draw 9-patch 和去黑边
  11. 下载最新版本火狐浏览器,并且下载geckodriver.exe
  12. 20-HTML与HTML5常用标签(前端)
  13. cpua55和a53哪个好_哪种双核a73和四核a53智能电视更好?电视的64位CPU和双核
  14. 【DFS题型九/双向DFS】王子救公主
  15. 零售行业的六大主要EDI报文
  16. 【Android 10 源码】healthd 模块 BatteryService 初始化
  17. Redis键-值数据库 nosql 数据建模(4)------ 如何存储主从表数据 一对超级多关系
  18. bzoj2215: [Poi2011]Conspiracy
  19. 探讨select in 在postgresql的效率问题
  20. 关键路径、工期、总时差和自由时差精讲

热门文章

  1. 《理解 OpenStack + Ceph》---来自-[爱.知识]-推荐
  2. 6-5-树的双亲表示法-树和二叉树-第6章-《数据结构》课本源码-严蔚敏吴伟民版...
  3. LAMP兄弟连PHP课程学习笔记 第二天 PHP中使用变量
  4. 函数指针到文本反汇编
  5. iOS开发swift版异步加载网络图片(带缓存和缺省图片)
  6. 两道面试题,带你解析Java类加载机制
  7. 1分钟构建API网关日志解决方案
  8. ListView和数据适配器SimpleAdapter例子
  9. Apache的工作原理
  10. 区块链兼容以太坊智能合约