原版英文文章地址:http://www.codeproject.com/csharp/three_tier_architecture.asp
介绍
这篇文章讨论如何在c#中实现3层架构,使用MS Access数据库存储数据。在此,我在3层架构中实现一个小型的可复用的组件保存客户数据。并提供添加,更新,查找客户数据的功能。
背景
首先,我介绍一些3层架构的理论知识。简单说明:什么是3层架构?3层架构的优点是什么?
什么是3层架构?
3层架构是一种“客户端-服务器”架构,在此架构中用户接口,商业逻辑,数据保存以及数据访问被设计为独立的模块。主要有3个层面,第一层(表现层,GUI层),第二层(商业对象,商业逻辑层),第三层(数据访问层)。这些层可以单独开发,单独测试。
为什么要把程序代码分为3层,把用户接口层,商业逻辑层,数据访问层分离有许多的优点。
在快速开发中重用商业逻辑组件,我们已经在系统中实现添加,更新,删除,查找客户数据的组件。这个组件已经开发并且测试通过,我们可以在其他要保存客户数据的项目中使用这个组件。
系统比较容易迁移,商业逻辑层与数据访问层是分离的,修改数据访问层不会影响到商业逻辑层。系统如果从用SQL Server存储数据迁移到用Oracle存储数据,并不需要修改商业逻辑层组件和GUI组件
系统容易修改,假如在商业层有一个小小的修改,我们不需要在用户的机器上重装整个系统。我们只需要更新商业逻辑组件就可以了。
应用程序开发人员可以并行,独立的开发单独的层。
代码
这个组件有3层,第一个层或者称为GUI层用form实现,叫做FrmGUI。第二层或者称为商业逻辑层,叫做BOCustomer,是Bussniess Object Customer的缩写。最后是第三层或者称为数据层,叫做DACustomer,是Data Access Customer的缩写。为了方便我把三个层编译到一个项目中。
用户接口层
下面是用户接口成的一段代码,我只选取了调用商业逻辑层的一部分代码。


1
//This function get the details from the user via GUI
2
//tier and calls the Add method of business logic layer.
3
private void cmdAdd_Click(object sender, System.EventArgs e)
4

{
5
try
6
{
7
8
cus = new BOCustomer();
9
10
cus.cusID=txtID.Text.ToString();
11
12
cus.LName = txtLName.Text.ToString();
13
14
cus.FName = txtFName.Text.ToString();
15
16
cus.Tel= txtTel.Text.ToString();
17
18
cus.Address = txtAddress.Text.ToString();
19
20
cus.Add();
21
22
}
23
24
catch(Exception err)
25
26
{
27
28
MessageBox.Show(err.Message.ToString());
29
30
}
31
32
}
33
34
35
36
//This function gets the ID from the user and finds the
37
38
//customer details and return the details in the form of
39
40
//a dataset via busniss object layer. Then it loops through
41
42
//the content of the dataset and fills the controls.
43
44
45
46
private void cmdFind_Click(object sender, System.EventArgs e)
47
48

{
49
50
try
51
52
{
53
54
String cusID = txtID.Text.ToString();
55
56
57
58
BOCustomer thisCus = new BOCustomer();
59
60
61
62
DataSet ds = thisCus.Find(cusID);
63
64
65
66
DataRow row;
67
68
row = ds.Tables[0].Rows[0];
69
70
71
72
//via looping
73
74
foreach(DataRow rows in ds.Tables[0].Rows )
75
76
{
77
78
txtFName.Text = rows["CUS_F_NAME"].ToString();
79
80
txtLName.Text = rows["CUS_L_NAME"].ToString();
81
82
txtAddress.Text = rows["CUS_ADDRESS"].ToString();
83
84
txtTel.Text = rows["CUS_TEL"].ToString();
85
86
}
87
88
}
89
90
catch (Exception err)
91
92
{
93
94
MessageBox.Show(err.Message.ToString());
95
96
}
97
98
99
100
}
101
102
103
104
//this function used to update the customer details.
105
106
private void cmdUpdate_Click(object sender,
107
108
System.EventArgs e)
109
110

{
111
112
try
113
114
{
115
116
cus = new BOCustomer();
117
118
cus.cusID=txtID.Text.ToString();
119
120
cus.LName = txtLName.Text.ToString();
121
122
cus.FName = txtFName.Text.ToString();
123
124
cus.Tel= txtTel.Text.ToString();
125
126
cus.Address = txtAddress.Text.ToString();
127
128
129
130
cus.Update();
131
132
}
133
134
catch(Exception err)
135
136
{
137
138
MessageBox.Show(err.Message.ToString());
139
140
}
141
142
}
143
144
商业逻辑层
下面是商业逻辑层的所有代码,主要包括定义customer对象的属性。但这仅仅是个虚构的customer对象,如果需要可以加入其他的属性。商业逻辑层还包括添加,更新,查找,等方法。
商业逻辑层是一个中间层,处于GUI层和数据访问层中间。他有一个指向数据访问层的引用cusData = new DACustomer().而且还引用了System.Data名字空间。商业逻辑层使用DataSet返回数据给GUI层。


1
using System;
2
3
using System.Data;
4
5
6
7
namespace _3tierarchitecture
8
9

{
10
11
/**//// <SUMMARY>
12
13
/// Summary description for BOCustomer.
14
15
/// </SUMMARY>
16
17
18
19
public class BOCustomer
20
21
{
22
23
//Customer properties
24
25
private String fName;
26
27
private String lName;
28
29
private String cusId;
30
31
private String address;
32
33
private String tel;
34
35
36
37
private DACustomer cusData;
38
39
40
41
public BOCustomer()
42
43
{
44
45
//An instance of the Data access layer!
46
47
cusData = new DACustomer();
48
49
}
50
51
52
53
54
55
56
57
/**//// <SUMMARY>
58
59
/// Property FirstName (String)
60
61
/// </SUMMARY>
62
63
public String FName
64
65
{
66
67
68
69
get
70
71
{
72
73
return this.fName;
74
75
}
76
77
set
78
79
{
80
81
try
82
83
{
84
85
this.fName = value;
86
87
88
89
if (this.fName == "")
90
91
{
92
93
throw new Exception(
94
95
"Please provide first name
");
96
97
}
98
99
}
100
101
catch(Exception e)
102
103
{
104
105
throw new Exception(e.Message.ToString());
106
107
}
108
109
}
110
111
}
112
113
114
115
/**//// <SUMMARY>
116
117
/// Property LastName (String)
118
119
/// </SUMMARY>
120
121
public String LName
122
123
{
124
125
get
126
127
{
128
129
return this.lName;
130
131
}
132
133
set
134
135
{
136
137
//could be more checkings here eg revmove ' chars
138
139
//change to proper case
140
141
//blah blah
142
143
this.lName = value;
144
145
if (this.LName == "")
146
147
{
148
149
throw new Exception("Please provide name
");
150
151
}
152
153
154
155
}
156
157
}
158
159
160
161
/**//// <SUMMARY>
162
163
/// Property Customer ID (String)
164
165
/// </SUMMARY>
166
167
public String cusID
168
169
{
170
171
get
172
173
{
174
175
return this.cusId;
176
177
}
178
179
set
180
181
{
182
183
this.cusId = value;
184
185
if (this.cusID == "")
186
187
{
188
189
throw new Exception("Please provide ID
");
190
191
}
192
193
194
195
}
196
197
}
198
199
200
201
/**//// <SUMMARY>
202
203
/// Property Address (String)
204
205
/// </SUMMARY>
206
207
public String Address
208
209
{
210
211
get
212
213
{
214
215
return this.address;
216
217
}
218
219
set
220
221
{
222
223
this.address = value;
224
225
226
227
if (this.Address == "")
228
229
{
230
231
throw new Exception("Please provide address
");
232
233
}
234
235
}
236
237
}
238
239
240
241
/**//// <SUMMARY>
242
243
/// Property Telephone (String)
244
245
/// </SUMMARY>
246
247
public String Tel
248
249
{
250
251
get
252
253
{
254
255
return this.tel;
256
257
}
258
259
set
260
261
{
262
263
this.tel = value;
264
265
if (this.Tel == "")
266
267
{
268
269
throw new Exception("Please provide Tel
");
270
271
}
272
273
274
275
}
276
277
}
278
279
280
281
/**//// <SUMMARY>
282
283
/// Function Add new customer. Calls
284
285
/// the function in Data layer.
286
287
/// </SUMMARY>
288
289
public void Add()
290
291
{
292
293
cusData.Add(this);
294
295
}
296
297
298
299
300
301
/**//// <SUMMARY>
302
303
/// Function Update customer details.
304
305
/// Calls the function in Data layer.
306
307
/// </SUMMARY>
308
309
public void Update()
310
311
{
312
313
cusData.Update(this);
314
315
}
316
317
318
319
/**//// <SUMMARY>
320
321
/// Function Find customer. Calls the
322
323
/// function in Data layer.
324
325
/// It returns the details of the customer using
326
327
/// customer ID via a Dataset to GUI tier.
328
329
/// </SUMMARY>
330
331
public DataSet Find(String str)
332
333
{
334
335
if (str == "")
336
337
throw new Exception("Please provide ID to search");
338
339
340
341
DataSet data = null;
342
343
344
345
data = cusData.Find(str);
346
347
348
349
return data;
350
351
}
352
353
}
354
355
}
356
357
数据访问层
数据层包括处理MS Access数据库的细节。所有这些细节都是透明的,不会影响到商业逻辑层。数据访问层有个指向商业逻辑层的引用BOCustomer cus。为了应用方便并且支持其他数据库。


1
using System;
2
3
using System.Data.OleDb;
4
5
using System.Data;
6
7
8
9
namespace _3tierarchitecture
10
11
12
13

{
14
15
16
17
/**//// <SUMMARY>
18
19
/// Summary description for DACustomer.
20
21
/// </SUMMARY>
22
23
public class DACustomer
24
25
{
26
27
private OleDbConnection cnn;
28
29
//change connection string as per the
30
31
//folder you unzip the files
32
33
private const string CnnStr =
34
35
"Provider=Microsoft.Jet.OLEDB.4.0;Data " +
36
37
"Source= D:\\Rahman_Backup\\Programming\\" +
38
39
"Csharp\\3tierarchitecture\\customer.mdb;";
40
41
42
43
//local variables
44
45
private String strTable="";
46
47
private String strFields="";
48
49
private String strValues="";
50
51
private String insertStr="";
52
53
54
55
//this needs to be changed based on customer
56
57
//table fields' Name of the database!
58
59
private const String thisTable = "tblCustomer";
60
61
private const String cus_ID = "CUS_ID";
62
63
private const String cus_LName = "CUS_L_NAME";
64
65
private const String cus_FName = "CUS_F_NAME";
66
67
private const String cus_Tel = "CUS_TEL";
68
69
private const String cus_Address = "CUS_ADDRESS";
70
71
72
73
74
75
public DACustomer()
76
77
{
78
79
}
80
81
82
83
public DACustomer(BOCustomer cus)
84
85
{
86
87
// A reference of the business object class
88
89
}
90
91
92
93
//standard dataset function that adds a new customer
94
95
96
97
public void Add(BOCustomer cus)
98
99
{
100
101
102
103
String str = BuildAddString(cus);
104
105
106
107
OpenCnn();
108
109
110
111
//Open command option - cnn parameter is imporant
112
113
OleDbCommand cmd = new OleDbCommand(str,cnn);
114
115
116
117
118
119
//execute connection
120
121
cmd.ExecuteNonQuery();
122
123
124
125
// close connection
126
127
CloseCnn();
128
129
130
131
}
132
133
134
135
//standard dataset function that updates
136
137
//details of a customer based on ID
138
139
public void Update(BOCustomer cus)
140
141
{
142
143
OpenCnn();
144
145
146
147
String selectStr = "UPDATE " + thisTable +
148
149
" set " + cus_LName + " = '" + cus.LName + "'" +
150
151
", " + cus_FName + " = '" + cus.FName + "'" +
152
153
", " + cus_Address + " = '" + cus.Address + "'" +
154
155
", " + cus_Tel + " = '" + cus.Tel + "'" +
156
157
" where cus_ID = '" + cus.cusID + "'";
158
159
160
161
OleDbCommand cmd = new OleDbCommand(selectStr,cnn);
162
163
164
165
cmd.ExecuteNonQuery();
166
167
168
169
CloseCnn();
170
171
}
172
173
174
175
//standard dataset function that finds and
176
177
//return the detail of a customer in a dataset
178
179
public DataSet Find(String argStr)
180
181
{
182
183
DataSet ds=null;
184
185
186
187
try
188
189
{
190
191
OpenCnn();
192
193
194
195
String selectStr = "select * from " + thisTable +
196
197
" where cus_ID = '" + argStr + "'";
198
199
OleDbDataAdapter da =
200
201
new OleDbDataAdapter(selectStr,cnn);
202
203
ds = new DataSet();
204
205
da.Fill(ds,thisTable);
206
207
208
209
CloseCnn();
210
211
212
213
214
215
}
216
217
catch(Exception e)
218
219
{
220
221
String Str = e.Message;
222
223
}
224
225
226
227
return ds;
228
229
}
230
231
232
233
private void OpenCnn()
234
235
{
236
237
// initialise connection
238
239
String cnnStr = CnnStr;
240
241
cnn = new OleDbConnection(cnnStr);
242
243
// open connection
244
245
cnn.Open();
246
247
}
248
249
250
251
private void CloseCnn()
252
253
{
254
255
// 5- step five
256
257
cnn.Close();
258
259
}
260
261
262
263
// just a supporting function that builds
264
265
// and return the insert string for dataset.
266
267
private String BuildAddString(BOCustomer cus)
268
269
{
270
271
// these are the constants as
272
273
// set in the top of this module.
274
275
strTable="Insert into " + thisTable;
276
277
strFields=" (" + cus_ID +
278
279
"," + cus_LName +
280
281
"," + cus_FName +
282
283
"," + cus_Address +
284
285
"," + cus_Tel + ")";
286
287
288
289
//these are the attributes of the
290
291
//customer business object.
292
293
strValues= " Values ( '" + cus.cusID +
294
295
"' , '" + cus.LName +
296
297
"' , '" + cus.FName +
298
299
"' , '" + cus.Address +
300
301
"' , '" + cus.Tel + "' )";
302
303
304
305
insertStr = strTable + strFields + strValues;
306
307
308
309
return insertStr;
310
311
312
313
}
314
315
}
316
317
}
318