先来看一段程序:
<pre name="code" class="vb">Imports System.Configuration '配置文件命名空间 Imports System.Reflection '反射命名空间 Imports IDAL '引用接口层 Public Class DataAccess '程序集名称(同时代表命名空间名称):DAL Private Shared ReadOnly AssemblyName As String = System.Configuration.ConfigurationManager.AppSettings("assname") '数据库类型:Sql Private Shared ReadOnly db As String = System.Configuration.ConfigurationManager.AppSettings("DB") '创建D层类SqlUserInfoDAL的实例 Public Shared Function CreateUserInfo() As IDAL.IUserInfo Dim dalUserInfoName As String = AssemblyName & "." & db & "UserInfoDAL" '要实例化的D层类的名称 Return CType(Assembly.Load(AssemblyName).CreateInstance(dalUserInfoName), IUserInfo) End Function
对于学过反射+配置文件+抽象工厂实现数据访问的人来说,这小段程序还是很容易理解的。我将这段程序用在了机房收费系统个人重构版中。如图所示:
但是在运行过程中却报了错:
未能加载文件或程序集“DAL”或它的某一个依赖项。系统找不到指定文件。
通过查资料,知道了一种解决方案:将DAL层的DAL.dll文件复制到UI层相应的目录下,如图所示。
之后,程序正常运行,并通过CreateUserInfo()创建出了SqlUserInfoDAL的实例。
有了成功的经验,我便做了如下操作:
- 在DAL层,添加了一个新的类:SqlWorkLogDAL
- 在DataAccess中,添加了一个新的方法:CreateWorkLog()
如下所示:
'创建D层类SqlWorkLogDAL的实例 Public Shared Function CreateWorkLog() As IDAL.IWorkLog Dim dalWorkLogName As String = AssemblyName & "." & db & "WorkLogDAL" '要实例化的D层类的名称 Return CType(Assembly.Load(AssemblyName).CreateInstance(dalWorkLogName), IWorkLog) End Function
但是运行后,还是报了错,不过这次错误不一样:
未将对象引用设置到对象的实例。
通过查资料,也找到了答案:修改DAL层--属性--编译--生成输出路径,将生成输出路径改到UI层\bin\Debug文件夹下。
虽然两种解决方法不一样,但是本质都是把DAL层的DAL.dll文件放到UI层。
那么,为什么第一种解决方法只有“短暂疗效”,而第二种方法可以“根治”呢?
这时,我做了个尝试,我不去修改路径,而是再次把DAL层中的DAL.dll文件复制并替换我刚才复制到UI层的文件。
结果成功了。
这就说明了:两次复制的文件并非同一个文件,虽然它们都叫DAL.dll,但是在文件内部,它们一定发生了变化。
这里我们就要说说,dll文件是怎么生成的了。
每个程序集下面都有两个文件夹bin和obj,如图:
bin目录用来保存项目生成后程序集,obj目录用来保存编译结果。编译是分模块进行的,编译整个完成后会合并为一个.dll或.exe文件保存到bin目录下。而且采用增量编译的方式。
所以,当我添加了一个新的类SqlWorkLogDAl后,生成的DAL.dll已经不是原来的DAL.dll了。
复制的方式的缺陷在于:只要DAL层发生变化,就得通过手动复制来更新U层的DAL.dll文件。但是修改路径,就不用我们管了,它会自动将最新的DAL.dll文件生成到UI层。
那么为什么要把D层的.dll文件在U层生成呢?
这就要知道Assembly.Load()是怎么定位程序集了?
当你只给定程序集名称时,如“DAL”,那么CLR只会在应用程序的目录下查找,而咱们的应用程序在UI层,所以要在U层有DAL.dll。
问题又来了:
在U层,有Entity.dll,有BLL.dll,有DataAccess.dll……为什么就是没有DAL.dll,为什么只有它需要修改生成路径?
这里就要看看程序的包图了:
---->代表依赖,也表示引用,那么从UI层看,UI层可以直接或间接地引用Facade层,BLL层,DataAccess层,IDAL层,但是没有引用DAL层,所以别的层的.dll文件会出现在U层,而DAL层的.dll文件没有出现在U层。
文章中,如果有理解得不对的地方,还请大家批评指正。
应用反射+配置文件+抽象工厂时出现的错误和原因分析