背景:我们现在写的程序,用的数据库基本上都是SQLServer的。试想一下,如果我们以后还需要开发同样的程序,但是,所使用的数据库不同。那时,我们应该怎么做?是重新进行开发呢,还是在原有程序的基础上进行修改呢?
在个人版的VB.NET的机房收费系统中,针对上边提到的问题,我们应该如何去解决。下边,Answer出场,Question退下。
一、Answer
在这里,我们可以运用抽象工厂+反射+配置文件来解决上述问题。详情:《谈一谈:抽象工厂+反射+配置文件 实现数据库访问》
二、New Question
问题解决了,但是,随着问题的解决,随之而出现的是新的问题。
先来看一段程序:
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层。
三、反思
在使用抽象工厂+反射+配置文件时,我也遇到了上述的问题。当时为了解决问题,在网上查资料,咨询别人,最后也把问题给解决了。但是,在看到上边这篇关于如何解决问题的博客时,我陷入了深思。
我记得米老师曾经给我说过:不会,可以去问别人;但是问别人问题的同时,也必须要知道人家为什么会知道这个问题的答案,人家是从哪知道的。
我们在遇到问题的时候,都会去考虑怎么去解决问题,不管通过什么方法途径,基本上到最后,问题都能被解决。但是,有时候我们欠缺的是,深层次的思考。针对一个问题,可能解决了,就没事了,基本上不会去想:会不会有别的方法、途径,也能达到同样的效果?
在遇到问题时,应该抓住问题,进行深层次的发掘,多角度的思考。
学习,不仅要知其然,还要知其所以然。
转自:徐晨阳:应用反射+配置文件+抽象工厂时出现的错误和原因分析