DataEntrance.InitializeDBAccesserFactory(dbInfoMgr,new XDBAccesserFactory()) ;
后面在StudentForm中,我们又是这样来获取IDBAccesser引用:
实际上,如果没有DataEntrance,我们仍可以正常的使用生成的数据层代码,只不过繁琐些。比如我们可以直接获取IDBAccesser引用:
(1)代码中硬编码了数据库类型。上面代码指定了使用针对SqlServer数据库的访问者,当数据库类型发生变化时,所有类似的地方都要手动修改。
(2)即使我只是要提取某个指定对象的指定字段的值,也需要经历像下面这样繁琐的过程:
string whereStr = string.Format("where {0} = '1001'" ,Student._ID) ;
Student st = (Student)dealStudent.GetAObject(whereStr) ;
int age = st.Age ;
也许在得到编号为1001学生的年龄之后,就再也用不着dealStudent了,但是也不得不如此。在现实的应用中,我们会碰到很多类似的情况,如果每次都这样,未免也激怒大部分使用者。但是如果能这样,就会令使用者开心了:
从上面的叙述,我们已经可以看到,DataEntrance的主要目标有两个:
(1)将使用数据访问者的代码与创建数据访问者的代码隔离开来,这有点像抽象工厂模式的目的。如果在应用的过程中,需要更换数据库类型,只需要修改相关的配置文件中的指定数据库类型的字段即可,而程序不需重编译。
(2)最大程度的简化数据层的使用。上面的例子已经说明了DataEntrance的威力。
下面我们就来看个明白,如何使用DataEntrance以及DataEntrance是怎么工作的?
首先,DataEntrance是一个静态类,这样我们在使用它的时候,可以直接通过类名引用其方法,而不需管理DataEntrance的实例。
其次,DataEntrance需要在系统启动的时候初始化。
虽然,DataEntrance是一个静态类,但是其需要被初始化,初始化动作的目的就是设定数据库的相关信息,比如连接字符串、数据库类型等。在这里,我们需要考虑,如果在程序运行的过程中,用户更改了数据库的配置信息,那么我们的DataEntrance也应该能随其改变以适应最新的情况。为了使DataEntrance能接收这样的通知,我设计了IDataBaseInfoMgr接口,该接口发布了DbConfigChanged事件。其定义如下:
/// IDataBaseInfoMgr 用于向DataEntrance提供数据库连接信息
/// </summary>
public interface IDataBaseInfoMgr
{
DataBaseType GetDbType() ;
string GetConnString() ;
string GetConnString(string dbName) ; //针对多数据库
void ActivateDbConnChangeeEvent() ; //外部通知IDataBaseInfoMgr数据库连接信息发生了变化
event EventHandler DbConfigChanged ;
bool IsMultiDataBase{get ;}
}
我把该接口的引用作为DataEntrance初始化的参数传进去,这样DataEntrance就能在数据库配置信息改变时,做相应的调整了。看看DataEntrance的初始化方法的声明:
第一个参数就是IDataBaseInfoMgr引用,我们可以自己实现IDataBaseInfoMgr接口,其实,在使用XCodeFactory生成数据层代码时,已经给我们自动生成了一个默认的DataBaseInfoMgr.cs文件,我们可以直接使用其中定义的DataBaseInfoMgr类,但是我建议使用者最好是修改一下GetConnString方法和GetDbType方法的实现--从配置文件获取连接信息和数据库类型信息,而不是将它们写死在方法实现中。
第二个参数是一个IDBAccesserFactory引用,IDBAccesserFactory接口有什么用了?为了说明它,我们可以先看看下面的代码:
对DataEntrance.GetFieldValue方法, 我们传入的第一个参数是我们的数据对象类型,也就是说我们需要根据数据对象的类型来得到对应的访问类实例,这需要通过反射来完成,IDBAccesserFactory就是做这件事的,并且IDBAccesserFactory还会缓存创建过的数据访问者实例。由于反射无法穿越自定义的程序集,所以我将XDBAccesserFactory.cs放在了本地文件夹EnterpriseServerBase.DataAccess中。IDBAccesserFactory接口的定义如下:
{
void Initialize(DataBaseType dbType ,string connStr ,bool cachAccesser) ;
IDBAccesser CreateDBAccesser(Type dataClassType) ;
IDBAccesser CreateDBAccesser(DataBaseType dbType , string connStr , Type dataClassType);
IDBAccesser CreateDBAccesser(Type dataClassType ,string dealerNamespace) ;
}
那么,CreateDBAccesser方法是如何根据数据对象类的名字来创建对应的访问者实例的了?你肯定还记得XCF约定:数据访问类的名字的格式是:“数据库表名 + 数据库类型 + Dealer”,并且数据对象类和访问者处于同一命名空间中,所以一旦IDBAccesserFactory知道了数据对象类的类型,自然就可以根据数据库类型推倒出访问者类型,然后通过反射就可以创建访问者实例了。
对于DataEntrance的初始化,通常我们只要这样就足够了:
DataEntrance.InitializeDBAccesserFactory(dbInfoMgr,new XDBAccesserFactory()) ;
再次,IDBAccesser 接口中的方法基本上都可以在DataEntrance中找到对应的静态方法。只不过这个静态方法比IDBAccesser 的对应方法多了一个参数,这个参数就是数据对象类的类型,正如上面提到的,这个类型用于IDBAccesserFactory动态的产生访问者实例。所以,以前我们需要先new一个访问者出来,然后再进行数据访问操作,现在不用这么麻烦了。对数据层的操作,我们完全可以通过DataEntrance进行。
下面的例子是根据学生的ID找到其导师的名字:
IDBAccesser studentDealer = dbAccesserFactory. CreateDBAccesser (typeof(student)) ;
Student stu = (Student)studentDealer.GetAObject("Where ID = '001'") ;
IDBAccesser mentorDealer = dbAccesserFactory. CreateDBAccesser (typeof(Mentor)) ;
Mentor mentor = (Mentor)mentorDealer.GetAObject(string.Format("Where ID = '{0}'" ,stu.MentorID)) ;
string theName = mentor.Name ;
//使用DataEntrance可以这样做:
string mentorID = DataEntrance.GetFieldValue(typeof(Student) ,"001" ,"MentorID").ToString() ;
string theName = DataEntrance.GetFieldValue(typeof(Mentor) ,mentorID ,"Name").ToString() ;
使用DataEntrance的优势很明显了,而且我们的代码完全不依赖于数据库的类型。使用何种数据库类型只需要在一个地方指定,那就是IDBAccesserFactory的Initialize方法的参数中,而这个参数的值最终来源于配置文件。
最后,我们需要进一步挖掘DataEntrance,DataEntrance正如其名,是我们访问数据层的入口。我想使我们与数据层的所有交互都通过DataEntrance进行,而且我们也不想再关心IDBAccesserFactory了。所以我决定将一切都封装到DataEntrance的静态方法中(比如IDBAccesserFactory的初始化),这样能最大限度的方便我们的使用。通过VS.NET的智能感知,你可以看到DataEntrance的所有公有静态方法,这些方法的作用一目了然,基本与IDBAccesser接口的方法一一对应。
到现在为止,我们已经完全可以通过DataEntrance来完成一切的数据层的访问操作了。如果你只是想使用XcodeFactory生成的数据层代码,而不想关心其它的东西,那么了解一下DataEntrance就足够了,如果你想知道这些代码内部是如何工作的,还有哪些更高级的使用方法等高级内容,请继续关注我后面的文章。
DataEntrance源代码
XCodeFactory3.0完全攻略 目录