继续 下面可都是代码了 仔细看哦!
#001 using System;
#002 using System.Data;
#003
#004 using TraceLWord4.AccessTask; // 引用数据访问层
#005 using TraceLWord4.Classes; // 引用实体规范层
#006
#007 namespace TraceLWord4.InterService
#008 {
#009 /// <summary>
#010 /// LWordService 留言板服务类
#011 /// </summary>
#012 public class LWordService
#013 {
#014 /// <summary>
#015 /// 读取 LWord 数据表,返回留言对象数组
#016 /// </summary>
#017 /// <returns></returns>
#018 public LWord[] ListLWord()
#019 {
#020 return (new LWordTask()).ListLWord();
#021 }
#022
#023 /// <summary>
#024 /// 发送留言信息到数据库
#025 /// </summary>
#026 /// <param name="newLWord">留言对象</param>
#027 public void PostLWord(LWord newLWord)
#028 {
#029 (new LWordTask()).PostLWord(newLWord);
#030 }
#031 }
#032 }
从行#018中可以看出,无论如何,ListLWord函数都要返回一个LWord数组!这个数组可能为空值,但是一旦数组的长度不为零,那么其中的元素必定是一个LWord类对象!而一个LWord类对象,就一定有TextContent和PostTime这两个属性!这个要比DataSet类对象作为参数的形式明确得多……同样的,LWordTask也要做出反应:
#001 using System;
#002 using System.Collections;
#003 using System.Data;
#004 using System.Data.OleDb;
#005 using System.Web;
#006
#007 using TraceLWord4.Classes; // 引用实体规范层
#008
#009 namespace TraceLWord4.AccessTask
#010 {
#011 /// <summary>
#012 /// LWordTask 留言板任务类
#013 /// </summary>
#014 public class LWordTask
#015 {
#016 // 数据库连接字符串
#017 private const string DB_CONN=@"PROVIDER=Microsoft.Jet.OLEDB.4.0;
DATA Source=C:\DbFs\TraceLWordDb.mdb";
#018
#019 /// <summary>
#020 /// 读取 LWord 数据表,返回留言对象数组
#021 /// </summary>
#022 /// <returns></returns>
#023 public LWord[] ListLWord()
#024 {
#025 // 留言对象集合
#026 ArrayList lwordList=new ArrayList();
#027
#028 string cmdText="SELECT * FROM [LWord] ORDER BY [LWordID] DESC";
#029
#030 OleDbConnection dbConn=new OleDbConnection(DB_CONN);
#031 OleDbCommand dbCmd=new OleDbCommand(cmdText, dbConn);
#032
#033 try
#034 {
#035 dbConn.Open();
#036 OleDbDataReader dr=dbCmd.ExecuteReader();
#037
#038 while(dr.Read())
#039 {
#040 LWord lword=new LWord();
#041
#042 // 设置留言编号
#043 lword.UniqueID=(int)dr["LWordID"];
#044 // 留言内容
#045 lword.TextContent=(string)dr["TextContent"];
#046 // 发送时间
#047 lword.PostTime=(DateTime)dr["PostTime"];
#048
#049 // 加入留言对象到集合
#050 lwordList.Add(lword);
#051 }
#052 }
#053 catch
#054 {
#055 throw;
#056 }
#057 finally
#058 {
#059 dbConn.Close();
#060 }
#061
#062 // 将集合转型为数组并返回给调用者
#063 return (LWord[])lwordList.ToArray(typeof(TraceLWord4.Classes.LWord));
#064 }
#065
#066 /// <summary>
#067 /// 发送留言信息到数据库
#068 /// </summary>
#069 /// <param name="newLWord">留言对象</param>
#070 public void PostLWord(LWord newLWord)
#071 {
#072 // 留言内容不能为空
#073 if(newLWord==null || newLWord.TextContent==null || newLWord.TextContent=="")
#074 throw new Exception("留言内容为空");
#075
#076 string cmdText="INSERT INTO [LWord]([TextContent]) VALUES(@TextContent)";
#077
#078 OleDbConnection dbConn=new OleDbConnection(DB_CONN);
#079 OleDbCommand dbCmd=new OleDbCommand(cmdText, dbConn);
#080
#081 // 设置留言内容
#082 dbCmd.Parameters.Add(new OleDbParameter("@TextContent",
OleDbType.LongVarWChar));
#083 dbCmd.Parameters["@TextContent"].Value=newLWord.TextContent;
#084
#085 try
#086 {
#087 dbConn.Open();
#088 dbCmd.ExecuteNonQuery();
#089 }
#090 catch
#091 {
#092 throw;
#093 }
#094 finally
#095 {
#096 dbConn.Close();
#097 }
#098 }
#099 }
#100 }
这样,即便是将LWordTask.cs文件中的ListLWords方法修改成访问[RegUser]数据表的代码,也依然不会影响到外观层。因为函数只返回一个LWord类型的数组。再有,因为位于外观层的重复器控件绑定的是LWord类对象,而LWord类中就必有对TextContent字段的定义。这样也就达到了规范数据访问层返回结果的目的。这便是为什么在Duwamish7中会出现Common项目的原因。不知道你现在看明白了么?而Bincess.CN的做法和PetShop3.0一样,是通过自定义类来达到实体规范层的目的!PetShop3.0是通过Modal项目,而Bincess.CN则是通过Classes项目。
餐馆又来了一位新大厨师傅——谈谈跨越数据库平台的问题
餐馆面积不大,但生意很火。每天吃饭的人都特别多。为了加快上菜的速度,所以餐馆又找来了一位新的大厨师傅。假如,TraceLWord4为了满足一部分用户对性能的较高需要,要其数据库能使用MS SQL Server 2000。那么我们该怎么办呢?数据库要从Access 2000升迁到MS SqlServer 2000,那么只要集中修改AccessTask项目中的程序文件就可以了。但是,我又不想让这样经典的留言板失去对Access 2000数据库的支持。所以,正确的做法就是把原来所有的程序完整的拷贝一份放到另外的一个目录里。然后集中修改AccessTask项目,使之可以支持MS SQL Server 2000。这样这个留言板就有了两个版本,一个是Access 2000版本,另外一个就是MS SQL Server 2000版本……新的大厨师傅过来帮忙了,我们有必要让原来表现极佳的大厨师傅下课吗?可这样,新大厨师傅不是等于没来一样?新的大厨师傅过来帮忙了,我们有必要为新来的大厨师傅重新配备一套餐馆服务生系统、菜单系统吗?当然也没必要!那么,可不可以让TraceLWord4同时支持Access 2000又支持MS SQL Server 2000呢?也就是说,不用完整拷贝原来的程序,而是在解决方案里加入一个新的项目,这个项目存放的是可以访问MS SQL Server 2000数据库的代码。然后,我们再通过一个“开关”来进行控制,当开关指向Access 2000一端时,TraceLWord4就可以运行在Access 2000数据库平台上,而如果开关指向MS SQL Server 2000那一端时,TraceLWord4就运行在MS SQL Server 2000数据库平台上……
在TraceLWord5中,加入了一个新项目SqlServerTask,这个项目的代码是访问的MS SQL Server 2000数据库。还有一个新建的项目DALFactory,这个项目就是一个“开关”。这个“开关”项目中仅有一个DbTaskDriver.cs程序文件,就是用它来控制TraceLWord5到底运行载那个数据库平台上?
关于TraceLWord5,更完整的代码,可以在CodePackage/TraceLWord5目录中找到——
DALFactory项目,其实就是“数据访问层工厂”,而DbTaskDriver类就是一个工厂类。也就是说DALFactory项目是“工厂模式”的一种应用。关于“工厂模式”,顾名思义,工厂是制造产品的地方,而“工厂模式”,就是通过“工厂类”来制造对象实例。“工厂类”可以通过给定的条件,动态地制造不同的对象实例。就好像下面这个样子:
// 水果基类public class Fruit;// 苹果是一种水果public class Apple : Fruit;// 句子是一种水果public class Orange : Fruit; // 水果工厂类public class FruitFactory{ // 根据水果名称制造一个水果对象 public static Fruit CreateInstance(string fruitName) { if(fruitName=="APPLE") return new Apple(); else if(fruiteName=="ORANGE") return new Orange(); else return null; }}
// 制造一个Apple对象,即:new Apple();
Apple anApple=(Apple)FruitFactory.CreateInstance("APPLE");
// 制造一个Orange对象,即:new Orange();
Orange anOrange=(Orange)FruitFactory.CreateInstance("ORANGE");
工厂类制造对象实例,实际通常是要通过语言所提供的RTTI(RunTime Type Identification运行时类型识别)机制来实现。在Visual C#.NET中,是通过“反射”来实现的。它被封装在“System.Reflection”名称空间下,通过C#反射,我们可以在程序运行期间动态地建立对象。关于C#.NET反射,你可以到其它网站上搜索一下相关资料,这里就不详述了。左边是工厂模式的UML示意图。
新建的DbTaskDriver.cs文件,位于DALFactory项目中
#001 using System;
#002 using System.Configuration;
#003 using System.Reflection; // 需要使用 .NET 反射
#004
#005 namespace TraceLWord5.DALFactory
#006 {
#007 /// <summary>
#008 /// DbTaskDriver 数据库访问层工厂
#009 /// </summary>
#010 public class DbTaskDriver
#011 {
#012 类 DbTaskDriver 构造器
#020
#021 /// <summary>
#022 /// 驱动数据库任务对象实例
#023 /// </summary>
#024 public object DriveLWordTask()
#025 {
#026 // 获取程序集名称
#027 string assemblyName=ConfigurationSettings.AppSettings["AssemblyName"];
#028 // 获取默认构造器名称
#029 string constructor=ConfigurationSettings.AppSettings["Constructor"];
#030
#031 // 建立 AccessTask 或者 SqlServerTask 对象实例
#032 return Assembly.Load(assemblyName).CreateInstance(constructor, false);
#033 }
#034 }
#035 }
那么相应的,LWordService.cs程序文件也要做相应的修改。
#001 using System;
#002 using System.Data;
#003
#004 using TraceLWord5.AccessTask;
#005 using TraceLWord5.Classes; // 引用实体规范层
#006 using TraceLWord5.DALFactory; // 引用数据访问层工厂
#007 using TraceLWord5.SqlServerTask;
#008
#009 namespace TraceLWord5.InterService
#010 {
...
#014 public class LWordService
#015 {
...
#020 public LWord[] ListLWord()
#021 {
#022 object dbTask=(new DbTaskDriver()).DriveLWordTask();
#023
#024 // 留言板运行在 Access 数据库平台上
#025 if(dbTask is AccessTask.LWordTask)
#026 return ((AccessTask.LWordTask)dbTask).ListLWord();
#027
#028 // 留言板运行在 MS SQL Server 数据库平台上
#029 if(dbTask is SqlServerTask.LWordTask)
#030 return ((SqlServerTask.LWordTask)dbTask).GetLWords();
#031
#032 return null;
#033 }
...
#039 public void PostLWord(LWord newLWord)
#040 {
#041 object dbTask=(new DbTaskDriver()).DriveLWordTask();
#042
#043 // 留言板运行在 Access 数据库平台上
#044 if(dbTask is AccessTask.LWordTask)
#045 ((AccessTask.LWordTask)dbTask).PostLWord(newLWord);
#046
#047 // 留言板运行在 MS SQL Server 数据库平台上
#048 if(dbTask is SqlServerTask.LWordTask)
#049 ((SqlServerTask.LWordTask)dbTask).AddNewLWord(newLWord);
#050 }
#051 }
#052 }
原来的AccessTask项目及程序文件不需要变化,只是多加了一个SqlServerTask项目。新项目中,也有一个LWordTask.cs程序文件,其内容是:
#001 using System;
#002 using System.Collections;
#003 using System.Data;
#004 using System.Data.SqlClient; // 需要访问 MS SQL Server 数据库
#005 using System.Web;
#006
#007 using TraceLWord5.Classes; // 引用实体规范层
#008
#009 namespace TraceLWord5.SqlServerTask
#010 {
#011 /// <summary>
#012 /// LWordTask 留言板任务类
#013 /// </summary>
#014 public class LWordTask
#015 {
#016 // 数据库连接字符串
#017 private const string DB_CONN=@"Server=127.0.0.1; uid=sa; pwd=;
DataBase=TraceLWordDb";
#018
#019 /// <summary>
#020 /// 读取 LWord 数据表,返回留言对象数组
#021 /// </summary>
#022 /// <returns></returns>
#023 public LWord[] GetLWords()
#024 {
#025 // 留言对象集合
#026 ArrayList lwordList=new ArrayList();
#027
#028 string cmdText="SELECT * FROM [LWord] ORDER BY [LWordID] DESC";
#029
#030 SqlConnection dbConn=new SqlConnection(DB_CONN);
#031 SqlCommand dbCmd=new SqlCommand(cmdText, dbConn);
#032
#033 try
#034 {
#035 dbConn.Open();
#036 SqlDataReader dr=dbCmd.ExecuteReader();
#037
#038 while(dr.Read())
#039 {
#040 LWord lword=new LWord();
#041
#042 // 设置留言编号
#043 lword.UniqueID=(int)dr["LWordID"];
#044 // 留言内容
#045 lword.TextContent=(string)dr["TextContent"];
#046 // 发送时间
#047 lword.PostTime=(DateTime)dr["PostTime"];
#048
#049 // 加入留言对象到集合
#050 lwordList.Add(lword);
#051 }
#052 }
#053 catch
#054 {
#055 throw;
#056 }
#057 finally
#058 {
#059 dbConn.Close();
#060 }
#061
#062 // 将集合转型为数组并返回给调用者
#063 return (LWord[])lwordList.ToArray(typeof(TraceLWord5.Classes.LWord));
#064 }
#065
#066 /// <summary>
#067 /// 发送留言信息到数据库
#068 /// </summary>
#069 /// <param name="newLWord">留言对象</param>
#070 public void AddNewLWord(LWord newLWord)
#071 {
#072 // 留言内容不能为空
#073 if(newLWord==null || newLWord.TextContent==null || newLWord.TextContent=="")
#074 throw new Exception("留言内容为空");
#075
#076 string cmdText="INSERT INTO [LWord]([TextContent]) VALUES(@TextContent)";
#077
#078 SqlConnection dbConn=new SqlConnection(DB_CONN);
#079 SqlCommand dbCmd=new SqlCommand(cmdText, dbConn);
#080
#081 // 设置留言内容
#082 dbCmd.Parameters.Add(new SqlParameter("@TextContent", SqlDbType.NText));
#083 dbCmd.Parameters["@TextContent"].Value=newLWord.TextContent;
#084
#085 try
#086 {
#087 dbConn.Open();
#088 dbCmd.ExecuteNonQuery();
#089 }
#090 catch
#091 {
#092 throw;
#093 }
#094 finally
#095 {
#096 dbConn.Close();
#097 }
#098 }
#099 }
#100 }
特别指出的是,这个SqlServerTask中的LWordTask程序文件,也遵循“土豆炖牛肉盖饭”式的强制标准!
在TraceLWord5中,也需要配置Web.Config文件,需要加入自定义的键值:
#001 <?xml version="1.0" encoding="utf-8" ?>
#002 <configuration>
#003
#004 <system.web>
#005 <identity impersonate="true" />
#006 <compilation defaultLanguage="c#" debug="true" />
#007 <customErrors mode="RemoteOnly" />
#008 </system.web>
#009
#010 <appSettings>
...
#026 <!--// SQLServer 2000 数据库任务程序集及驱动类名称 //-->
#027 <add key="AssemblyName"
#028 value="TraceLWord5.SqlServerTask" />
#029 <add key="Constructor"
#030 value="TraceLWord5.SqlServerTask.LWordTask" />
#031
#032 </appSettings>
#033
#034 </configuration>
通过修改配置文件中的关键信息,就可以修改留言板的数据库运行平台。这样便做到了跨数据库平台的目的。
请关注三层结构”原理与用意“ (五)
祝君早日成功!