本文主要是介绍DataTable填充实体类返回泛型集合,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
昨天找坤哥看到我的一段代码,如下:
稍微解释下,这段代码时D层查询结束后,将datatable查询到的结果赋值给实体对象的属性,然后返回实体的过程,坤哥看了之后问我,如果实体有500多个属性,难道也要这样一条一条的写吗?如果返回多个实体时怎么办?这时,我才意识到自己的代码时非常有问题的,原来设计的是每个方法最多返回一个实体,但是当遇到查询到多条记录的时候,就又冒着破坏三层结构的事返回Datatable去了,真的是很有问题啊。
怎么改,我脑海中一下子就浮现了老办法:数组+循环,用循环读条读取列值,保存到对象数组。之后查了查,发现我太OUT了。明明就有又新又好的技术可我用过了也想不起来。
百度上是用的是这样的:在实体层添加一个工具类,类似D层的工具类,只不过这个工具类是完成将datatable转换为一个包含实体的泛型集合的,然后每次返回的时候,将集合作为结果返回。
这个方法最好的地方还是在对每个实体属性的赋值上面,通过内部的属性集合循环赋值,再也不用一条条写了,这样,不仅封装性好了,而且使代码更不容易出错。
但是,唯一遗憾的就是,使用这个工具类对于实体和datatable有一个条件限制,就是实体的属性名必须和datatable的列名对应。
在百度上找了很多这样的类,但是只找到了C#版的,然后自己改写了个VB.NET版的。下面写出自己的DEMO:
首先,是实体类:
这个实体类是对教师信息表的映射。
- Public Class EntityUser
- Private FilUserName As String '用户名
- Private FilPwd As String '密码
- Private FilTeaName As String '教师姓名
- Private FilTeaLevel As String '教师级别
- Private FilRegDate As String '注册日期
- Private FilRegTime As String '注册时间
- Private FilDelDate As String '注销日期
- Private FilDelTime As String '注销时间
- Private FilOnLineStatue As String '在线状态
- ''' <summary>
- ''' 用户名
- ''' </summary>
- ''' <value></value>
- ''' <returns></returns>
- ''' <remarks></remarks>
- Public Property UserName() As String
- Get
- Return FilUserName
- End Get
- Set(ByVal value As String)
- FilUserName = value
- End Set
- End Property
- ''' <summary>
- ''' 密码
- ''' </summary>
- ''' <value></value>
- ''' <returns></returns>
- ''' <remarks></remarks>
- Public Property Pwd() As String
- Get
- Return FilPwd
- End Get
- Set(ByVal value As String)
- FilPwd = value
- End Set
- End Property
- ''' <summary>
- ''' 教师姓名
- ''' </summary>
- ''' <value></value>
- ''' <returns></returns>
- ''' <remarks></remarks>
- Public Property TeaName() As String
- Get
- Return FilTeaName
- End Get
- Set(ByVal value As String)
- FilTeaName = value
- End Set
- End Property
- ''' <summary>
- ''' 教师级别
- ''' </summary>
- ''' <value></value>
- ''' <returns></returns>
- ''' <remarks></remarks>
- Public Property TeaLevel() As String
- Get
- Return FilTeaLevel
- End Get
- Set(ByVal value As String)
- FilTeaLevel = value
- End Set
- End Property
- ''' <summary>
- ''' 注册日期
- ''' </summary>
- ''' <value></value>
- ''' <returns></returns>
- ''' <remarks></remarks>
- Public Property RegDate() As String
- Get
- Return FilRegDate
- End Get
- Set(ByVal value As String)
- FilRegDate = value
- End Set
- End Property
- ''' <summary>
- ''' 注册时间
- ''' </summary>
- ''' <value></value>
- ''' <returns></returns>
- ''' <remarks></remarks>
- Public Property RegTime() As String
- Get
- Return FilDelDate
- End Get
- Set(ByVal value As String)
- FilDelTime = value
- End Set
- End Property
- ''' <summary>
- ''' 注销日期
- ''' </summary>
- ''' <value></value>
- ''' <returns></returns>
- ''' <remarks></remarks>
- Public Property DelDate() As String
- Get
- Return FilDelDate
- End Get
- Set(ByVal value As String)
- FilDelDate = value
- End Set
- End Property
- ''' <summary>
- ''' 注销时间
- ''' </summary>
- ''' <value></value>
- ''' <returns></returns>
- ''' <remarks></remarks>
- Public Property DelTime() As String
- Get
- Return FilDelTime
- End Get
- Set(ByVal value As String)
- FilDelTime = value
- End Set
- End Property
- ''' <summary>
- ''' 在线状态
- ''' </summary>
- ''' <value></value>
- ''' <returns></returns>
- ''' <remarks></remarks>
- Public Property OnLineStatue() As String
- Get
- Return FilOnLineStatue
- End Get
- Set(ByVal value As String)
- FilOnLineStatue = value
- End Set
- End Property
- End Class
我 的教师信息表如下:里面有3条记录,这个结果等一会咱们运行的时候会用到。
然后,写了个SqlHelper,这个代码不再写。因为这里是做个DEMO,所以没有采用3层架构,大家做的时候要注意不要破坏整体的结构。
然后下面就是本文主要要讨论的EntityHelper,不知道这样取名准不准确,但是如果要把这个类加入三层里面,我会把向SQLHelper那样对待,将它作为实体层的一个工具类加进去。
- Imports System.Collections.Generic '增加泛型的命名空间
- Imports System.Reflection '引入反射:为了使用PropertyInfo
- Public Class EntityHelper
- Public Shared Function convertToList(Of T As {New})(ByVal dt As DataTable) As IList(Of T) '将datatable转化为泛型集合
- '注意:1,convertToList(Of T As {New}) 这里的new是用来约束T的,必须有,不然new T的时候会出现错误
- '2,new约束在C#和VB.NET里面的写法是不一样的,C#里面用的是where来为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
至此,主要的类都写完了,下面来看下客户端该如何调用:
- <span style="font-size:18px;"> Dim strSQL As String = "select * from T_TeaInfo where TeaLevel ='管理员'" '注意,这种破坏架构的写法不提倡
- Dim mysqlhelper As New SQLHelper '定义查询助手类
- Dim dt As New DataTable '定义查询到的表集
- Dim myList As List(Of EntityUser) '保存转换后的泛型集合
- dt = mysqlhelper.ExecSelect(strSQL, CommandType.Text) '执行查询
- '将dt转换为泛型集合
- myList = EntityHelper.convertToList(Of EntityUser)(dt)
- TextBox1.Text = myList.Count '在文本框里面显示出查询到的教师数目
- </span>
运行后,可以看到,结果如下:
小结下:
对于三层中,可能刚开始做的时候,我们从D层向上层传数据的时候用的是Datatable,只能说,这是一种非常破坏结构的方法,自己真的体会到了实体的好处,才会用它。
绑定控件显示
在D层查询完成之后,我们将DataTable转化为泛型集合,然后经过中间各层,返回U层,到了这里,问题来了,我们这时候要将这个集合作为数据源绑定到DataGridView上,并将结果显示出来,本文主要介绍的就是如何将集合显示在DataGridView上的问题。(或许你返回的是datatable或一个实体,它们加载数据的原理是一至滴~)
一,显示实体的全部属性
当DataGridView的列名就是Class T的属性的时候,这时,只需要绑定数据源,然后手动更改列名就好。
- <span style="font-size:14px;"> '将dt转换为泛型集合
- myList = EntityHelper.convertToList(Of EntityUser)(dt)
- TextBox1.Text = myList.Count '在文本框里面显示出查询到的教师数目
- If (myList.Count > 0) Then '如果查询到记录
- ''''将查询到的数据显示到DataGridView中
- myDataGrid.DataSource = myList '设置控件的数据源:但是这样设置还不够,因为列的名字显示出来还是英文的,所以,还应该设置没列的名字。
- '修改列名
- myDataGrid.Columns(0).HeaderText = "卡号"
- myDataGrid.Columns(1).HeaderText = "密码"
- myDataGrid.Columns(2).HeaderText = "教师姓名"
- myDataGrid.Columns(3).HeaderText = "教师级别"
- myDataGrid.Columns(4).HeaderText = "注册日期"
- myDataGrid.Columns(5).HeaderText = "注册时间"
- myDataGrid.Columns(6).HeaderText = "注销日期"
- myDataGrid.Columns(7).HeaderText = "注销时间"
- myDataGrid.Columns(8).HeaderText = "上机状态"
- Else
- MsgBox("没有查询到任何记录!")
- End If</span>
显示结果如下:
二,显示Class T 的部分属性
有时候,我们并不是要显示T 的全部属性,只需要显示一部分,比如,我们在显示教师信息的时候,可能不需要显示教师的密码,这时候该怎么办呢?
方法一:加载完所有数据后手动移除列
如上图中,如果我想移除密码这一列,只需在加载完所有数据后,再加上一句代码:
- '加载完所有数据后手动移除密码这一列
- myDataGrid.Columns.Remove("Pwd")
当再次显示,结果就如下图:
虽然也能实现想要的效果,但是总感觉这么做有点儿雷,就好像吃了东西再吐出来那样,反正我是不用。还是看看别的方法吧。
方法二:设置DataPropertyName属性
例如,我现在只想显示用户名,首先,右击窗体上的DataGridView控件,然后选择编辑列:
添加一个叫用户名的列,然后设置列的DataPropertyName属性,通过这个属性,可以设置绑定到实体的某个属性值,datatable的某个列等。 这里的DataPropertyName值要跟实体的属性名对应。
之后,我们在代码里面写入
- <span style="font-size:18px;"> myDataGrid.AutoGenerateColumns = False '不允许自动添加列
- myDataGrid.DataSource = myList '设置数据源</span>
这样就ok了~
运行下,可以看到这种效果:
小结:
真心感觉DataGridView这个空间非常好用,直接绑定数据源就可以显示数据。
而且如果在D层,查询完成之后,就将datatable转化为实体集,当结果传到U层DataGridView显示的时候,直接指定dataSource为实体集合,那么,整个过程没有像以前vb中MSHFlexGrid控件那样:
这个过程每次都要一行一行的赋值,避免了filds属性在指定时出错的问题,而且代码更简洁了,我们的工作量也大大减少了。
这篇关于DataTable填充实体类返回泛型集合的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!