基於.NET平台的分層架構實戰(七-外一篇)—對數據訪問層第一種實現(Access+SQL)的重構
昨天的文章基於.NET平台的分層架構實戰(七)——數據訪問層的第一種實現:Access+SQL發布後,很多朋友對我的程序提出了意見和建議,在這裡先謝謝你們!!!尤其是金色海洋(jyk),對我的程序提出了很多建設性的意見。
我大體總結了一下,昨天程序的主要缺點有:
1.Connection對象沒有關閉
2.DataReader對象沒有關閉
3.相似代碼太多,造成代碼冗余。
其中第一點問題,目前還沒有太好的解決方案,主要是因為Connection一旦關閉,DataReader就無法讀取了。而且,Connection對象應該會自動在適當的時候關閉(通過觀察臨時文件得出),並且在實際運行時並無影響(在功能上),所以這裡沒有專門解決。而針對後面兩個問題,我使用了如下解決方案。
對於關閉DataReader的方法,實現起來很簡單,在finally裡將他關閉就行了。關鍵是如何去處冗余代碼。
經過我的分析,數據訪問層的操作可以分為三類:不返回數據,返回單個實體類,返回實體類集合。我將這三種操作的公共部分抽出,寫成三個方法放在AccessDALHelper裡,類型不同的問題使用泛型解決。
這樣做有一個難題,就是不同實體在由DataReader轉化為實體類時的代碼很不一樣,無法抽出。這裡,我使用了Strategy模式解決。具體做法是:首先定義一個由DataReader轉換為實體類的策略接口,然後為不同的實體編寫不同的轉換策略,示意圖如下:
可以看出,所有轉換策略都要實現IDataReaderToEntityStrategy接口,並且每個策略都有一個自組合,這是以為他們都要實現Singleton模式。而AccessDALHelper與具體策略無關,僅與接口耦合。
下面來看一下具體代碼:
首先是IDataReaderToEntityStrategy接口
IDataReaderToEntityStrategy.cs:
IDataReaderToEntityStrategy
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Data;
5using System.Data.OleDb;
6
7namespace NGuestBook.AccessDAL
8{
9 /**//// <summary>
10 /// 由DataReader轉換成實體類的策略接口
11 /// </summary>
12 public interface IDataReaderToEntityStrategy<T>
13 {
14 /**//// <summary>
15 /// 將DataReader轉換為實體類,采用泛型
16 /// </summary>
17 /// <param name="dataReader">包含數據的DataReader對象</param>
18 /// <returns>實體類</returns>
19 T DataReaderToEntity(OleDbDataReader dataReader);
20 }
21}
然後以Admin為例,看一下策略的具體實現:
AdminDataReaderToEntityStrategy.cs:
AdminDataReaderToEntityStrategy
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Data;
5using System.Data.OleDb;
6using NGuestBook.Entity;
7
8namespace NGuestBook.AccessDAL
9{
10 /**//// <summary>
11 /// DataReader到實體類的轉換策略-管理員
12 /// 實現上使用Singleton模式,保證全局唯一實例
13 /// </summary>
14 public class AdminDataReaderToEntityStrategy : IDataReaderToEntityStrategy<AdminInfo>
15 {
16 private static AdminDataReaderToEntityStrategy singleInstance = null;
17
18 /**//// <summary>
19 /// 私有化構造函數,保證無法外部實例化
20 /// </summary>
21 private AdminDataReaderToEntityStrategy() { }
22
23 /**//// <summary>
24 /// 靜態方法,用於取得全局唯一實例
25 /// </summary>
26 /// <returns>全局唯一實例</returns>
27 public static AdminDataReaderToEntityStrategy GetInstance()
28 {
29 if (singleInstance == null)
30 {
31 singleInstance = new AdminDataReaderToEntityStrategy();
32 }
33
34 return singleInstance;
35 }
36
37 /**//// <summary>
38 /// 由DataReader轉換到管理員實體類
39 /// </summary>
40 /// <param name="dataReader">包含數據的DataReader對象</param>
41 /// <returns>管理員實體類</returns>
42 public AdminInfo DataReaderToEntity(OleDbDataReader dataReader)
43 {
44 AdminInfo admin = new AdminInfo();
45 admin.ID = (int)dataReader["ID"];
46 admin.Name = (string)dataReader["Name"];
47 admin.Password = (string)dataReader["Password"];
48
49 return admin;
50 }
51 }
52}
可以看到,這裡實現了一個單件模式。下一個,是重構後的AccessDALHelper,增加了三個方法。
AccessDALHelper.cs:
AccessDALHelper
1using System;
2using System.Collections.Generic;
3using System.Web;
4using System.Web.Caching;
5using System.Configuration;
6using System.Data;
7using System.Data.OleDb;
8using NGuestBook.Utility;
9
10namespace NGuestBook.AccessDAL
11{
12 /**//// <summary>
13 /// Access數據庫操作助手
14 /// </summary>
15 public sealed class AccessDALHelper
16 {
17 /**//// <summary>
18 /// 讀取Access數據庫的連接字符串
19 /// 首先從緩存裡讀取,如果不存在則到配置文件中讀取,並放入緩存
20 /// </summary>
21 /// <returns>Access數據庫的連接字符串</returns>
22 private static string GetConnectionString()
23 {
24 if (CacheAccess.GetFromCache("AccessConnectionString") != null)
25 {
26 return CacheAccess.GetFromCache("AccessConnectionString").ToString();
27 }
28 else
29 {
30 string dbPath = ConfigurationManager.AppSettings["AccessPath"];
31 string dbAbsolutePath = HttpContext.Current.Server.MapPath(dbPath);
32 string connectionString = ConfigurationManager.AppSettings["AccessConnectionString"];
33
34 CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config"));
35 CacheAccess.SaveToCache("AccessConnectionString", connectionString.Replace("{DBPath}", dbAbsolutePath), fileDependency);
36
37 return connectionString.Replace("{DBPath}", dbAbsolutePath);
38 }
39 }
40
41 /**//// <summary>
42 /// 執行SQL語句並且不返回任何值
43 /// </summary>
44 /// <param name="SQLCommand">所執行的SQL命令</param>
45 /// <param name="parameters">參數集合</param>
46 public static void ExecuteSQLNonQuery(string SQLCommand, OleDbParameter[] parameters)
47 {
48 OleDbConnection connection = new OleDbConnection(GetConnectionString());
49 OleDbCommand command = new OleDbCommand(SQLCommand, connection);
50
51 for (int i = 0; i < parameters.Length; i++)
52 {
53 command.Parameters.Add(parameters[i]);
54 }
55
56 connection.Open();
57 command.ExecuteNonQuery();
58 connection.Close();
59 }
60
61 /**//// <summary>
62 /// 執行SQL語句並返回包含查詢結果的DataReader
63 /// </summary>
64 /// <param name="SQLCommand">所執行的SQL命令</param>
65 /// <param name="parameters">參數集合</param>
66 /// <returns></returns>
67 public static OleDbDataReader ExecuteSQLDataReader(string SQLCommand, OleDbParameter[] parameters)
68 {
69 OleDbConnection connection = new OleDbConnection(GetConnectionString());
70 OleDbCommand command = new OleDbCommand(SQLCommand, connection);
71
72 for (int i = 0; i < parameters.Length; i++)
73 {
74 command.Parameters.Add(parameters[i]);
75 }
76
77 connection.Open();
78 OleDbDataReader dataReader = command.ExecuteReader();
79 //connection.Close();
80
81 return dataReader;
82 }
83
84 /**//// <summary>
85 /// 執行不需要返回數據的操作
86 /// </summary>
87 /// <param name="SQLCommand">SQL命令</param>
88 /// <param name="parameters">參數</param>
89 /// <returns>是否成功</returns>
90 public static bool OperateNonData(string SQLCommand, OleDbParameter[] parameters)
91 {
92 try
93 {
94 ExecuteSQLNonQuery(SQLCommand, parameters);
95 return true;
96 }
97 catch
98 {
99 return false;
100 }
101 }
102
103 /**//// <summary>
104 /// 執行返回單個實體類的操作
105 /// </summary>
106 /// <typeparam name="T">實體類類型</typeparam>
107 /// <param name="SQLCommand">SQL命令</param>
108 /// <param name="parameters">參數</param>
109 /// <param name="strategy">DataReader到實體類的轉換策略</param>
110 /// <returns>實體類</returns>
111 public static T OperateEntity<T>(string SQLCommand, OleDbParameter[] parameters, IDataReaderToEntityStrategy<T> strategy)
112 {
113 OleDbDataReader dataReader = ExecuteSQLDataReader(SQLCommand, parameters);
114 try
115 {
116 if (!dataReader.HasRows)
117 {
118 throw new Exception();
119 }
120
121 dataReader.Read();
122 return strategy.DataReaderToEntity(dataReader);
123 }
124 catch
125 {
126 return default(T);
127 }
128 finally
129 {
130 dataReader.Close();
131 }
132 }
133
134 /**//// <summary>
135 /// 執行返回實體類集合的操作
136 /// </summary>
137 /// <typeparam name="T">實體類類型</typeparam>
138 /// <param name="SQLCommand">SQL命令</param>
139 /// <param name="parameters">參數</param>
140 /// <param name="strategy">DataReader到實體類的轉換策略</param>
141 /// <returns>實體類</returns>
142 public static IList<T> OperateEntityCollection<T>(string SQLCommand, OleDbParameter[] parameters, IDataReaderToEntityStrategy<T> strategy)
143 {
144 OleDbDataReader dataReader = AccessDALHelper.ExecuteSQLDataReader(SQLCommand, null);
145 try
146 {
147 if (!dataReader.HasRows)
148 {
149 throw new Exception();
150 }
151
152 IList<T> entityCollection = new List<T>();
153 int i = 0;
154 while (dataReader.Read())
155 {
156 entityCollection.Add(strategy.DataReaderToEntity(dataReader));
157 i++;
158 }
159
160 return entityCollection;
161 }
162 catch
163 {
164 return default(IList<T>);
165 }
166 finally
167 {
168 dataReader.Close();
169 }
170 }
171 }
172}
最後以Admin為例,看一下簡化後的數據訪問層實現:
AdminDAL.cs:
AdminDAL1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Data;
5using System.Data.OleDb;
6using NGuestBook.IDAL;
7using NGuestBook.Entity;
8
9namespace NGuestBook.AccessDAL
10{
11 public class AdminDAL : IAdminDAL
12 {
13 /**//// <summary>
14 /// 插入管理員
15 /// </summary>
16 /// <param name="admin">管理員實體類</param>
17 /// <returns>是否成功</returns>
18 public bool Insert(AdminInfo admin)
19 {
20 string SQLCommand = "insert into [TAdmin]([Name],[Password]) values(@name,@password)";
21 OleDbParameter[] parameters ={
22 new OleDbParameter("name",OleDbType.VarChar,20),
23 new OleDbParameter("password",OleDbType.VarChar,50)
24 };
25 parameters[0].Value = admin.Name;
26 parameters[1].Value = admin.Password;
27
28 return AccessDALHelper.OperateNonData(SQLCommand, parameters);
29 }
30
31 /**//// <summary>
32 /// 刪除管理員
33 /// </summary>
34 /// <param name="id">欲刪除的管理員的ID</param>
35 /// <returns>是否成功</returns>
36 public bool Delete(int id)
37 {
38 string SQLCommand = "delete from [TAdmin] where [ID]=@id";
39 OleDbParameter[] parameters ={
40 new OleDbParameter("id",OleDbType.Integer)
41 };
42 parameters[0].Value = id;
43
44 return AccessDALHelper.OperateNonData(SQLCommand, parameters);
45 }
46
47 /**//// <summary>
48 /// 更新管理員信息
49 /// </summary>
50 /// <param name="admin">管理員實體類</param>
51 /// <returns>是否成功</returns>
52 public bool Update(AdminInfo admin)
53 {
54 string SQLCommand = "update [TAdmin] set [Name]=@name,[Password]=@password where [ID]=@id";
55 OleDbParameter[] parameters ={
56 new OleDbParameter("id",OleDbType.Integer),
57 new OleDbParameter("name",OleDbType.VarChar,20),
58 new OleDbParameter("password",OleDbType.VarChar,50)
59 };
60 parameters[0].Value = admin.ID;
61 parameters[1].Value = admin.Name;
62 parameters[2].Value = admin.Password;
63
64 return AccessDALHelper.OperateNonData(SQLCommand, parameters);
65 }
66
67 /**//// <summary>
68 /// 按ID取得管理員信息
69 /// </summary>
70 /// <param name="id">管理員ID</param>
71 /// <returns>管理員實體類</returns>
72 public AdminInfo GetByID(int id)
73 {
74 string SQLCommand = "select * from [TAdmin] where [ID]=@id";
75 OleDbParameter[] parameters ={
76 new OleDbParameter("id",OleDbType.Integer)
77 };
78 parameters[0].Value = id;
79
80 return AccessDALHelper.OperateEntity<AdminInfo>(SQLCommand, parameters, AdminDataReaderToEntityStrategy.GetInstance());
81 }
82
83 /**//// <summary>
84 /// 按用戶名及密碼取得管理員信息
85 /// </summary>
86 /// <param name="name">用戶名</param>
87 /// <param name="password">密碼</param>
88 /// <returns>管理員實體類,不存在時返回null</returns>
89 public AdminInfo GetByNameAndPassword(string name, string password)
90 {
91 string SQLCommand = "select * from [TAdmin] where [Name]=@name and [Password]=@password";
92 OleDbParameter[] parameters ={
93 new OleDbParameter("name",OleDbType.VarChar,20),
94 new OleDbParameter("password",OleDbType.VarChar,50)
95 };
96 parameters[0].Value = name;
97 parameters[1].Value = password;
98
99 return AccessDALHelper.OperateEntity<AdminInfo>(SQLCommand, parameters, AdminDataReaderToEntityStrategy.GetInstance());
100 }
101
102 /**//// <summary>
103 /// 按管理員名取得管理員信息
104 /// </summary>
105 /// <param name="name">管理員名</param>
106 /// <returns>管理員實體類</returns>
107 public AdminInfo GetByName(string name)
108 {
109 string SQLCommand = "select * from [TAdmin] where [Name]=@name";
110 OleDbParameter[] parameters ={
111 new OleDbParameter("name",OleDbType.VarChar,20)
112 };
113 parameters[0].Value = name;
114
115 return AccessDALHelper.OperateEntity<AdminInfo>(SQLCommand, parameters, AdminDataReaderToEntityStrategy.GetInstance());
116 }
117
118 /**//// <summary>
119 /// 取得全部管理員信息
120 /// </summary>
121 /// <returns>管理員實體類集合</returns>
122 public IList<AdminInfo> GetAll()
123 {
124 string SQLCommand = "select * from [TAdmin]";
125
126 return AccessDALHelper.OperateEntityCollection<AdminInfo>(SQLCommand, null, AdminDataReaderToEntityStrategy.GetInstance());
127 }
128 }
129}