数据库访问层的一种简单设计与实现

 

1 引言

在近期使用.NET开发项目时,发现这样一些问题,影响着程序的复用性和软件的灵活性:

(1)数据库类型是变动的

原因有两个:一是因为不同的客户,对同一个应用程序的性能要求或费用开销不同,要求使用的数据库类型不同;二是我们在开发程序时,在不同的使用环境下,需要使用不同类型的数据库。

比如,如果使用ACCESS数据库,那么在程序中会使用System.Data.OleDb命名空间下的类,如OleDbConnection,OleDbParameter,OleDbDataAdapter等等。如果现在要求支持SQL Server数据库该怎么办呢?那么就需要修改现有的大量的代码,将System.Data.OleDb下的类全部替换为System.Data.SqlClient命名空间下的类,即使是使用“查找/替换”进行批量的修改,也是非常繁琐,也很容易出错。当然还会存在一些错误,比如,OleDb是使用“?”来传递参数的,而SQL Server中使用的是命名参数(如,“@para”)。因此还需要修改大量的SQL语句,麻烦啊!

(2)主键生成的规则不同

不同应用环境下,可能使用不同的主键生成策略,有的需要使用数据库唯一键,有的使用表唯一键,有的需要加入时间或其他标记,有的只使用简单的计数器就可以,有的使用GUID…… 如果程序中写死了,下次环境变了,需要使用新的规则时,就又得改代码喽。

(3)数据库连接管理

控制连接的数目,有时时单个连接,有时是若干个连接,有时使用连接池……

(4)SQL语句安全性检查

为了防止恶意攻击,需要对SQL进行检查。有很多种检查方法,究竟使用哪个呢?

(5)程序中SQL语句到处飞

如果使用关系数据库,SQL语句是少不了的。但是SQL语句在应用层出现太多,一旦数据库发生变化,那么上层需要修改的SQL语句就太多了,而且在编译时发现不了SQL语句的错误,在运行时才能找到。另外大量的SQL语句,还严重影响了程序的美观。

对于上面的问题,最好的解决方法就是建立一个数据库访问中间层,来屏蔽这些问题。(当然有一些现成的框架,如NHibernate,可以使用,不需要我们自己动手。)本文后续部分将讨论如何解决这些问题,主要在如何设计,并给出部分实现。

2 约定

(1)本文中谈及的数据库仅限于关系数据库。数据库类型指不同的关系数据库系统,如Oracle,SQL Server,Sybase等等。

(2)数据库对象指ADO.NET中访问数据库的对象:Connection对象,Command对象,Adapter对象、Parameter对象。

3 应用程序的一般结构

通常,数据库相关的应用程序应该具有图1所示的结构。至于为什么,就不用多说了。从图中可以看到数据库访问层所处的位置,及其应该具有的功能。

DA01Hierarchy.jpg

图1 应用程序一般结构

4 适应不同类型的数据库

如第1节所述,在ADO.NET中,使用不同类型的数据库,主要就是这些数据库对象的变化:Connection,Command,Adapter,Parameter。在程序中我们需要根据不同的数据库创建合适的对象。比如,使用SQL Server数据库,就需要引用System.Data.SqlClient命名空间,并使用SqlConnection, SqlCommand, SqlAdapter对象;如果使用ORACLE数据库,则要引用System.Data.OracleClient命名空间,并使用OracleConnection,OralceCommand等等。

通常的简单做法是使用一个数据库对象工厂,在工厂中通过条件判断,是何种类型的数据库,创建相应的数据库对象,代码如下:

[代码1] 简单,但缺乏弹性和复用性的方法 

ContractedBlock.gif ExpandedBlockStart.gif DbObjectFactory(Simple)
 1     /// <summary>
 2     /// 数据库类型。
 3     /// </summary>
 4     public enum DbType
 5     {
 6         SqlServer,
 7         Oracle,
 8         OleDb,
 9     }
10 
11     public class DbObjectFactory
12     {
13         /// <summary>
14         /// Connection 对象的创建。
15         /// </summary>
16         /// <param name="dbType">数据库类型。</param>
17         /// <returns>Connection 对象。</returns>
18         public static IDbConnection CreateConnection(DbType dbType)
19         {
20             switch(dbType)
21             {
22                 case DbType.SqlServer:
23                     return new SqlConnection();
24                 case DbType.Oracle:
25                     return new OracleConnection();
26                 case DbType.OleDb:
27                     return new OleDbConnection();
28             }
29             throw new Exception("不支持的数据库类型。");
30         }
31 
32         /// <summary>
33         /// Parameter 对象的创建。
34         /// </summary>
35         /// <param name="dbType">数据库类型。</param>
36         /// <returns>Parameter 对象。</returns>
37         public static IDataParameter CreateParameter(DbType dbType)
38         {
39             switch(dbType)
40             {
41                 case DbType.SqlServer:
42                     return new SqlParameter();
43                 case DbType.Oracle:
44                     return new OracleParameter();
45                 case DbType.OleDb:
46                     return new OleDbParameter();
47             }
48             throw new Exception("不支持的数据库类型。");
49         }
50     }
51 

上面的代码中都是条件判断,而且

CreateConnection 函数和 CreateParameter 函数代码几乎一样, DataAdapter 和 Command 的创建也是如此。这样的做法缺点主要有两点:

(1)代码重复,可维护性差;

(2)新增加一种数据库类型时,需要修改现有的代码。

其实我们仔细分析以下数据库类型和对象之间的关系,可以发现图2所示的规律:

DA02DatabaseObject.jpg
图2 不同数据库和对象的关系

很明显,采用抽象工厂模式可以很好的处理不同数据库和不同数据库对象之间的组和系列的关系。如图3:

DA03DbObjectFactory.jpg
图3 数据库对象工厂设计

根据上面的类图,生成C#代码如下:


[代码2: DbObjectFactory.cs] (有删节)

ContractedBlock.gif ExpandedBlockStart.gif DbObjectFactory
 1     /// <summary>
 2     /// 数据库对象工厂的虚基类。
 3     /// </summary>
 4     public abstract class DbObjectFactory
 5     {
 6         /// <summary>
 7         /// 创建数据库连接对象,并打开连接。
 8         /// </summary>
 9         /// <returns> 数据库连接。返回null 表示连接失败。</returns>
10         public abstract IDbConnection CreateConnection();
11 
12         /// <summary>
13         /// 创建Command 对象。
14         /// </summary>
15         /// <returns> 执行SQL 的Command 对象。</returns>
16         public abstract IDbCommand CreateCommand();
17 
18         /// <summary>
19         /// 创建DbDataAdapter 对象。
20         /// </summary>
21         /// <returns>DbDataAdapter 对象。</returns>
22         public abstract IDbDataAdapter CreateDataAdapter();
23 
24         /// <summary>
25         /// 创建Parameter 对象。
26         /// </summary>
27         /// <returns>Parameter 对象。</returns>
28         public abstract IDataParameter CreateParameter();
29     }
30 


[代码3: OleDbObjectFactory.cs] (有删节)

ContractedBlock.gif ExpandedBlockStart.gif OleDbObjectFactory
 1     /// <summary>
 2     ///  OleDb 数据库对象工厂。
 3     /// </summary>
 4     public class OleDbObjectFactory : DbObjectFactory
 5     {
 6         /// <summary>
 7         /// 创建数据库连接对象,并打开连接。
 8         /// </summary>
 9         /// <returns> 数据库连接。返回null 表示连接失败。</returns>
10         public override IDbConnection CreateConnection()
11         {
12             return new OleDbConnection();
13         }
14 
15         /// <summary>
16         /// 创建Command 对象。
17         /// </summary>
18         /// <returns> 执行SQL 的Command 对象。</returns>
19         public override IDbCommand CreateCommand()
20         {
21             return new OleDbCommand();
22         }
23 
24         /// <summary>
25         /// 创建DbDataAdapter 对象。
26         /// </summary>
27         /// <returns>DbDataAdapter 对象。</returns>
28         public override IDbDataAdapter CreateDataAdapter()
29         {
30             return new OleDbDataAdapter();
31         }
32 
33         /// <summary>
34         /// 创建Parameter 对象。
35         /// </summary>
36         /// <returns>Parameter 对象。</returns>
37         public override IDataParameter CreateParameter()
38         {
39             return new OleDbParameter();
40         }
41     }
42 


[代码4: OleDbObjectFactory.cs] (有删节)

ContractedBlock.gif ExpandedBlockStart.gif OleDbObjectFactory
 1     /// <summary>
 2     /// Sql Server 数据库对象工厂。
 3     /// </summary>
 4     public class SqlObjectFactory : DbObjectFactory
 5     {
 6         /// <summary>
 7         /// 创建数据库连接对象,并打开连接。
 8         /// </summary>
 9         /// <returns> 数据库连接。返回null 表示连接失败。</returns>
10         public override IDbConnection CreateConnection()
11         {
12             return new SqlConnection();
13         }
14 
15         /// <summary>
16         /// 创建Command 对象。
17         /// </summary>
18         /// <returns> 执行SQL 的Command 对象。</returns>
19         public override IDbCommand CreateCommand()
20         {
21             return new SqlCommand();
22         }
23 
24         /// <summary>
25         /// 创建DbDataAdapter 对象。
26         /// </summary>
27         /// <returns>DbDataAdapter 对象。</returns>
28         public override IDbDataAdapter CreateDataAdapter()
29         {
30             return new SqlDataAdapter();
31         }
32 
33         /// <summary>
34         /// 创建Parameter 对象。
35         /// </summary>
36         /// <returns>Parameter 对象。</returns>
37         public override IDataParameter CreateParameter()
38         {
39             return new SqlParameter();
40         }
41     }
42 

使用时,根据应用环境的数据库类型,创建相应的具体工厂对象即可。这样就避免了使用生硬的条件判断了。新增加一种类型的数据库时,只要添加一个类,从DbObjectFactory继承即可,而现有的代码不需要做任何的修改。(第8节描述了应用程序如何使用这些类)

5 主键的生成

不同的业务需要,生成主键的方式不同。(BTW,主键最好是无意义的)。一种简单的做法和第4节的[代码1]类似,缺点也很明显,不再赘述。

所有的主键生成器使用一个接口,不同类型的生成器实现改接口即可,然后由具体的应用程序来选择使用不同的主键生成器。(第8节描述了应用程序如何使用)

DA04IdGenerator.jpg
图4 关键字生成器类图

根据上面的类图,生成代码如下:


[代码5: IdGenerator.cs]

ContractedBlock.gif ExpandedBlockStart.gif IdGenerator
 1 using System;
 2     /// <summary>
 3     /// 关键码(Id)生成器接口。
 4     /// </summary>
 5     public interface IdGenerator
 6     {
 7         /// <summary>
 8         /// 获取一个全局的关键码(ID)。
 9         /// </summary>
10         /// <returns>关键码(ID)。</returns>
11         long GetNextId();
12 
13         /// <summary>
14         /// 获取指定KeyName上的一个关键码(ID)。
15         /// </summary>
16         /// <param name="keyName">关键码的名称/标识。</param>
17         /// <returns>关键码(ID)。</returns>
18         long GetNextId(string keyName);
19     }
20 


[代码6: CounterIdGenerator.cs]

ContractedBlock.gif ExpandedBlockStart.gif CounterIdGenerator
 1     /// <summary>
 2     /// 关键码(Id)生成器。使用计数器。
 3     /// </summary>
 4     public class CounterIdGenerator : IdGenerator
 5     {
 6         /// <summary>
 7         /// 键表,用于存储计数器关键码。
 8         /// </summary>
 9         protected Hashtable keyTable = new Hashtable(8);
10 
11         /// <summary>
12         /// 构造函数。
13         /// </summary>
14         public CounterIdGenerator()
15         {
16         }
17 
18         /// <summary>
19         /// 获取一个全局的关键码(ID)。
20         /// </summary>
21         /// <returns>关键码(ID)。</returns>
22         public long GetNextId()
23         {
24             return GetNextId("GlobalKey");
25         }
26 
27         /// <summary>
28         /// 获取指定KeyName上的一个关键码(ID)。
29         /// </summary>
30         /// <param name="keyName">关键码的名称/标识。</param>
31         /// <returns>关键码(ID)。</returns>
32         public long GetNextId(string keyName)
33         {
34             if(keyName == null || keyName == string.Empty)
35             {
36                 throw new ArgumentNullException("keyName");
37             }
38 
39             long nextKey = 0;
40 
41             if(keyTable.ContainsKey(keyName))
42             {
43                 nextKey = Convert.ToInt64(keyTable[keyName]);
44             }
45             else
46             {
47                 keyTable.Add(keyName, nextKey);
48             }
49 
50             keyTable[keyName] = ++nextKey;
51 
52             return nextKey;
53         }
54     }
55 


[代码7:KeyTableIdGenerator.cs]

ContractedBlock.gif ExpandedBlockStart.gif KeyTableIdGenerator
 1     /// <summary>
 2     /// 关键码(Id)生成器。使用键表。
 3     /// </summary>
 4     public class KeyTableIdGenerator : IdGenerator
 5     {
 6         /// <summary>
 7         /// 构造函数。
 8         /// </summary>
 9         /// <param name="dbConfig">数据库配置。</param>
10         /// <param name="keyTableName">数据库中的键表名称。</param>
11         public KeyTableIdGenerator(DatabaseConfig dbConfig, string keyTableName)
12         {
13             _dbConfig = dbConfig;
14             _keyTableName = keyTableName;
15         }
16 
17         /// <summary>
18         /// 数据库配置。
19         /// </summary>
20         private DatabaseConfig _dbConfig = null;
21 
22         /// <summary>
23         /// 数据库中的键表名称。
24         /// </summary>
25         protected string _keyTableName = null;
26 
27         /// <summary>
28         /// 读写数据库表KeyTable的模型。
29         /// </summary>
30         private KeyTableModel model = null;
31 
32         /// <summary>
33         /// 读写数据库表KeyTable的模型。
34         /// </summary>
35         private KeyTableModel Model
36         {
37             get
38             {
39                 if(model == null)
40                 {
41                     return model = new KeyTableModel(_dbConfig, _keyTableName);
42                 }
43                 else
44                 {
45                     return model;
46                 }
47             }
48         }
49 
50         /// <summary>
51         /// 获取一个关键码ID。
52         /// </summary>
53         /// <returns>关键码。</returns>
54         public long GetNextId()
55         {
56             return Model.GetNextKey();
57         }
58 
59         /// <summary>
60         /// 获取指定KeyName上的一个关键码(ID)。
61         /// </summary>
62         /// <param name="keyName">关键码的名称/标识。这里一般使用表名。</param>
63         /// <returns>关键码(ID)。</returns>
64         public long GetNextId(string keyName)
65         {
66             return Model.GetNextKey(keyName);
67         }
68     }
69 

如果有新的ID生成规则,则从IdGenerator继承并应用即可。

6、数据库连接管理

方法和第5节的ID生成器一样。描述 从略。

DA05ConnectionManager.jpg
图5 数据库连接管理类图

7SQL语句安全性检查

方法和第5节的ID生成器一样。描述从略。

DA06SqlStatementChecker.jpg
图6 SQL语句检查器类图

8、组合起来(如何使用上面的东东)

上面谈到了如何适应不同的数据库,不同的主键生成,不同的数据库连接管理和SQL语句安全性检查,那么在具体某一中类型的应用程序中,如何使用他们呢?

不同的主键生成器,不同的数据库连接器,实现了不同的行为或算法,而不同应用程序程序就是不同的使用环境,因此采用策略模式是很自然的了。

策略模式设计到三个角色类:

环境角色:应用程序充当使用的环境

抽象策略角色:抽象类DatabaseConfig

具体策略:由应用程序实现的一个具体类,从DatabaseConfig类继承。

三个角色类如图7所示,Application是应用程序类,环境角色;DatabaseConfig是抽象策略类;XxxAppDbConfig是具体策略类,由该应用程序自己创建并实现。

DA07Strategy.jpg
图7 策略模式的使用


[代码8:DatabaseConfig.cs]  

ContractedBlock.gif ExpandedBlockStart.gif DatabaseConfig
  1     /// <summary>
  2     /// 数据库配置。
  3     /// (包括数据库连接管理器、数据库对象工厂管理器、关键字生成器和SQL语句检查器的配置)。
  4     /// [Design Patterns]和类DbObjectFactory 一起,构成Strategy 模式。
  5     /// </summary>
  6     [Serializable]
  7     public abstract class DatabaseConfig
  8     {
  9         #region 数据库对象工厂
 10 
 11         /// <summary>
 12         /// 数据库对象工厂。
 13         /// </summary>
 14         protected DbObjectFactory _dbObjectFactory = null;
 15 
 16         /// <summary>
 17         /// 创建数据库对象工厂。
 18         /// </summary>
 19         protected abstract DbObjectFactory NewDbObjectFactory();
 20 
 21         /// <summary>
 22         /// 获取数据库对象工厂。
 23         /// </summary>
 24         public virtual DbObjectFactory DbObjectFactory
 25         {
 26             get
 27             {
 28                 if(_dbObjectFactory == null)
 29                 {
 30                     _dbObjectFactory = NewDbObjectFactory();
 31                 }
 32                 return _dbObjectFactory;
 33             }
 34         }
 35 
 36         #endregion
 37 
 38         #region 数据库链接管理器
 39 
 40         /// <summary>
 41         /// 数据库连接管理器。
 42         /// </summary>
 43         protected ConnectionManager _connectionManager = null;
 44 
 45         /// <summary>
 46         /// 创建数据库连接管理器。
 47         /// </summary>
 48         protected abstract ConnectionManager NewConnectionManager();
 49 
 50         /// <summary>
 51         /// 获取数据库连接管理器。
 52         /// </summary>
 53         public virtual ConnectionManager ConnectionManager
 54         {
 55             get
 56             {
 57                 if(_connectionManager == null)
 58                 {
 59                     _connectionManager = NewConnectionManager();
 60                 }
 61                 return _connectionManager;
 62             }
 63         }
 64 
 65         #endregion
 66 
 67         #region 关键字生成器
 68 
 69         /// <summary>
 70         /// 关键字生成器。
 71         /// </summary>
 72         protected IdGenerator _idGenerator = null;
 73 
 74         /// <summary>
 75         /// 创建关键字生成器。
 76         /// </summary>
 77         /// <returns></returns>
 78         protected abstract IdGenerator NewIdGenerator();
 79 
 80         /// <summary>
 81         /// 获取关键字生成器。
 82         /// </summary>
 83         public virtual IdGenerator IdGenerator
 84         {
 85             get
 86             {
 87                 if(_idGenerator == null)
 88                 {
 89                     _idGenerator = NewIdGenerator();
 90                 }
 91                 return _idGenerator;
 92             }
 93         }
 94 
 95         #endregion
 96         
 97         #region SQL 语句检查器
 98 
 99         /// <summary>
100         /// SQL 语句检查器。
101         /// </summary>
102         protected SqlStatementChecker _sqlStatementChecker;
103 
104         /// <summary>
105         /// 创建SQL 语句检查器。
106         /// </summary>
107         protected abstract SqlStatementChecker NewSqlStatementChecker();
108 
109         /// <summary>
110         /// 获取SQL 语句检查器。
111         /// </summary>
112         public virtual SqlStatementChecker SqlStatementChecker
113         {
114             get
115             {
116                 if(_sqlStatementChecker == null)
117                 {
118                     _sqlStatementChecker = NewSqlStatementChecker();
119                 }
120                 return _sqlStatementChecker;
121             }
122         }
123 
124         /// <summary>
125         /// 是否检查SQL 语句的安全性。
126         /// </summary>
127         protected bool _checkSqlStatement = false;
128 
129         /// <summary>
130         /// 获取或设置是否检查SQL 语句的安全性。
131         /// </summary>
132         public virtual bool CheckSqlStatement
133         {
134             get
135             {
136                 return _checkSqlStatement;
137             }
138             set
139             {
140                 _checkSqlStatement = value;
141             }
142         }
143 
144         #endregion
145     }
146 


[代码9:XxxAppDbConfig.cs]

ContractedBlock.gif ExpandedBlockStart.gif HfdDBConfig
 1     /// <summary>
 2     /// HFD系统的数据库配置。
 3     /// </summary>
 4     public class HfdDBConfig : DatabaseConfig
 5     {
 6         private static string _connectionString = null;
 7 
 8         /// <summary>
 9         /// 获取数据库连接字符串。
10         /// </summary>
11         public static string ConnectionString
12         {
13             get
14             {
15                 if(_connectionString == null)
16                 {
17                     // 读取配置文件,获取数据库连接字符串。
18                     // dot.gif
19                     _connectionString = "server=(local);database=HfdLog;uid=sa;pwd=1";
20                 }
21                 return _connectionString;
22             }
23         }
24 
25         protected override ConnectionManager NewConnectionManager()
26         {
27             // 每个进程和该数据库之间只有一个连接。
28             return new SingletonConnectionManager(ConnectionString, this);
29         }
30 
31         protected override DbObjectFactory NewDbObjectFactory()
32         {
33             // 采用 SQL Server 数据库。
34             return new SqlObjectFactory(this);
35         }
36 
37         protected override IdGenerator NewIdGenerator()
38         {
39             // 使用键表生成ID
40             return new KeyTableIdGenerator(this"KeyTable"); 
41         }
42 
43         protected override SqlStatementChecker NewSqlStatementChecker()
44         {
45             // 不进行SQL语句的安全性检查。
46             return null
47         }
48 
49     }
50 

9、表模型

至此,基本的结构已经搭建好了,但是距离数据库访问层还差一步,就是实现数据的访问模式,实现对物理数据库中的表、视图等的访问。

应用程序对数据库的访问有好几种方式:

(1)事务脚本(存储过程)。一种面向过程的方法。

(2)ORM(对象-关系映射)。一种面向对象的方法。

(3)表模型。以物理数据表为基本单位进行访问,类似 .NET中的DataTable。

我觉得在 .NET中还是第三种方式更容易实现一点。

因为表和视图有很多相似点,不同的是视图是只读的,表是可读写的。因此建立一个基类DataModel(抽象类),提供表和视图相同的操作,然后为表和视图各建立一个类TableModel和ViewModel,从DataModel继承,并添加各自不同的操作。如图8:

DA08Model.jpg
图8 表模型类图

DataModel中的GetView方法作为查询,TableModel中的Delete方法是删除,XxxModel中的Insert和Update方法分别是添加和修改。XxxModel是由实际的应用程序实现的,针对表xxx的数据模型。这样我们可以为每一个表和视图都建立一个Model,来实现对数据库表和视图的操作。这是数据访问层的核心。(注:DataModel中的TableName,KeyName是抽象属性,由基类(如XxxModel)实现)


[代码10:DataModel.cs]

ContractedBlock.gif ExpandedBlockStart.gif DataModel
  1     /// <summary>
  2 
  3     /// 数据模型。
  4 
  5     /// </summary>
  6 
  7     [Serializable]
  8 
  9     public abstract class DataModel
 10 
 11     {
 12 
 13         #region 构造函数
 14 
 15  
 16 
 17         /// <summary>
 18 
 19         /// 创建数据模型实例,并载入初始化数据到 Table。
 20 
 21         /// </summary>
 22 
 23         /// <param name="dbConfig">数据库配置。</param>
 24 
 25         public DataModel(DatabaseConfig dbConfig)
 26 
 27         {
 28 
 29             if(dbConfig == null)
 30 
 31             {
 32 
 33                 throw new ArgumentNullException("dbConfig");
 34 
 35             }
 36 
 37  
 38 
 39             _dbConfig = dbConfig;
 40 
 41  
 42 
 43             InitTable();
 44 
 45         }
 46 
 47  
 48 
 49         /// <summary>
 50 
 51         /// 数据库配置。
 52 
 53         /// </summary>
 54 
 55         protected DatabaseConfig _dbConfig = null;
 56 
 57  
 58 
 59         #endregion
 60 
 61  
 62 
 63         #region 属性
 64 
 65  
 66 
 67         /// <summary>
 68 
 69         /// 获取表名或视图名。
 70 
 71         /// </summary>
 72 
 73         public abstract string TableName
 74 
 75         {
 76 
 77             get;
 78 
 79         }
 80 
 81  
 82 
 83         /// <summary>
 84 
 85         /// 获取关键字名。
 86 
 87         /// </summary>
 88 
 89         public abstract string KeyName
 90 
 91         {
 92 
 93             get;
 94 
 95         }
 96 
 97  
 98 
 99         /// <summary>
100 
101         /// 该表的所有列的列名。
102 
103         /// 用逗号隔开。用于 SELECT 语句中。
104 
105         /// </summary>
106 
107         /// <remarks>
108 
109         /// 目的:避免使用SELECT * 。
110 
111         /// </remarks>
112 
113         public abstract string ColumnNames
114 
115         {
116 
117             get;
118 
119         }
120 
121  
122 
123         /// <summary>
124 
125         /// 获取内蕴的 DataTable 对象。
126 
127         /// </summary>
128 
129         public DataTable Table
130 
131         {
132 
133             get
134 
135             {
136 
137                 return _table;
138 
139             }
140 
141         }
142 
143         /// <summary>
144 
145         /// 内存表。
146 
147         /// </summary>
148 
149         protected DataTable _table = null;
150 
151  
152 
153         /// <summary>
154 
155         /// 获取 Table 中的记录的条数。
156 
157         /// </summary>
158 
159         public int RowCount
160 
161         {
162 
163             get
164 
165             {
166 
167                 return _table.Rows.Count;
168 
169             }
170 
171         }
172 
173  
174 
175         /// <summary>
176 
177         /// 指示内存表 Table 中的字符串比较是否区分大小写。
178 
179         /// </summary>
180 
181         public bool CaseSensitive
182 
183         {
184 
185             get
186 
187             {
188 
189                 return _table.CaseSensitive;
190 
191             }
192 
193             set
194 
195             {
196 
197                 _table.CaseSensitive = value;
198 
199             }
200 
201         }
202 
203  
204 
205         /// <summary>
206 
207         /// 初始化内存表时使用的 Sql 语句。
208 
209         /// </summary>
210 
211         public abstract string InitalSelectCommandText
212 
213         {
214 
215             get;
216 
217         }
218 
219  
220 
221         #endregion
222 
223  
224 
225         #region 获取数据库对象
226 
227  
228 
229         /// <summary>
230 
231         /// 获取数据库连接。
232 
233         /// </summary>
234 
235         protected DbConnection Connection
236 
237         {
238 
239             get
240 
241             {
242 
243                 return (DbConnection)_dbConfig.ConnectionManager.GetConnection();
244 
245             }
246 
247         }
248 
249  
250 
251         /// <summary>
252 
253         /// 获取一个DataAdapter对象。
254 
255         /// </summary>
256 
257         /// <returns>DataAdapter对象。</returns>
258 
259         /// <remarks>返回的DataAdapter对象的SelectCommand的Connection属性已被设置。</remarks>
260 
261         protected DbDataAdapter GetDataAdapter()
262 
263         {
264 
265             DbDataAdapter da = (DbDataAdapter)_dbConfig.DbObjectFactory.CreateDataAdapter();
266 
267             return da;
268 
269         }
270 
271  
272 
273         /// <summary>
274 
275         /// 获取一个Command对象。
276 
277         /// </summary>
278 
279         /// <returns>Command对象。</returns>
280 
281         /// <remarks>返回的Command对象的Connection属性已被设置。</remarks>
282 
283         protected DbCommand GetCommand()
284 
285         {
286 
287             DbCommand cmd = (DbCommand)_dbConfig.DbObjectFactory.CreateCommand();
288 
289             cmd.Connection = Connection;
290 
291             return cmd;
292 
293         }
294 
295  
296 
297         /// <summary>
298 
299         /// 获取一个Parameter对象。
300 
301         /// </summary>
302 
303         /// <returns>Parameter对象。</returns>
304 
305         protected DbParameter GetParameter()
306 
307         {
308 
309             return (DbParameter)_dbConfig.DbObjectFactory.CreateParameter();
310 
311         }
312 
313  
314 
315         #endregion
316 
317  
318 
319         #region 初始化内存表
320 
321  
322 
323         /// <summary>
324 
325         /// 从数据库中读取初始数据到内存表。
326 
327         /// </summary>
328 
329         /// <remarks>
330 
331         /// 派生类最好能复写(override)该方法,但是派生类不需要要调用此方法,由基类的构造函数调用,类似于模板方法。
332 
333         /// </remarks>
334 
335         protected virtual void InitTable()
336 
337         {
338 
339             DbDataAdapter da = GetDataAdapter();
340 
341  
342 
343             try
344 
345             {
346 
347                 _table = new DataTable(TableName);
348 
349  
350 
351                 da.SelectCommand = GetCommand();
352 
353                 da.SelectCommand.CommandText = InitalSelectCommandText;
354 
355                 da.SelectCommand.CommandType = CommandType.Text;
356 
357  
358 
359                 // 载入数据。
360 
361                 da.Fill(_table);
362 
363  
364 
365                 // 设置数据表的主键。
366 
367                 _table.PrimaryKey = new DataColumn[] { _table.Columns[KeyName] };
368 
369             }
370 
371             catch(Exception ex)
372 
373             {
374 
375                 Debug.WriteLine(
376 
377                     ex.ToString()
378 
379                     + Environment.NewLine
380 
381                     + "SelectCommand为:"
382 
383                     + da.SelectCommand.CommandText
384 
385                     );
386 
387             }
388 
389         }
390 
391  
392 
393         /// <summary>
394 
395         /// 强制重新从数据库中读取数据,刷新内存表。
396 
397         /// </summary>
398 
399         public void Refresh(string selectCommandText)
400 
401         {
402 
403             // 1、清除 DataTable 中原来的数据。
404 
405  
406 
407             try
408 
409             {
410 
411                 _table.Clear();
412 
413                 _table.AcceptChanges();
414 
415             }
416 
417             finally
418 
419             {
420 
421             }
422 
423  
424 
425  
426 
427             // 2、载入新数据。
428 
429  
430 
431             LoadData(selectCommandText);
432 
433         }
434 
435  
436 
437         /// <summary>
438 
439         /// 从数据库加载数据。
440 
441         /// </summary>
442 
443         protected void LoadData(string selectCommandText)
444 
445         {
446 
447             DbDataAdapter da = GetDataAdapter();
448 
449             da.SelectCommand = GetCommand();
450 
451             da.SelectCommand.CommandText = selectCommandText;
452 
453             da.SelectCommand.CommandType = CommandType.Text;
454 
455  
456 
457             try
458 
459             {
460 
461                 da.Fill(_table);
462 
463             }
464 
465             catch(Exception ex)
466 
467             {
468 
469                 Debug.WriteLine(
470 
471                     ex.ToString()
472 
473                     + Environment.NewLine
474 
475                     + "SelectCommand为:"
476 
477                     + da.SelectCommand.CommandText
478 
479                     );
480 
481             }
482 
483             finally
484 
485             {
486 
487             }
488 
489         }
490 
491  
492 
493         #endregion
494 
495  
496 
497         #region SQL 命令(最好使用命名参数)
498 
499  
500 
501         /// <summary>
502 
503         /// 获取 SELECT 语句。
504 
505         /// </summary>
506 
507         /// <returns>SELECT 语句。没有 WHERE 子句 ,没有 ORDER BY 子句。</returns>
508 
509         protected string GetSelectText()
510 
511         {
512 
513             return GetSelectText(nullnull);
514 
515         }
516 
517  
518 
519         /// <summary>
520 
521         /// 获取 SELECT 语句,并使用 rowFilter 作为 WHERE 子句,sort 作为 ORDER 子句。
522 
523         /// </summary>
524 
525         /// <param name="rowFilter">
526 
527         /// 条件语句。充当 WHERE 子句。
528 
529         /// 最好使用命名参数,而不是拼接字符串。
530 
531         /// 可以为空(null或string.Empty)。
532 
533         /// </param>
534 
535         /// <param name="sort">排序条件。作为 ORDER 子句。可以为空。</param>
536 
537         /// <returns>SELECT 语句。</returns>
538 
539         protected string GetSelectText(string rowFilter, string sort)
540 
541         {
542 
543             StringBuilder sb = new StringBuilder();
544 
545             sb.Append("SELECT ");
546 
547             sb.Append(ColumnNames);
548 
549             sb.Append(" FROM ");
550 
551             sb.Append(TableName);
552 
553  
554 
555             if(rowFilter != null && rowFilter != string.Empty)
556 
557             {
558 
559                 sb.Append(" WHERE ");
560 
561                 sb.Append(rowFilter);
562 
563             }
564 
565  
566 
567             if(sort != null && sort != string.Empty)
568 
569             {
570 
571                 sb.Append(" ORDER BY ");
572 
573                 sb.Append(sort);
574 
575             }
576 
577  
578 
579             return sb.ToString();
580 
581         }
582 
583  
584 
585         #endregion
586 
587         
588 
589         #region 查询(返回 DataView 对象)
590 
591  
592 
593         /// <summary>
594 
595         /// 获取 DataView 对象。
596 
597         /// </summary>
598 
599         /// <returns>DataView 对象。</returns>
600 
601         /// <remarks>
602 
603         /// DataView 是用于排序、筛选、搜索和导航的 DataTable 的自定义视图。
604 
605         /// </remarks>
606 
607         public DataView GetView()
608 
609         {
610 
611             return GetView(null);
612 
613         }
614 
615  
616 
617         /// <summary>
618 
619         /// 根据指定的 rowFilter 获取 DataView 对象。
620 
621         /// </summary>
622 
623         /// <param name="rowFilter">要应用于 DataView 的条件语句。</param>
624 
625         /// <returns>DataView 对象。如果返回 null,表示rowFilter 参数的语法错误。</returns>
626 
627         /// <remarks>
628 
629         /// rowFilter 参数的语法规则参见:
630 
631         /// ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.NETDEVFX.v20.chs/cpref4/html/P_System_Data_DataColumn_Expression.htm
632 
633         /// </remarks>
634 
635         public DataView GetView(string rowFilter)
636 
637         {
638 
639             return GetView(rowFilter, null, DataViewRowState.CurrentRows);
640 
641         }
642 
643  
644 
645         /// <summary>
646 
647         /// 根据rowFilter中指定的条件,查询[TableName]表中的所有数据。
648 
649         /// </summary>
650 
651         /// <param name="rowFilter">条件语句。</param>
652 
653         /// <param name="sort">排序条件。</param>
654 
655         /// <returns>DataView 对象。如果返回 null,表示rowFilter 或 sort 参数的语法错误。</returns>
656 
657         /// <remarks>
658 
659         /// rowFilter 和 sort 参数的语法规则参见:
660 
661         /// ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.NETDEVFX.v20.chs/cpref4/html/P_System_Data_DataColumn_Expression.htm
662 
663         /// </remarks>
664 
665         public DataView GetView(string rowFilter, string sort)
666 
667         {
668 
669             return GetView(rowFilter, sort, DataViewRowState.CurrentRows);
670 
671         }
672 
673  
674 
675         /// <summary>
676 
677         /// 根据指定的 rowFilter、sort 和 rowState 获取 DataView 对象。
678 
679         /// </summary>
680 
681         /// <param name="rowFilter">要应用于 DataView 的条件语句。</param>
682 
683         /// <param name="sort">要应用于 DataView 的排序条件。</param>
684 
685         /// <param name="rowState">要应用于 DataView 的 DataViewRowState。</param>
686 
687         /// <returns>DataView 对象。如果返回 null,表示rowFilter 或 sort 参数的语法错误。</returns>
688 
689         /// <remarks>
690 
691         /// rowFilter 和 sort 参数的语法规则参见:
692 
693         /// ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.NETDEVFX.v20.chs/cpref4/html/P_System_Data_DataColumn_Expression.htm
694 
695         /// </remarks>
696 
697         public DataView GetView(string rowFilter, string sort, DataViewRowState rowState)
698 
699         {
700 
701             DataView view = null;
702 
703             try
704 
705             {
706 
707                 view = new DataView(_table);
708 
709  
710 
711                 if(rowFilter != null && rowFilter != string.Empty)
712 
713                 {
714 
715                     view.RowFilter = rowFilter;
716 
717                 }
718 
719  
720 
721                 if(sort != null && sort != string.Empty)
722 
723                 {
724 
725                     view.Sort = sort;
726 
727                 }
728 
729  
730 
731                 view.RowStateFilter = rowState;
732 
733             }
734 
735             catch(Exception ex)
736 
737             {
738 
739                 Debug.WriteLine(ex.ToString());
740 
741                 return null;
742 
743             }
744 
745  
746 
747             return view;
748 
749         }
750 
751  
752 
753         #endregion
754 
755  
756 
757         #region 获取数据
758 
759  
760 
761         /// <summary>
762 
763         /// 获取关键字为 id 的 DataRow。
764 
765         /// </summary>
766 
767         /// <param name="id">关键字。</param>
768 
769         /// <returns>如果存在,返回 DataRow 对象。否则,返回 null。</returns>
770 
771         public DataRow GetRow(long id)
772 
773         {
774 
775             DataRow[] rows = _table.Select(KeyName + "=" + id.ToString());
776 
777  
778 
779             if(rows.Length == 0)
780 
781             {
782 
783                 return null;
784 
785             }
786 
787             else if(rows.Length == 1)
788 
789             {
790 
791                 return rows[0];
792 
793             }
794 
795             else
796 
797             {
798 
799                 StringBuilder sb = new StringBuilder();
800 
801                 sb.Append("严重错误:关键字重复。");
802 
803                 sb.Append(Environment.NewLine);
804 
805                 sb.Append(" 表[");
806 
807                 sb.Append(TableName);
808 
809                 sb.Append("]中的关键字[");
810 
811                 sb.Append(id);
812 
813                 sb.Append("]重复");
814 
815                 sb.Append(rows.Length);
816 
817                 sb.Append("次。");
818 
819                 throw new Exception(sb.ToString());
820 
821             }
822 
823         }
824 
825  
826 
827         #endregion
828 
829  
830 
831     }
832 


[代码11:TableModel.cs]

ContractedBlock.gif ExpandedBlockStart.gif TableModel
  1     /// <summary>
  2     /// 表模型。用于操作数据库表。
  3     /// </summary>
  4     [Serializable]
  5     public abstract class TableModel : DataModel
  6     {
  7         #region 构造函数
  8 
  9         /// <summary>
 10         /// 创建数据模型实例,并载入初始化数据到 Table。
 11         /// </summary>
 12         /// <param name="dbConfig">数据库配置。</param>
 13         public TableModel(DatabaseConfig dbConfig)
 14             : base(dbConfig)
 15         {
 16         }
 17 
 18         #endregion
 19 
 20         #region 删除
 21 
 22         /// <summary>
 23         /// 删除指定id的记录。
 24         /// 该方法不会影响内存表Table。
 25         /// </summary>
 26         /// <param name="id">关键码。</param>
 27         /// <returns>
 28         /// 包含三个元素的string数组。
 29         /// [0]:"fail"或"success",分别指示成功或失败。
 30         /// [1]:失败的原因(异常) 或 成功操作影响的行数,正常情况下,该值应该为0或1。
 31         /// [2]:执行的SQL语句。
 32         /// </returns>
 33         public virtual string[] Delete(long id)
 34         {
 35             // 1、删除数据库中的数据。
 36 
 37             IDbCommand cmd = GetCommand();
 38             cmd.CommandText = GetDeleteText();
 39             cmd.CommandType = CommandType.Text;
 40 
 41             DbParameter idParam = GetParameter();
 42             idParam.ParameterName = "@" + KeyName;
 43             idParam.Value = id;
 44             cmd.Parameters.Add(idParam);
 45 
 46             int affectedRows = -1;
 47             try
 48             {
 49                 affectedRows = cmd.ExecuteNonQuery();
 50             }
 51             catch(Exception ex)
 52             {
 53                 return new string[] { "fail", ex.ToString(), cmd.CommandText };
 54             }
 55 
 56 
 57             // 2、删除内存表DataTable中的数据。
 58 
 59             if(_table != null)
 60             {
 61                 try
 62                 {
 63                     DataRow[] rows = _table.Select(KeyName + "=" + id);
 64                     foreach(DataRow row in rows)
 65                     {
 66                         row.Delete();
 67                     }
 68                     _table.AcceptChanges();
 69                 }
 70                 catch(Exception ex) // 数据库已经删除成功,但是内存表更新失败。
 71                 {
 72                     return new string[] { "warning", ex.ToString(), cmd.CommandText };
 73                 }
 74                 finally
 75                 {
 76                 }
 77             }
 78 
 79             return new string[] { "success", Convert.ToString(affectedRows), cmd.CommandText };
 80         }
 81 
 82         #endregion
 83 
 84         #region 关键码
 85 
 86         protected long GetNextId()
 87         {
 88             return _dbConfig.IdGenerator.GetNextId(TableName);
 89         }
 90 
 91         #endregion
 92 
 93         #region 检查
 94 
 95         /// <summary>
 96         /// 字符串检查。
 97         /// 检查规则:
 98         /// 根据canBeEmpty指示的值检查是否为空值。(空值:null或String.Empty)
 99         /// 如果不为空,长度应小于 maxLength 指示的值。
100         /// </summary>
101         /// <param name="str">待检查的字符串。</param>
102         /// <param name="canBeEmpty">是否可以为空(null或String.Empty)。</param>
103         /// <param name="maxLength">字符串的最大长度。</param>
104         /// <returns>如果此方法成功,则为 true;否则为 false。</returns>
105         /// <remarks>String.Empty 等价于 "",其长度为 0。</remarks>
106         public virtual bool CheckString(string str, bool canBeEmpty, int maxLength)
107         {
108             if(str == null || str == string.Empty)
109             {
110                 if(canBeEmpty)
111                 {
112                     return true;
113                 }
114                 else
115                 {
116                     return false;
117                 }
118             }
119             else
120             {
121                 if(str.Length <= maxLength)
122                 {
123                     return true;
124                 }
125 
126                 return false;
127             }
128         }
129 
130         #endregion
131 
132         #region SQL 命令(删除)
133 
134         /// <summary>
135         /// 获取 DELETE 命令,用以删除关键码等于命名参数@KeyName的值的记录。
136         /// </summary>
137         /// <returns>带命名参数的 DELETE 命令。</returns>
138         protected string GetDeleteText()
139         {
140             if(_deleteText != null)
141             {
142                 return _deleteText;
143             }
144 
145             StringBuilder sb = new StringBuilder();
146             sb.Append("DELETE FROM ");
147             sb.Append(TableName);
148             sb.Append(" WHERE ");
149             sb.Append(KeyName);
150             sb.Append("=@");
151             sb.Append(KeyName);
152 
153             return _deleteText = sb.ToString();
154         }
155 
156         /// <summary>
157         /// 带命名参数的 DELETE 命令。
158         /// </summary>
159         private string _deleteText = null;
160 
161         #endregion
162 
163     }
164 


[
代码12:XxxModel.cs]

ContractedBlock.gif ExpandedBlockStart.gif StationModel
  1     /// <summary>
  2     /// [车站]的数据模型。
  3     /// 用于操作数据表[Station]。
  4     /// </summary>
  5     [Serializable]
  6     public class StationModel : TableModel
  7     {
  8         #region 常量
  9 
 10         // 
 11         // 字段长度的定义。
 12         // 和数据库中相应字段的长度一致。
 13         // 用于参数校验。
 14         //
 15 
 16         public const int CzmcMaxLength = 50;
 17 
 18         #endregion
 19 
 20         #region 构造函数
 21 
 22         /// <summary>
 23         /// 构造函数。
 24         /// </summary>
 25         /// <param name="dbConfig">数据库配置。</param>
 26         public StationModel(DatabaseConfig dbConfig)
 27             : base(dbConfig)
 28         {
 29         }
 30 
 31         #endregion
 32 
 33         #region 属性
 34 
 35         /// <summary>
 36         /// 表名。
 37         /// </summary>
 38         public override string TableName
 39         {
 40             get
 41             {
 42                 return "Station";
 43             }
 44         }
 45 
 46         /// <summary>
 47         /// 关键码名。
 48         /// </summary>
 49         public override string KeyName
 50         {
 51             get
 52             {
 53                 return "stationID";
 54             }
 55         }
 56 
 57         /// <summary>
 58         /// 该表的所有列的列名。
 59         /// 用逗号隔开。用于 SELECT 语句中。
 60         /// </summary>
 61         /// <remarks>
 62         /// 目的:避免使用SELECT * 。
 63         /// </remarks>
 64         public override string ColumnNames
 65         {
 66             get
 67             {
 68                 return "stationID,czmc";
 69             }
 70         }
 71 
 72         public override string InitalSelectCommandText
 73         {
 74             get
 75             {
 76                 return GetSelectText();
 77             }
 78         }
 79         #endregion
 80 
 81         #region 添加INSERT
 82 
 83         /// <summary>
 84         /// 添加一条记录。
 85         /// </summary>
 86         /// <param name="czmc">数据项名称</param>
 87         /// <returns>
 88         /// 包含三个元素的string数组。
 89         /// [0]:"warning" | "fail" | "success",分别指示警告,失败,成功。
 90         /// [1]:失败时,指示失败的原因(即异常信息) ; 成功时,指示新记录的关键码。
 91         /// [2]:执行的SQL语句。
 92         /// </returns>
 93         public string[] Insert(
 94             string czmc
 95             )
 96         {
 97             #region 1、参数检查
 98 
 99             if(!Check(czmc))
100             {
101                 return new string[] { "fail""参数不合法,参数检查没通过。""" };
102             }
103 
104             #endregion
105 
106             long id = GetNextId(); // 获取关键码。
107 
108             #region 2、生成 INSERT 语句
109 
110             StringBuilder sb = new StringBuilder();
111 
112             sb.Append("INSERT INTO ");
113             sb.Append(TableName);
114             sb.Append("(");
115             sb.Append(KeyName);
116             sb.Append(",czmc) ");
117             sb.Append("VALUES(@");
118             sb.Append(KeyName);
119             sb.Append(",@czmc)");
120 
121             #endregion
122 
123             #region 3、执行
124 
125             DbCommand cmd = GetCommand();
126             cmd.CommandText = sb.ToString();
127             cmd.CommandType = CommandType.Text;
128 
129             SetParameters(cmd, id, czmc);
130 
131             int affectedRows = -1;
132             try
133             {
134                 affectedRows = cmd.ExecuteNonQuery();
135             }
136             catch(Exception ex)
137             {
138                 return new string[] { "fail", ex.ToString(), cmd.CommandText };
139             }
140 
141             #endregion
142 
143             #region 4、更新内存表
144             if(_table != null)
145             {
146                 try
147                 {
148                     DataRow newRow = _table.NewRow();
149                     newRow[KeyName] = id;
150 
151                     SetRow(newRow, czmc);
152 
153                     _table.Rows.Add(newRow);
154                     _table.AcceptChanges();
155                 }
156                 catch(Exception ex) // 数据库已经修改成功,但是内存表更新失败。
157                 {
158                     return new string[] { "warning", ex.ToString(), cmd.CommandText };
159                 }
160                 finally
161                 {
162                 }
163             }
164             #endregion
165 
166             return new string[] { "success", Convert.ToString(id), cmd.CommandText };
167         }
168 
169 
170         #endregion
171 
172         #region 修改UPDATE
173 
174         /// <summary>
175         /// 添加一条记录。
176         /// </summary>
177         /// <param name="id">待修改记录的主键码。</param>
178         /// <param name="czmc">数据项名称</param>
179         /// <returns>
180         /// 包含三个元素的string数组。
181         /// [0]:"warning" | "fail" | "success",分别指示警告,失败,成功。
182         /// [1]:失败时,指示失败的原因(即异常信息) ; 成功时,指示被修改记录的关键码。
183         /// [2]:执行的SQL语句。
184         /// </returns>
185         public string[] Update(
186             long id,
187             string czmc
188             )
189         {
190             #region 1、参数检查
191 
192             if(!Check(czmc))
193             {
194                 return new string[] { "fail""参数不合法,参数检查没通过。""" };
195             }
196 
197             #endregion
198 
199             #region 2、生成UPDATE语句
200 
201             StringBuilder sb = new StringBuilder();
202 
203             sb.Append("UPDATE ");
204             sb.Append(TableName);
205             sb.Append(" SET czmc=@czmc ");
206             sb.Append(" WHERE ");
207             sb.Append(KeyName);
208             sb.Append("=@" + KeyName);
209 
210             #endregion
211 
212             #region 3、执行
213 
214             DbCommand cmd = GetCommand();
215             cmd.CommandText = sb.ToString();
216             cmd.CommandType = CommandType.Text;
217 
218             SetParameters(cmd, id, czmc);
219 
220             int affectedRows = -1;
221             try
222             {
223                 affectedRows = cmd.ExecuteNonQuery();
224             }
225             catch(Exception ex)
226             {
227                 return new string[] { "fail", ex.ToString(), cmd.CommandText };
228             }
229 
230             #endregion
231 
232             #region 4、更新内存表
233 
234             if(_table != null)
235             {
236                 try
237                 {
238                     DataRow[] rows = _table.Select(KeyName + "=" + Convert.ToString(id));
239 
240                     if(rows.Length == 1)
241                     {
242                         DataRow row = rows[0];
243 
244                         SetRow(row, czmc);
245 
246                         _table.AcceptChanges();
247                     }
248                     else
249                     {
250                         throw new Exception("表[" + TableName + "]中,关键码[" + Convert.ToString(id) + "]重复或为0,数量:" + Convert.ToString(rows.Length) + "");
251                     }
252                 }
253                 catch(Exception ex) // 数据库已经修改成功,但是内存表更新失败。
254                 {
255                     return new string[] { "warning", ex.ToString(), cmd.CommandText };
256                 }
257                 finally
258                 {
259                 }
260             }
261             #endregion
262 
263             return new string[] { "success", Convert.ToString(id), cmd.CommandText };
264         }
265 
266         #endregion
267 
268         #region 其他
269         /// <summary>
270         /// 为 Command 对象添加相应的参数并设定参数的值。
271         /// 一般用于 UPDATA 和 INSERT 两个操作。
272         /// 注意:参数的名称必须相同。
273         /// </summary>
274         /// <param name="cmd">待设定的 Command 对象。</param>
275         /// <param name="id">记录的主键码。</param>
276         /// <param name="czmc">数据项名称</param>
277         /// <param name="description">描述说明</param>
278         /// <param name="address">PLC 地址</param>
279         /// <param name="dataType">PLC 中对应的数据类型。</param>
280         private void SetParameters(DbCommand cmd,
281             long id,
282             string czmc)
283         {
284             DbParameter idParam = GetParameter();
285             idParam.ParameterName = "@" + KeyName;
286             idParam.Value = id;
287             cmd.Parameters.Add(idParam);
288 
289             DbParameter czmcParam = GetParameter();
290             czmcParam.ParameterName = "@czmc";
291             czmcParam.Value = czmc;
292             cmd.Parameters.Add(czmcParam);
293         }
294 
295         /// <summary>
296         /// 设置 DataRow 的数据。
297         /// </summary>
298         /// <param name="row">待设定的 DataRow 对象。</param>
299         /// <param name="czmc">数据项名称</param>
300         /// <param name="description">描述说明</param>
301         /// <param name="address">PLC 地址</param>
302         /// <param name="dataType">PLC 中对应的数据类型。</param>
303         private static void SetRow(DataRow row, string czmc)
304         {
305             row["czmc"= czmc;
306         }
307         #endregion
308 
309         #region 参数检查
310 
311         //
312         // 1、检查是否符合数据库中字段类型长度等。
313         // 2、根据业务规则,检查参数的值是否合法。可以使用正则表达式等工具进行检查。
314         //
315 
316         /// <summary>
317         /// 检查车站名称czmc是否可以成功保存到数据库。
318         /// </summary>
319         /// <param name="czmc">车站名称。</param>
320         /// <returns>如果此方法成功,则为 true;否则为 false。 </returns>
321         public bool CheckCzmc(string czmc)
322         {
323             return CheckString(czmc, false, CzmcMaxLength);
324         }
325 
326         /// <summary>
327         /// 检查参数组合是否合法。
328         /// </summary>
329         /// <param name="czmc">数据项名称</param>
330         /// <param name="description">描述说明</param>
331         /// <param name="address">PLC 地址</param>
332         /// <param name="dataType">PLC 中对应的数据类型。</param>
333         /// <returns>如果此方法成功,则为 true;否则为 false。</returns>
334         public bool Check( string czmc )
335         {
336             if(CheckCzmc(czmc))
337             {
338                 return true;
339             }
340 
341             return false;
342         }
343 
344         #endregion
345     }
346 

从上面的代码可以看出,所有的

SQL 语句都到了 Model 类中,业务逻辑层和应用程序层都不需要书写 SQL 语句,减轻了数据库变化带来的影响。

10、结束语

吁~,终于写完了。以上是我的设计,很多都没有考虑到,比如性能啊等等,因为水平有限,其中有很多问题和错误,请大家的评点和指教,谢谢!

 

转载于:https://www.cnblogs.com/zhangleixp/archive/2006/09/07/DataAccessLayerDesign.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值