【个人机房重构】——泛型集合代替DataTable 逃离我推掉我的手 2022-08-10 13:52 153阅读 0赞 背景:听说,在VB.NET机房收费系统中,尽量使用泛型来传递数据,不要使用DataTable。 # 一、【DataTable】 # DataTable是一个临时保存数据的表,我们在机房收费系统中,将查到的数据先暂时存放在DataTable上,然后将DataTable一层一层的进行传递,最终传到U层,把用户需要的数据呈现给用户。 在VB.NET版机房收费系统中,采用的是三层架构。用三层架构开发项目,各层之间经常会传递数据。例如:D层负责与数据库交互,一般是得到DataTable或DataSet对象,然后返回给B层,B层进行类似的处理来读取数据:dt.Rows\[0\]\[“xxx”\];或者dt.Rows\[0\]\[1\]。有时DataTable也会被传到UI层,与控件进行绑定,显示数据。例如使用DataGridView控件。 无论是什么情况,使用DataTable不可避免的要填写读取的字段,这样做非常容易写错,而且编译器不检查;而且你必须要了解数据库的结构,如果是进行合作开发的话,我想如果使用DataTable,那就呵呵了吧。同时,使用DataTable不符合面向对象编程思想。 下边是一段使用DataTable的代码: If dt.Rows.Count > 0 Then '查到的表中有数据,则需要给实体字段赋值 Dim Drow As DataRow 'DataTable中的一行数据 Drow = dt.Rows(0) '从dt中取值 user.studentID = Drow("studentID").ToString.Trim() '学号赋值 user.studentName = Drow("studentName").ToString.Trim '学生姓名赋值 user.Sex = Drow("sex").ToString.Trim '性别赋值 user.Department = Drow("Department").ToString.Trim '系别赋值 user.Grade = Drow("Grade").ToString.Trim '年级赋值 user.Sclass = Drow("Sclass").ToString.Trim '班级赋值 user.State = Drow("State").ToString.Trim '状态赋值 user.Comment = Drow("Comment").ToString.Trim '备注赋值 user.Balance = Drow("Balance").ToString.Trim '余额赋值 上边的代码是“学生查询余额”窗体中的,dt就是一个新实例化的DataTable。在这里,你必须清楚的了解数据库的结构,才能保证准确无误的写出正确的代码。而且,在这个“学生查询余额”这个窗体中,需要返回的实体属性只有9个,工作量还比较小。但是,以后我们不可能只开发这样的小系统,到那时,一个实体的属性可不就是几个的事情了。 # 二、【泛型集合】 # 1、什么是泛型 ? 泛型是具有[占位符][Link 1](类型[参数][Link 2])的类、[结构][Link 3]、[接口][Link 4]和[方法][Link 5],这些占位符是类、结构、接口和方法所[存储][Link 6]或使用的一个或多个类型的占位符。当然可能看不太懂,其实我也不懂,百度百科上是这样写的。 泛型其实就像一个口袋,你可以很方便地往里面装东西,只是在第一次使用这个口袋的时候要注意声明它只能装什么样类型的东西,以后就不能装错了。那么我们就用钱包为例吧,我们首先描述一下钱包。钱包的用途不外乎是装点儿东西,当然,除了钱还可以装其它很多东西,例如银行卡、便签条、照片等等,但是这些东西有些共同的地方,至少是尺寸方面不能超过钱包的限制,谁可以把冰箱也揣在钱包里呢?因此,我们在设计能装进钱包的物品的类的时候就要考虑到尺寸的因素。 在三层架构中,实体类即数据库的映射,因此实体类中的属性和数据库表中的字段是相对应的。把DataTable中的每一行记录视为一个实体类,把其中的字段读取出来,到实体类的属性中,再把所有的实体类存在泛型集合中。因此,DataTable中有多少个记录,泛型集合中就有多少个实体类,每个实体类的属性和DataTable的字段是相对应的。这样一来,传到B层或U层的将是一个实体类的泛型集合。 使用泛型集合传递数据,编写B层的人员无需手动填写需要的字段,直接按一下点,全都提示出来了,想用哪个用哪个,不会出现写错的情况;你不必了解数据库结构;符合面向对象思想。 **2、具体的使用** 下边是具体使用泛型集合的方法: 因为在系统中,传递数据大都用泛型,所以我们可以把将DataTable转换成一个实体填充泛型集合的方法抽象出来,写成一个类似于SQLHelper的工具类,放在D层,用的时候直接调用。 **定义一个ConvertHelper的工具类** Imports System.Collections.Generic '增加泛型的命名空间 Imports System.Reflection '引入反射:为了使用PropertyInfo Public Class ConvertHelper '将datatable转化为泛型集合 Public Shared Function convertToList(Of T As {New})(ByVal dt As DataTable) As IList(Of T) '注意:convertToList(Of T As {New}) 这里的new是用来约束T的,必须有,不然new T的时候会出现错误 Dim myList As New List(Of T) '定义最终返回的集合 Dim myTpye As Type = GetType(T) '得到实体类的类型名 Dim dr As DataRow '定义行集 Dim tempName As String = String.Empty '定义一个临时变量 '遍历DataTable的所有数据行 For Each dr In dt.Rows Dim myT As New T '定义一个实体类的对象 Dim propertys() As PropertyInfo = myT.GetType().GetProperties() '定义属性集合 Dim Pr As PropertyInfo '遍历该对象的所有属性 For Each Pr In propertys tempName = Pr.Name '将属性名称赋值给临时变量 '检查DataTable是否包含此列(列名==对象的属性名) If (dt.Columns.Contains(tempName)) Then '将此属性与datatable里的列明比较,查看datatable是否包含此属性 '判断此属性是否有Setter If (Pr.CanWrite = False) Then '判断此属性是否可写,如果不可写,跳出本次循环 Continue For End If Dim value As Object = dr(tempName) '定义一个对象型的变量来保存列的值 If (value.ToString <> DBNull.Value.ToString()) Then '如果非空,则赋给对象的属性 Pr.SetValue(myT, value, Nothing) '在运行期间,通过反射,动态的访问一个对象的属性 End If End If Next myList.Add(myT) '添加到集合 Next Return myList '返回实体集合 End Function End Class **U层** Public Class frmCheckBalance Private Sub btnCheck_Click(sender As Object, e As EventArgs) Handles btnCheck.Click 'Dim EUser As New Entity.StudentInfoEntity '实例化新的UserInfoEntity,用来传递B层的实体 Dim EUser As List(Of Entity.StudentInfoEntity) '实例化新的泛型集合,用来传递B层的实体 Dim BCheck As New BLL.CheckBalanceBLL '定义BCheck,用来传递参数 Dim User As New Entity.StudentInfoEntity '定义传递的参数 User.cardNo = txtCID.Text.Trim() '卡号赋值 EUser = BCheck.Check(User) '调用B层查询余额的方法 '判断 返回的泛型集合是否为空 '不为空 If IsNothing(EUser) = False Then '查询后显示到窗体上 txtSID.Text = EUser(0).studentID '0代表第一个实体 txtName.Text = EUser(0).studentName txtSex.Text = EUser(0).Sex txtDpt.Text = EUser(0).Department txtGrade.Text = EUser(0).Grade txtClass.Text = EUser(0).Sclass txtState.Text = EUser(0).State txtComment.Text = EUser(0).Comment txtBalance.Text = EUser(0).Balance Else '为空 txtCID.Text = "" '清空卡号文本框 txtCID.Focus() '光标停留在卡号文本框 End If End Sub '退出按钮 Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click Me.Close() End Sub End Class **B层** Public Class CheckBalanceBLL '定义一个Check函数,用于查询学生余额 Function Check(ByVal user As Entity.StudentInfoEntity) As List(Of Entity.StudentInfoEntity) '返回一个泛型类 '先判断卡号是否为空 If user.cardNo.ToString.Trim = "" Then MsgBox("卡号不能为空,请输入卡号!", MsgBoxStyle.Exclamation, "警告") Return Nothing End If '卡号不为空 Dim DUser As New DAL.CheckBalanceDAL Dim EUser As List(Of Entity.StudentInfoEntity) EUser = DUser.Query(user) '把实体user传到EUser里,即传到实体层中 '判断:集合是否是空值 If IsNothing(EUser) = False Then Return EUser Else MsgBox("没有此卡号的记录,请重新输入卡号", MsgBoxStyle.Exclamation, "警告") Return Nothing End If End Function End Class **D层** Imports System.Data.SqlClient Imports System.Data Public Class CheckBalanceDAL Function Query(ByVal user As Entity.StudentInfoEntity) As List(Of Entity.StudentInfoEntity) '返回实体 '定义一个操作数据库的助手类 Dim MySqlHelper As New SqlHelper '定义对数据库的操作语句 Dim strSql As String strSql = "select * from T_StudentInfo where cardNo=@cardNo" '加入参数 Dim paras As SqlParameter() paras = {New SqlParameter("@cardNo", user.cardNo)} '执行查询,并将查询的结果保存到dt里边 Dim dt As New DataTable '保存转换后的泛型集合 Dim myList As List(Of Entity.StudentInfoEntity) '执行查询 dt = MySqlHelper.ExecSelect(strSql, CommandType.Text, paras) '先判断dt是否为空 If dt.Rows.Count > 0 Then '将dt转换为泛型集合 myList = ConvertHelper.convertToList(Of Entity.StudentInfoEntity)(dt) Return myList Else Return Nothing End If End Function End Class **结果:** ![SouthEast][] 注:实体层的代码在这里略过。使用泛型集合传递数据,必须满足:实体类的属性名必须和数据库表中的字段名一模一样;必须有已知的实体类和DataTable中的数据相对应,也就是说必须明确你要转换成的实体类类型,否则没办法指定泛型集合的类型,也就没办法调用。 # 三、【反思】 # 刚开始的时候,只是在设计模式中见过泛型这个词。对它并没有多深的理解。后来,上课的时候,米老师和师哥师姐一直提,才慢慢地开始了解。 敲机房收费系统的时候,刚开始也是使用的DataTable来返回数据的。后来,经过跟别的同学进行交流,都是建议用泛型。后来就改成使用泛型了,省得以后再返工。这就是“前人栽树,后人乘凉”吧 。 做事情的时候,要和别人多交流,多看看别人是如何做的,不要总守着自己的那一亩三分地,目光局限在自己的世界里。 多交流、多思考! [Link 1]: http://baike.baidu.com/view/873819.htm [Link 2]: http://baike.baidu.com/view/327406.htm [Link 3]: http://baike.baidu.com/view/160039.htm [Link 4]: http://baike.baidu.com/view/159864.htm [Link 5]: http://baike.baidu.com/view/169819.htm [Link 6]: http://baike.baidu.com/view/87682.htm [SouthEast]: /images/20220810/94850d9db3ca4261b70a4e53ab3ac2eb.png
还没有评论,来说两句吧...