1.Introducing MVC
The Model-View-Controller pattern is certainly nothing new. It has been around for decades. There have been many variations of it and many articles, book chapters, and blog posts written about it. The original impetus for the pattern was to help intelligently structure an application to handle low-level user input, back when UI controls were quite primitive. As the developer-friendliness and richness of UI controls has improved over time, MVC has taken on broader meaning.
These days MVC generally refers to the separation of concerns wherein you have a Model to represent the logic and data of the problem domain, a View to display the Model on-screen, and a Controller to handle user interaction events and inform the Model when processing needs to occur. The Controller is interested in user interaction; such as keyboard, mouse, or stylus input; for an entire form or user control…not just one button or dropdown.
Now that I have regurgitated a generic explanation of MVC, let us see what it really means in practice. Previously we saw how loosely coupling application logic to the UI has many desirable advantages. What we did not review was how to implement that loose coupling. As you have probably guessed by now, the MVC pattern is a fantastic way to do it.
The essence of a loosely coupled system boils down to one (bizarre) term that I coined: referencelessness. A loosely coupled system does not have unnecessary object references directly connecting its major functional components. Where there are direct references between parts of the system, they should often be expressed as interfaces or abstract base classes, to allow for dependency injection and object mocking (more on that later). The only places in the system where it makes sense to have a direct reference between two components is where the Controller references the Model, and possibly between the Controller and View in certain situations. As we examine how the demo application uses MVC, the reasoning behind these statements will become apparent.
2.My Demo
In my demo application, I write one application to make it clear. Assuming we have a table, as it is shown in the following.
Student Information Table
(Field) |
(Type) |
StuId(Primary Key) |
Int |
StuNo |
Char(8) |
StuName |
Varchar(16) |
Sex |
Char(1) |
Brithday |
Datetime |
Address |
Varchar(64) |
Telphone |
Varchar(16) |
Remark |
Varchar(128) |
We want to operate this Student Information Table with one application based MVC pattern. It can add a record, modify a record, delete a record, and show all the record.
I write the Model Layer like this. It’s very easy, isn’t it? :-)
1namespace Model
2{
3 public class StudentInfo
4 {
5 private string _stuNo;
6 private string _stuName;
7 private string _sex;
8 private DateTime _brithday;
9 private string _address;
10 private string _telphone;
11 private string _remark;
12
13 public string StuNo
14 {
15 get
16 {
17 return _stuNo;
18 }
19 set
20 {
21 _stuNo = value;
22 }
23 }
24 public string StuName
25 {
26 get
27 {
28 return _stuName;
29 }
30 set
31 {
32 _stuName = value;
33 }
34 }
35 public string Sex
36 {
37 get
38 {
39 return _sex;
40 }
41 set
42 {
43 _sex = value;
44 }
45 }
46 public DateTime Brithday
47 {
48 get
49 {
50 return _brithday;
51 }
52 set
53 {
54 _brithday = value;
55 }
56 }
57 public string Address
58 {
59 get
60 {
61 return _address;
62 }
63 set
64 {
65 _address = value;
66 }
67 }
68 public string Telphone
69 {
70 get
71 {
72 return _telphone;
73 }
74 set
75 {
76 _telphone = value;
77 }
78 }
79 public string Remark
80 {
81 get
82 {
83 return _remark;
84 }
85 set
86 {
87 _remark = value;
88 }
89 }
90 }
91}
92
And then I write the Controller Layer like this.
1sing System;
2using System.Collections.Generic;
3using System.Text;
4using System.Data;
5using System.Data.SqlClient;
6using Model;
7namespace Manage
8{
9 public class StudentManage
10 {
11 SqlConnection sqlCon;
12 public StudentManage(string strCon)
13 {
14 sqlCon = new SqlConnection(strCon);
15 }
16 Get all Students’ Informathion#region Get all Students’ Informathion
17 public DataTable GetAllStudent(out string strInfo)
18 {
19 strInfo = "";
20 DataTable objDataTable = new DataTable();
21 SqlCommand sqlCom = new SqlCommand();
22 sqlCom.CommandText = "select * from info";
23 sqlCom.Connection = sqlCon;
24 SqlDataAdapter sqlDa = new SqlDataAdapter(sqlCom);
25 try
26 {
27 sqlDa.Fill(objDataTable);
28 if (objDataTable.Rows.Count == 0)
29 {
30 strInfo = "No Record";
31 }
32 }
33 catch
34 {
35 strInfo = "Can Not Find Database";
36 }
37 finally
38 {
39 sqlDa.Dispose();
40 sqlCom.Dispose();
41 }
42 return objDataTable;
43 }
44 #endregion
45 Get Student’s Information Via Student’s NO#region Get Student’s Information Via Student’s NO
46 public DataTable GetStudentByNo(string strStuNo,out string strInfo)
47 {
48 strInfo = "";
49 DataTable objDataTable = new DataTable();
50 SqlCommand sqlCom = new SqlCommand();
51
52 sqlCom.CommandText = "select * from info where StuNo like '%"+strStuNo+"%'";
53 sqlCom.Connection = sqlCon;
54 SqlDataAdapter sqlDa = new SqlDataAdapter(sqlCom);
55 try
56 {
57 sqlDa.Fill(objDataTable);
58 if (objDataTable.Rows.Count == 0)
59 {
60 strInfo = "No Record";
61 }
62 }
63 catch
64 {
65 strInfo = "Failure";
66 }
67 finally
68 {
69 sqlDa.Dispose();
70 sqlCom.Dispose();
71 }
72 return objDataTable;
73 }
74 #endregion
75 Get Student’s Information Via Student’s Name#region Get Student’s Information Via Student’s Name
76 public DataTable GetStudentByName(string strStuName, out string strInfo)
77 {
78 strInfo = "";
79 DataTable objDataTable = new DataTable();
80 SqlCommand sqlCom = new SqlCommand();
81 sqlCom.CommandText = "select * from info where StuName like '%" + strStuName + "%'";
82 sqlCom.Connection = sqlCon;
83 SqlDataAdapter sqlDa = new SqlDataAdapter(sqlCom);
84 try
85 {
86 sqlDa.Fill(objDataTable);
87 if (objDataTable.Rows.Count == 0)
88 {
89 strInfo = "No Record";
90 }
91 }
92 catch
93 {
94 strInfo = "Failure";
95 }
96 finally
97 {
98 sqlDa.Dispose();
99 sqlCom.Dispose();
100 }
101 return objDataTable;
102 }
103
104 #endregion
105 Add Students’s Information#region Add Students’s Information
106 public void AddStudent(StudentInfo objStudent, out string strInfo)
107 {
108 strInfo = "";
109 SqlCommand sqlCom = new SqlCommand();
110 sqlCom.CommandText = "insert into info(StuNo,StuName,Sex,Brithday,Address,Telphone,Remark) values('" + objStudent.StuNo + "','" + objStudent.StuName + "','" + objStudent.Sex + "','" + objStudent.Brithday + "','" + objStudent.Address + "','" + objStudent.Telphone + "','" + objStudent.Remark + "')";
111 sqlCom.Connection = sqlCon;
112 try
113 {
114 sqlCon.Open();
115 sqlCom.ExecuteNonQuery();
116 strInfo = "Successful";
117 }
118 catch
119 {
120 strInfo = "Failure";
121 }
122 finally
123 {
124
125 sqlCom.Dispose();
126 sqlCon.Close();
127 }
128 }
129 #endregion
130 Modify Students’s Information#region Modify Students’s Information
131 public void ModifyStudent(StudentInfo objStudent,int stuId, out string strInfo)
132 {
133 strInfo = "";
134 SqlCommand sqlCom = new SqlCommand();
135 sqlCom.CommandText = "update info set StuNo='" + objStudent.StuNo + "',StuName='" + objStudent.StuName + "',Sex='" + objStudent.Sex + "',Brithday='" + objStudent.Brithday + "',Address='" + objStudent.Address + "',Telphone='" + objStudent.Telphone + "',Remark='" + objStudent.Remark + "'where StuId="+stuId;
136 sqlCom.Connection = sqlCon;
137 try
138 {
139 sqlCon.Open();
140 sqlCom.ExecuteNonQuery();
141 strInfo = "Successful";
142 }
143 catch
144 {
145 strInfo = "Failure";
146 }
147 finally
148 {
149
150 sqlCom.Dispose();
151 sqlCon.Close();
152 }
153 }
154 #endregion
155 Delete Students’ Information#region Delete Students’ Information
156 public void DeleteStudent( int stuId, out string strInfo)
157 {
158 strInfo = "";
159 SqlCommand sqlCom = new SqlCommand();
160 sqlCom.CommandText = "delete from info where StuId=" + stuId;
161 sqlCom.Connection = sqlCon;
162 try
163 {
164 sqlCon.Open();
165 sqlCom.ExecuteNonQuery();
166 strInfo = "Successful";
167 }
168 catch
169 {
170 strInfo = "Failure";
171 }
172 finally
173 {
174
175 sqlCom.Dispose();
176 sqlCon.Close();
177 }
178 }
179 #endregion
180
181 }
182}
183
Then I write some code to test this Controller Layer, of course, I use the Nunit Test to do this.
1// 以下代码由 Microsoft Visual Studio 2005 生成。
2// 测试所有者应该检查每个测试的有效性。
3using Microsoft.VisualStudio.TestTools.UnitTesting;
4using System;
5using System.Text;
6using System.Collections.Generic;
7using Manage;
8using Model;
9using System.Data;
10namespace Test
11{
12 /**////
13 ///这是 Manage.StudentManage 的测试类,旨在
14 ///包含所有 Manage.StudentManage 单元测试
15 ///
16 [TestClass()]
17 public class StudentManageTest
18 {
19
20
21 private TestContext testContextInstance;
22
23 /**////
24 ///获取或设置测试上下文,上下文提供
25 ///有关当前测试运行及其功能的信息。
26 ///
27 public TestContext TestContext
28 {
29 get
30 {
31 return testContextInstance;
32 }
33 set
34 {
35 testContextInstance = value;
36 }
37 }
38 附加测试属性#region 附加测试属性
39 //
40 //编写测试时,可使用以下附加属性:
41 //
42 //使用 ClassInitialize 在运行类中的第一个测试前先运行代码
43 //
44 //[ClassInitialize()]
45 //public static void MyClassInitialize(TestContext testContext)
46 //{
47 //}
48 //
49 //使用 ClassCleanup 在运行完类中的所有测试后再运行代码
50 //
51 //[ClassCleanup()]
52 //public static void MyClassCleanup()
53 //{
54 //}
55 //
56 //使用 TestInitialize 在运行每个测试前先运行代码
57 //
58 //[TestInitialize()]
59 //public void MyTestInitialize()
60 //{
61 //}
62 //
63 //使用 TestCleanup 在运行完每个测试后运行代码
64 //
65 //[TestCleanup()]
66 //public void MyTestCleanup()
67 //{
68 //}
69 //
70 #endregion
71
72
73 /**////
74 ///AddStudent (StudentInfo, out string) 的测试
75 ///
76 [TestMethod()]
77 public void AddStudentTest()
78 {
79 string strCon = "server=.;user id =sa;password=sa;database= StuInfo;"; // TODO: 初始化为适当的值
80
81 StudentManage target = new StudentManage(strCon);
82
83 StudentInfo objStudent = new StudentInfo (); // TODO: 初始化为适当的值
84 objStudent.StuNo = "20083111";
85 objStudent.StuName = "李四";
86 objStudent.Sex = "M";
87 objStudent.Brithday = DateTime.Parse("1984-07-07");
88 objStudent.Address = "湖北武汉";
89 objStudent.Telphone = "02787342182";
90 objStudent.Remark = "好学生!";
91 string strInfo="";
92 string strInfo_expected = "Successful"; // TODO: 初始化为适当的值
93
94 target.AddStudent(objStudent, out strInfo);
95
96 Assert.AreEqual(strInfo_expected, strInfo);
97
98 }
99
100
101
102 /**////
103 ///GetAllStudent (out string) 的测试
104 ///
105 [TestMethod()]
106 public void GetAllStudentTest()
107 {
108 string strCon = "server=.;user id =sa;password=sa;database= StuInfo;"; // TODO: 初始化为适当的值
109
110 StudentManage target = new StudentManage(strCon);
111
112 string strInfo="";
113
114 DataTable actual;
115
116 actual = target.GetAllStudent(out strInfo);
117 Assert.AreNotEqual(actual.Rows, 0);
118
119
120 }
121
122 /**////
123 ///GetStudentByName (string, out string) 的测试
124 ///
125 [TestMethod()]
126 public void GetStudentByNameTest()
127 {
128 string strCon = "server=.;user id =sa;password=sa;database= StuInfo;"; // TODO: 初始化为适当的值
129
130 StudentManage target = new StudentManage(strCon);
131
132 string strStuName = "李四"; // TODO: 初始化为适当的值
133
134 string strInfo="";
135 string strInfo_expected = "Failure"; // TODO: 初始化为适当的值
136
137 DataTable actual;
138
139 actual = target.GetStudentByName(strStuName, out strInfo);
140
141 Assert.AreNotEqual(strInfo_expected, strInfo);
142 Assert.AreNotEqual(actual.Rows.Count, 0);
143 }
144
145 /**////
146 ///GetStudentByNo (string, out string) 的测试
147 ///
148 [TestMethod()]
149 public void GetStudentByNoTest()
150 {
151 string strCon = "server=.;user id =sa;password=sa;database= StuInfo;"; // TODO: 初始化为适当的值
152
153 StudentManage target = new StudentManage(strCon);
154
155 string strStuNo = "20083111"; // TODO: 初始化为适当的值
156
157 string strInfo="";
158 string strInfo_expected = "Failure"; // TODO: 初始化为适当的值
159
160 DataTable actual;
161
162 actual = target.GetStudentByNo(strStuNo, out strInfo);
163
164 Assert.AreNotEqual(strInfo, strInfo_expected);
165 Assert.AreNotEqual(actual.Rows.Count, 0);
166 }
167
168 /**////
169 ///ModifyStudent (StudentInfo, int, out string) 的测试
170 ///
171 [TestMethod()]
172 public void ModifyStudentTest()
173 {
174 string strCon = "server=.;user id =sa;password=sa;database= StuInfo;"; // TODO: 初始化为适当的值
175
176 StudentManage target = new StudentManage(strCon);
177
178 StudentInfo objStudent = new StudentInfo (); // TODO: 初始化为适当的值
179 objStudent.StuNo = "20083111";
180 objStudent.StuName = "李世民";
181 objStudent.Sex = "M";
182 objStudent.Brithday = DateTime.Parse("1984-07-07");
183 objStudent.Address = "湖北武汉";
184 objStudent.Telphone = "027873582";
185 objStudent.Remark = "坏学生!";
186
187 int stuId = 1; // TODO: 初始化为适当的值
188
189 string strInfo="";
190 string strInfo_expected = "Failure"; // TODO: 初始化为适当的值
191
192 target.ModifyStudent(objStudent, stuId, out strInfo);
193
194 Assert.AreEqual(strInfo_expected, strInfo);
195
196 }
197
198 /**////
199 ///StudentManage (string) 的测试
200 ///
201 [TestMethod()]
202 public void ConstructorTest()
203 {
204 string strCon = "server=.;user id =sa;password=sa;database= StuInfo;"; // TODO: 初始化为适当的值
205
206 StudentManage target = new StudentManage(strCon);
207
208 // TODO: 实现用来验证目标的代码
209 Assert.Inconclusive("Successful");
210 }
211 /**////
212 ///DeleteStudent (int, out string) 的测试
213 ///
214 [TestMethod()]
215 public void DeleteStudentTest()
216 {
217 string strCon = "server=.;user id =sa;password=sa;database= StuInfo;"; // TODO: 初始化为适当的值
218
219 StudentManage target = new StudentManage(strCon);
220
221 int stuId = 1; // TODO: 初始化为适当的值
222
223 string strInfo="";
224 string strInfo_expected = "Failure"; // TODO: 初始化为适当的值
225
226 target.DeleteStudent(stuId, out strInfo);
227
228 Assert.AreNotEqual(strInfo_expected, strInfo);
229 }
230
231 }
232
233
234}
235
At last, I finish the View Layer. It’s UI like this.
And the form of adding and modifying students’ information is as this.