今天要说的实体框架并不是 ADO.NET EntityFramework,而是利用特性与反射技术自己来搭建一个简单的实体框架。在来讲之前先说些题外话,我们知道要想使一个项目有更好的健壮性、可移植型,是要将项目分层,不管是c/s,还是b/s框架一般都是三层架构,数据处理层(DAL)、业务逻辑层(BLL)、界面显示层(USL或者UI)。当然根据项目的业务流程可能分个七八层也是常有的事。今天主要讲的是在数据处理层是怎样实现实体架构的。
言归正传,现在开始构建框架,首先建立数据库,就做一个学生选课信息系统(StudentManage),包括三张表,一个学生信息表(Students),一个课程表(CourseS),一个选课表(SCs),它们之间的关系如下图所示:
好了,数据库建好后,我们到程序中去进行操作,新建一个winform项目
看一下需要创建的类:
先来构建特性类
1 /// <summary>
2
3 /// 表特性类
4
5 /// </summary>
6
7 [AttributeUsage(AttributeTargets.Class)]//可以对类应用特性
8
9 public class TableAttribute:Attribute
10
11 {
12
13 /// <summary>
14
15 /// 表名
16
17 /// </summary>
18
19 public string TableName
20
21 {
22
23 get;
24
25 set;
26
27 }
28
29 }
30
31 [AttributeUsage(AttributeTargets.Property)]//可以对属性应用特性
32
33 public class FieldAttribute : Attribute
34
35 {
36
37 /// <summary>
38
39 /// 字段名
40
41 /// </summary>
42
43 public string FieldName
44
45 {
46
47 get;
48
49 set;
50
51 }
52
53 /// <summary>
54
55 /// 字段类型
56
57 /// </summary>
58
59 public SqlDbType FieldType
60
61 {
62
63 get;
64
65 set;
66
67 }
68
69 /// <summary>
70
71 /// 是不是主键
72
73 /// </summary>
74
75 public bool IsPrimaryKey
76
77 {
78
79 get;
80
81 set;
82
83 }
84
85 /// <summary>
86
87 /// 是否为空
88
89 /// </summary>
90
91 public bool IsNull
92
93 {
94
95 get;
96
97 set;
98
99 }
100
101 /// <summary>
102
103 /// 是否是自动增长
104
105 /// </summary>
106
107 public bool IsIdentity
108
109 {
110
111 get;
112
113 set;
114
115 }
116
117 }
118
119 在构建实体类(和表是对应着的):
120
121 /// <summary>
122
123 /// 空接口,实体类的父类
124
125 /// </summary>
126
127 public class IEntity
128
129 {
130
131 }
132
133 //学生类
134
135 [Table(TableName = "Students")]//表名(注意TableAttribute可以简写为Table)
136
137 public class Student:IEntity
138
139 {
140
141 [Field(FieldName="StudentID",FieldType=SqlDbType.NVarChar,IsPrimaryKey=true)]//字段特性
142
143 public string StudentID
144
145 {
146
147 get;
148
149 set;
150
151 }
152
153 [Field(FieldName="StudentName",FieldType=SqlDbType.NVarChar)]
154
155 public string StudentName
156
157 {
158
159 get;
160
161 set;
162
163 }
164
165 [Field(FieldName="Sex",FieldType=SqlDbType.NVarChar)]
166
167 public string Sex
168
169 {
170
171 get;
172
173 set;
174
175 }
176
177 [Field(FieldName="Age",FieldType=SqlDbType.TinyInt)]
178
179 public int Age
180
181 {
182
183 get;
184
185 set;
186
187 }
188
189 //课程类
190
191 [Table(TableName="Courses")]
192
193 public class Course:IEntity
194
195 {
196
197 [Field(FieldName="CourseID",FieldType=SqlDbType.Int,IsPrimaryKey=true,IsIdentity=true)]
198
199 public int CourseID
200
201 {
202
203 get;
204
205 set;
206
207 }
208
209 [Field(FieldName = "CourseName", FieldType = SqlDbType.NVarChar)]
210
211 public string CourseName
212
213 {
214
215 get;
216
217 set;
218
219 }
220
221 }
222
223 //选课实体类
224
225 [Table(TableName="SCs")]
226
227 public class SC:IEntity
228
229 {
230
231 [Field(FieldName="StudentID",FieldType=SqlDbType.NVarChar,IsPrimaryKey=true)]
232
233 public string StudentID
234
235 {
236
237 get;
238
239 set;
240
241 }
242
243 [Field(FieldName = "CourseID", FieldType = SqlDbType.Int, IsPrimaryKey = true)]
244
245 public int CourseID
246
247 {
248
249 get;
250
251 set;
252
253 }
254
255 }
好了,实体类构建完成后,我们就来构建实体操作类:
1 public class EntityOPT
2
3 {
4
5 string constr;//连接字符串
6
7 SqlConnection con;//数据库连接
8
9 /// <summary>
10
11 /// 数据初始化
12
13 /// </summary>
14
15 public EntityOPT()
16
17 {
18
19 constr = "server=.;database=StudentManage;uid=sa;pwd=sa"; ;
20
21 con = new SqlConnection(constr);
22
23 }
24
25 /// <summary>
26
27 /// 获得表名
28
29 /// </summary>
30
31 /// <param name="type"></param>
32
33 /// <returns></returns>
34
35 string GetTableName(Type type)
36
37 {
38
39 return ((TableAttribute)type.GetCustomAttributes(true)[0]).TableName;//给类只加了一个自定义特性
40
41 }
42
43 public List<T> QueryAll<T>() where T : IEntity//由于不知道前台会传哪个实体类,所以用泛型
44
45 {
46
47 try
48
49 {
50
51 Type type = typeof(T);//得到类型
52
53 List<T> entitys = new List<T>();//实体集合
54
55 string sql = "select *from " + GetTableName(type);//获得表名,拼接sql语句
56
57 SqlDataAdapter da = new SqlDataAdapter(sql, constr);
58
59 DataTable dt = new DataTable();
60
61 da.Fill(dt);//向DataTable中填充数据
62
63 foreach (DataRow row in dt.Rows)//遍历所有行
64
65 {
66
67 ConstructorInfo conInfo = type.GetConstructor(new Type[0]);//得到构造函数
68
69 object obj = conInfo.Invoke(null);//实例化对象
70
71 foreach (PropertyInfo pi in type.GetProperties())//得到所有属性
72
73 {
74
75 foreach (object ob in pi.GetCustomAttributes(true))//遍历字段所有特性
76
77 {
78
79 if (ob is FieldAttribute)
80
81 {
82
83 string fieldname = (ob as FieldAttribute).FieldName;//得到表中字段的名字
84
85 pi.SetValue(obj, row[fieldname], null);//给实体类属性赋值
86
87 break;
88
89 }
90
91 }
92
93 }
94
95 entitys.Add((T)obj);
96
97 }
98
99 return entitys;
100
101 }
102
103 catch (Exception ex)
104
105 {
106
107 throw new Exception("数据层查询所有数据发生异常:" + ex.Message);
108
109 }
110
111 }
112
113 /// <summary>
114
115 /// 插入数据
116
117 /// </summary>
118
119 /// <param name="entity"></param>
120
121 /// <returns></returns>
122
123 public bool InsertEntity(IEntity entity)
124
125 {
126
127 try
128
129 {
130
131 Type type = entity.GetType();
132
133 List<SqlParameter> pars = new List<SqlParameter>();//sql参数集合
134
135 string sql = "insert into " + GetTableName(type) + " values(";
136
137 foreach (PropertyInfo pi in type.GetProperties())//遍历类的所有属性
138
139 {
140
141 foreach (object obj in pi.GetCustomAttributes(true))//遍历属性的特性
142
143 {
144
145
146
147 if (obj is FieldAttribute && !(obj as FieldAttribute).IsIdentity)//有些主键是不自动增长的,如学号
148
149 {
150
151 string par = "@" + (obj as FieldAttribute).FieldName;
152
153 sql += par + ",";//拼接sql语句
154
155 SqlParameter sp = new SqlParameter(par, (obj as FieldAttribute).FieldType);
156
157 sp.Value = pi.GetValue(entity, null);
158
159 pars.Add(sp);
160
161 break;
162
163 }
164
165 }
166
167 }
168
169 sql = sql.TrimEnd(',') + ")";//去掉多余的逗号
170
171 return SaveChange(sql, pars);
172
173 }
174
175 catch (Exception ex)
176
177 {
178
179 throw new Exception(ex.Message);
180
181 }
182
183 }
184
185 /// <summary>
186
187 /// 删除数据
188
189 /// </summary>
190
191 /// <param name="entity"></param>
192
193 /// <returns></returns>
194
195 public bool DeleteEntity(IEntity entity)
196
197 {
198
199 try
200
201 {
202
203 Type type = entity.GetType();
204
205 List<SqlParameter> sps = new List<SqlParameter>();
206
207 string sql = "delete " + GetTableName(type) + " where ";
208
209 foreach (PropertyInfo pi in type.GetProperties())
210
211 {
212
213 foreach (object obj in pi.GetCustomAttributes(true))
214
215 {
216
217 if (obj is FieldAttribute && (obj as FieldAttribute).IsPrimaryKey)
218
219 {
220
221 FieldAttribute fa = obj as FieldAttribute;
222
223 sql += fa.FieldName + "=@" + fa.FieldName+" and ";//可能会是联合主键
224
225 SqlParameter sp = new SqlParameter("@" + fa.FieldName, fa.FieldType);
226
227 sp.Value = pi.GetValue(entity, null);
228
229 sps.Add(sp);
230
231 break;
232
233 }
234
235 }
236
237 }
238
239 sql = sql.Substring(0, sql.Length - 4);
240
241 return SaveChange(sql,sps);
242
243 }
244
245 catch (Exception ex)
246
247 {
248
249 throw new Exception(ex.Message);
250
251 }
252
253 }
254
255 /// <summary>
256
257 /// 更新数据
258
259 /// </summary>
260
261 /// <param name="entity"></param>
262
263 /// <returns></returns>
264
265 public bool UpdateEntity(IEntity entity)
266
267 {
268
269 try
270
271 {
272
273 Type type = entity.GetType();
274
275 List<SqlParameter> pars = new List<SqlParameter>();
276
277 string sql = "update " + GetTableName(type) + " set ";
278
279 string where = " where ";
280
281 foreach (PropertyInfo pi in type.GetProperties())
282
283 {
284
285 foreach (object obj in pi.GetCustomAttributes(true))
286
287 {
288
289 if (obj is FieldAttribute)
290
291 {
292
293 FieldAttribute fa = obj as FieldAttribute;
294
295 if (fa.IsPrimaryKey)
296
297 {
298
299
300
301 where += fa.FieldName + "=@" + fa.FieldName;
302
303 SqlParameter sp = new SqlParameter("@" + fa.FieldName, fa.FieldType);
304
305 sp.Value = pi.GetValue(entity, null);
306
307 pars.Add(sp);
308
309 }
310
311 else
312
313 {
314
315 sql += fa.FieldName + "=@" + fa.FieldName + ",";
316
317 SqlParameter sp = new SqlParameter("@" + fa.FieldName, fa.FieldType);
318
319 sp.Value = pi.GetValue(entity, null);
320
321 pars.Add(sp);
322
323 }
324
325 break;
326
327 }
328
329 }
330
331 }
332
333 sql = sql.TrimEnd(',') + where;
334
335 return SaveChange(sql, pars);
336
337 }
338
339 catch (Exception ex)
340
341 {
342
343 throw new Exception(ex.Message);
344
345 }
346
347 }
348
349 /// <summary>
350
351 /// 向数据库提交数据
352
353 /// </summary>
354
355 /// <param name="sql"></param>
356
357 /// <param name="pars"></param>
358
359 /// <returns></returns>
360
361 bool SaveChange(string sql, List<SqlParameter> pars)
362
363 {
364
365 try
366
367 {
368
369 SqlCommand cmd = new SqlCommand(sql, con);
370
371 foreach (SqlParameter par in pars)
372
373 {
374
375 cmd.Parameters.Add(par);
376
377 }
378
379 con.Open();
380
381 int count = cmd.ExecuteNonQuery();
382
383 if (count > 0)
384
385 {
386
387 return true;
388
389 }
390
391 else
392
393 {
394
395 return false;
396
397 }
398
399 }
400
401 catch (Exception ex)
402
403 {
404
405
406
407 throw new Exception(ex.Message);
408
409 }
410
411 finally
412
413 {
414
415 con.Close();
416
417 }
418
419 }
420
421
422
423 }
好了,到这一步,一个简单的实体框架算是构建完成,下面来测试。
先做一个界面:
四个button控件,一个dataGridView控件。
1 //先来做学生的添加:
2
3 Student student = new Student();
4
5 student.StudentID = "09040401036";
6
7 student.StudentName = "张三";
8
9 student.Age = 18;
10
11 student.Sex = "男";
12
13 if (entityopt.InsertEntity(student))
14
15 {
16
17 MessageBox.Show("添加成功!");
18
19 }
20
21 else
22
23 {
24
25 MessageBox.Show("添加失败!");
26
27 }
28
29 //查询操作:
30
31 private void button4_Click(object sender, EventArgs e)
32
33 {
34
35 dataGridView1.DataSource = entityopt.QueryAll<Student>();
36
37 }
单击添加并查询:
添加成功。
来做修改,把名字改为李四:
1 Student student = new Student();
2
3 student.StudentID = "09040401036";
4
5 student.StudentName = "李四";
6
7 student.Age = 18;
8
9 student.Sex = "男";
10
11 if (entityopt.UpdateEntity(student))
12
13 {
14
15 MessageBox.Show("修改成功!");
16
17 }
18
19 else
20
21 {
22
23 MessageBox.Show("修改失败!");
24
25 }
单击修改,并查询:
修改成功。
删除刚才的学生信息:
1 Student student = new Student();
2
3 student.StudentID = "09040401036";
4
5 if (entityopt.DeleteEntity(student))
6
7 {
8
9 MessageBox.Show("删除成功!");
10
11 }
12
13 else
14
15 {
16
17 MessageBox.Show("删除失败!");
18
19 }
删除并查询:
删除成功。
下面在对课程操作,首先添加课程:
1 Course course = new Course();
2
3 course.CourseName = "C#";
4
5 if (entityopt.InsertEntity(course))
6
7 {
8
9 MessageBox.Show("添加成功!");
10
11 }
12
13 else
14
15 {
16
17 MessageBox.Show("添加失败!");
18
19 }
20
21 //查询操作:
22
23 dataGridView1.DataSource = entityopt.QueryAll<Course>();
点击添加,然后查询,结果:
我们看到课程也添加成功了,在做修改:
1 Course course = new Course();
2
3 course.CourseID = 1;
4
5 course.CourseName = "asp.net";
6
7 if (entityopt.UpdateEntity(course))
8
9 {
10
11 MessageBox.Show("修改成功!");
12
13 }
14
15 else
16
17 {
18
19 MessageBox.Show("修改失败!");
20
21 }
点击修改,并查询,结果:
课程也能修改成功!
再来删除!
1 Course course = new Course();
2
3 course.CourseID = 1;
4
5 if (entityopt.DeleteEntity(course))
6
7 {
8
9 MessageBox.Show("删除成功!");
10
11 }
12
13 else
14
15 {
16
17 MessageBox.Show("删除失败!");
18
19 }
看一下运行结果:
删除成功!
好了,到这一步我们发现实体框架构建成功,并能运行。不过注意了,如果要是做选课的修改时,不能直接调用修改方法,因为它是联合主键,应该先删除在添加,其实修改本本身就是先删除后添加的过程。
好,今天讲的东西虽然是简单实体框架,但也有难度,一般大公司都有自己的实体框架,有不懂的地方可以和我交流。