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异常
下面是具体的出错信息:
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?相关推荐
- 这是EnterLib PIAB的BUG吗?
在默认的情况下,EnterLib的PIAB采用基于TransparentProxy/RealProxy的机制实现对方法调用的拦截,进而实现了对横切关注点(Crosscutting Concern)的动 ...
- 软件测试培训分享:做软件测试工作如何清楚的描述一个bug
一名合格的软件测试工程师是需要清楚的交代自己的工作任务的,必须要清楚的告诉技术员出现的bug,那么做软件测试工作如何清楚的描述一个bug呢?来看看下面的详细介绍. 软件测试培训分享:做软件测试工作如何 ...
- 一个Bug能有多大影响:亏损30亿、致6人死亡、甚至差点毁灭世界...
欢迎关注方志朋的博客,回复"666"获面试宝典 作者:博雯 来源:量子位(QbitAI) 一个Bug就地蒸发5亿美元: 软件设计层面出Bug致6人死亡: DeBug不成功直接世 ...
- 从一个Bug开始,重新认识一个强大的 Gson
点击上方"方志朋",选择"置顶公众号" 技术文章第一时间送达! 作者:Mafly, 地址:www.cnblogs.com/mafly/p/gson.html 从 ...
- 关于MySQL count(distinct) 逻辑的另一个bug
背景 上一篇博文(链接)介绍了count distinct的一个bug.解决完以后发现客户的SQL语句仍然返回错误结果(0), 再查原因,发现了另外一个bug.也就是说,这个SQL语 ...
- sdcms的一个bug,总是提示,该文件不允许被上传
之前通过sdcms做过一个网站,最近在上传文件时,总是提示,该文件不允许被上传.于是跟踪了一下,发现了一个bug 上传附近是通过admin目录下的Sdcms_Editor_Up.asp来提交的,当文件 ...
- CSSFriendlyAdapter 的一个Bug
因为要使用树控件的客户端脚本功能,发行asp.net 2.0中默认提供的树控件,并没有开放这方面的接口.要实现客户端的编程还是很有难度的. 查找了一些第三方的控件,不是需要收费,就是功能想对较弱. 后 ...
- QQ超市模拟排配2D版1.13 (XNA4.0) (修正双格货架移动的一个bug和3-5地图)
抱歉,更新了一个地图-- 下载地址:(版本过期了,请下新版) 1.13:更新日期:2012-3-22 更新3店5口地图错误问题.启动程序前请手动删除旧版地图数据. 地址:C:\(我的文档路径)\Sav ...
- VS2002 与 IIS6.0的一个bug
今天遇到一个问题,就是在win2003上用vs2002打开一个asp.net project.总是提示: The default Web access mode for this project is ...
最新文章
- GUI_DOWNLOAD加列名行
- Vue+Openlayers+el-radio实现切换地图显示
- JS跨域(ajax跨域、iframe跨域)解决方法及原理详解(jsonp)
- 于连生性聪颖的飞鸽传书
- 什么是HTML5前端开发?HTML5前端要学哪些技术?
- httpd-2.4.9.tar.bz2的编译安装配置以及CGI、虚拟主机、https、mod_deflate、mod_status的实现。...
- 查看mysql 主从日志_MySQL 主从
- ADNI数据集下载方法(完全步骤)
- 冶金、水泥、化工行业自动化通信产品介绍
- android 工具 Draw 9-patch 和去黑边
- 下载最新版本火狐浏览器,并且下载geckodriver.exe
- 20-HTML与HTML5常用标签(前端)
- cpua55和a53哪个好_哪种双核a73和四核a53智能电视更好?电视的64位CPU和双核
- 【DFS题型九/双向DFS】王子救公主
- 零售行业的六大主要EDI报文
- 【Android 10 源码】healthd 模块 BatteryService 初始化
- Redis键-值数据库 nosql 数据建模(4)------ 如何存储主从表数据 一对超级多关系
- bzoj2215: [Poi2011]Conspiracy
- 探讨select in 在postgresql的效率问题
- 关键路径、工期、总时差和自由时差精讲