【机房重构】一步一步往上爬——七层中的那些事

	机房重构开始已经一个多星期了,从最开始的理解登录到现在已经成功完成至少一次的“增”、“删”、“改”、“查”的操作,现在在七层的这个大环境下,从最开始的奄奄一息,终于变得生龙活虎起来了。
	之前总是听师哥师姐们说,敲完登录一条线了,后面就会很顺利了。可是,从我来说,事实并非如此。然而,磕磕绊绊,四个字足以形容我的这些天。不过,我心态好,我可以忍受一个人花时间调代码的孤独,再说,我也可以找小伙伴、找师父帮助我,我有什么理由不成功。
	我们在第一次机房收费系统的时候,就有很深的体会:完成了一次增删改查的操作,后面的路就平坦许多。而在机房重构中,这样的体会依旧出现了,只不过我出现的时间晚些,别人在敲完登录之后就有体会了,而我是在自己一次次摸索之后,完成了一些其他的功能,才有所体会的。
	下面就开始进入正题,和大家一起来看看,在七层中的那些事。
	首先说说,“增”、“删”、“改”、“查”。这四个对数据库的操作,思路都是一样的。总结来说,就是以下四条:
	1.增、删、改(有参数的)——把数据从U层传入D层,数据库做相应修改,返回给U层一个修改提示,即成功与否;
	2.增、删、该(无参数的)——根据U层操作,对数据库做相应修改,返回给U层一个修改提示,即成功与否;
	3.查(无参数的)——把数据从D层传入U层,直接返回数据库中的表信息;
	4.查(有参数的)——把数据从U层传入D层,找到相应的数据,返回一个符合查询的虚拟表;
	做完这些,自己才体会到在上篇博客中提到的SqlHelper的好处。之前,他们都说这个好,而自己却不能理解,很想弄清楚,深入研究,还是失败告终。这一次,自己没有刻意花时间去琢磨,而是在敲代码的过程中,用着用着就明白了。下面,便是我用到的SQLHelper封装好的代码:

Public Class SqlHelper
    '定义变量    
    '获得数据库的连接字符串    
    Private ReadOnly strConnection As String = ConfigurationManager.AppSettings("ConnStr")
    '设置连接    
    Dim conn As SqlConnection = New SqlConnection(strConnection)
    '定义cmd命令    
    Dim cmd As New SqlCommand

    ''' <summary>    
    ''' 执行增删改三个操作,(有参)返回值为Boolean类型,确认是否执行成功    
    ''' </summary>    
    ''' <param name="cmdText">需要执行语句,一般是Sql语句,也有存储过程</param>    
    ''' <param name="cmdType">判断Sql语句的类型,一般都不是存储过程</param>    
    ''' <param name="paras">参数数组,无法确认有多少参数</param>    
    ''' <returns></returns>    
    ''' <remarks>2015-2-3 10:05:46</remarks>    
    Public Function ExecAddDelUpdate(ByVal cmdText As String, ByVal cmdType As CommandType, ByVal paras As SqlParameter()) As Boolean
        '将传入的值,分别为cmd的属性赋值    
        cmd.Parameters.AddRange(paras)   '将参数传入    
        cmd.CommandType = cmdType            '设置一个值,解释cmdText    
        cmd.Connection = conn                '设置连接,全局变量    
        cmd.CommandText = cmdText            '设置查询的语句    

        Try
            conn.Open()                      '打开连接    
            Return cmd.ExecuteNonQuery()     '执行增删改操作    
            cmd.Parameters.Clear()           '清除参数    
        Catch ex As Exception
            Return False                         '如果出错,返回0    
        Finally
            Call CloseConn(conn)
            Call CloseCmd(cmd)
        End Try
    End Function
    ''' <summary>    
    ''' 执行增删改三个操作,(无参)    
    ''' </summary>    
    ''' <param name="cmdText">需要执行语句,一般是Sql语句,也有存储过程</param>    
    ''' <param name="cmdType">判断Sql语句的类型,一般都不是存储过程</param>    
    ''' <returns>Interger,受影响的行数</returns>    
    ''' <remarks>2015-2-3 10:05:39</remarks>    
    Public Function ExecAddDelUpdateNo(ByVal cmdText As String, ByVal cmdType As CommandType) As Boolean
        '为要执行的命令cmd赋值    
        cmd.CommandText = cmdText       '先是查询的sql语句    
        cmd.CommandType = cmdType       '设置Sql语句如何解释    
        cmd.Connection = conn           '设置连接    

        '执行操作    
        Try
            conn.Open()
            Return cmd.ExecuteNonQuery()
        Catch ex As Exception
            Return False
        Finally
            Call CloseConn(conn)
            Call CloseCmd(cmd)
        End Try
    End Function

    ''' <summary>    
    ''' 执行查询的操作,(有参),参数不限    
    ''' </summary>    
    ''' <param name="cmdText">需要执行语句,一般是Sql语句,也有存储过程</param>    
    ''' <param name="cmdType">判断Sql语句的类型,一般都不是存储过程</param>    
    ''' <param name="paras">传入的参数</param>    
    ''' <returns></returns>    
    ''' <remarks>2015-2-3 10:05:32</remarks>    
    Public Function ExecSelect(ByVal cmdText As String, ByVal cmdType As CommandType, ByVal paras As SqlParameter()) As DataTable

        Dim sqlAdapter As SqlDataAdapter
        Dim dt As New DataTable
        Dim ds As New DataSet
        '还是给cmd赋值    
        cmd.CommandText = cmdText
        cmd.CommandType = cmdType
        cmd.Connection = conn
        cmd.Parameters.AddRange(paras)  '参数添加    
        sqlAdapter = New SqlDataAdapter(cmd)  '实例化adapter    
        Try
            sqlAdapter.Fill(ds)           '用adapter将dataSet填充     
            dt = ds.Tables(0)             'datatable为dataSet的第一个表    
            cmd.Parameters.Clear()        '清除参数    
        Catch ex As Exception
            MsgBox("查询失败", CType(vbOKOnly + MsgBoxStyle.Exclamation, MsgBoxStyle), "警告")
        Finally                            '最后一定要销毁cmd    
            Call CloseCmd(cmd)
        End Try
        Return dt
    End Function


    ''' <summary>    
    ''' 执行查询的操作,(无参)    
    ''' </summary>    
    ''' <param name="cmdText">需要执行语句,一般是Sql语句,也有存储过程</param>    
    ''' <param name="cmdType">判断Sql语句的类型,一般都不是存储过程</param>    
    ''' <returns>dataTable,查询到的表格</returns>    
    ''' <remarks>2015-2-3 10:05:23</remarks>    
    Public Function ExecSelectNo(ByVal cmdText As String, ByVal cmdType As CommandType) As DataTable
        Dim sqlAdapter As SqlDataAdapter
        Dim ds As New DataSet
        '还是给cmd赋值    
        cmd.CommandText = cmdText
        cmd.CommandType = cmdType
        cmd.Connection = conn
        sqlAdapter = New SqlDataAdapter(cmd)  '实例化adapter    
        Try
            sqlAdapter.Fill(ds)           '用adapter将dataSet填充     
            Return ds.Tables(0)             'datatable为dataSet的第一个表    
        Catch ex As Exception
            Return Nothing
        Finally                            '最后一定要销毁cmd    
            Call CloseCmd(cmd)
        End Try
    End Function

    ''' <summary>    
    ''' 关闭连接    
    ''' </summary>    
    ''' <param name="conn">需要关闭的连接</param>    
    ''' <remarks>2015-2-3 10:05:56</remarks>    
    Public Sub CloseConn(ByVal conn As SqlConnection)
        If (conn.State <> ConnectionState.Closed) Then  '如果没有关闭    
            conn.Close()                    '关闭连接    
            conn = Nothing                  '不指向原对象    
        End If

    End Sub
    ''' <summary>    
    ''' 关闭命令    
    ''' </summary>    
    ''' <param name="cmd">需要关闭的命令</param>    
    ''' <remarks>2015-2-3 10:06:03</remarks>    
    Public Sub CloseCmd(ByVal cmd As SqlCommand)

        If Not IsNothing(cmd) Then          '如果cmd命令存在    
            cmd.Dispose()                   '销毁    
            cmd = Nothing
        End If
    End Sub
End Class
	敲的代码多了,对七层中的东西也是越来越熟悉了。下面,就从注册这一功能,重新感受一番七层给我的体会。
	注册首先明确涉及到一个新的表,所以,第一步就是添加配置文件,

<add key="StrCard"  value="CardDAL"/>
	第二封装实体,将注册表(也就是卡表)的各个字段都写入实体中,以便之后的各层之间的传参数。

Public Class EntityCard
    Private CardNo As String
    Private StudentNo As String
    Private RegisterDate As String
    Private RegisterTime As String
    Private Balance As String
    Private UserID As String
    Private IsCheck As String
    Private Type As String
    Private CardStatus As String

    Public Property Card() As String
        Get
            Return CardNo
        End Get
        Set(value As String)
            CardNo = value
        End Set
    End Property

    Public Property Student() As String
        Get
            Return StudentNo
        End Get
        Set(value As String)
            StudentNo = value
        End Set
    End Property

    Public Property RDate() As String
        Get
            Return RegisterDate
        End Get
        Set(value As String)
            RegisterDate = value
        End Set
    End Property

    Public Property RTime() As String
        Get
            Return RegisterTime
        End Get
        Set(value As String)
            RegisterTime = value
        End Set
    End Property

    Public Property BL() As String
        Get
            Return Balance
        End Get
        Set(value As String)
            Balance = value
        End Set
    End Property

    Public Property ID() As String
        Get
            Return UserID
        End Get
        Set(value As String)
            UserID = value
        End Set
    End Property

    Public Property Check() As String
        Get
            Return IsCheck
        End Get
        Set(value As String)
            IsCheck = value
        End Set
    End Property

    Public Property CardType() As String
        Get
            Return Type
        End Get
        Set(value As String)
            Type = value
        End Set
    End Property

    Public Property Status() As String
        Get
            Return CardStatus
        End Get
        Set(value As String)
            CardStatus = value
        End Set
    End Property

End Class
	第三创建接口方法,可根据需要自行创建,比如:添加学生,注册卡号等等。

Public Interface Card
    ''' <summary>
    ''' 创建注册的方法
    ''' </summary>
    ''' <param name="card"></param>
    ''' <returns></returns>
    ''' <remarks>2015-2-5 10:43:54</remarks>
    Function RegisterCard(ByVal card As Model.EntityCard) As Boolean
    ''' <summary>
    ''' 查询余额的方法
    ''' </summary>
    ''' <param name="card"></param>
    ''' <returns></returns>
    ''' <remarks>2015-2-5 23:05:15</remarks>
    Function QueryBalance(ByVal card As Model.EntityCard) As DataTable

End Interface
	第四创建抽象工厂+反射,经慕夏师父的一次详细指导后,对这一层的了解也是更深刻了些。它的好处就是可以避免出现大量的case或其他选择语句,因为工厂+反射自己会去查找相对应的数据表。如,下面指向的就是注册表:

 ''' <summary>
    ''' 实例化一个DAL中的Card表的类
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks>2015-2-5 10:17:38</remarks>
    Public Function CreateCard() As IDAL.Card
        Return CType(Assembly.Load("DAL").CreateInstance("DAL" & "." & StrCard), IDAL.Card)
    End Function
	第五D层实现接口的方法。这里就会用到之前封装好的Sqlhelper类,根据实际情况,选择对应的某种操作。如注册卡号(属于有参数的增操作):

 ''' <summary>
    ''' 注册新卡
    ''' </summary>
    ''' <param name="card"></param>
    ''' <returns>返回一个Boolean型的值</returns>
    ''' <remarks>2015-2-5 12:25:04</remarks>
    Public Function AddCard(ByVal card As Model.EntityCard) As Boolean Implements Card.RegisterCard
        Dim Sql As String
        Dim table As Boolean
        Dim sqlParams As SqlParameter() = {New SqlParameter("@CNO", card.Card),
                                           New SqlParameter("@SNO", card.Student),
                                           New SqlParameter("@Date", card.RDate),
                                           New SqlParameter("@Time", card.RTime),
                                           New SqlParameter("@Balance", card.BL),
                                           New SqlParameter("@ID", card.ID),
                                           New SqlParameter("@Check", card.Check),
                                           New SqlParameter("@Type", card.CardType),
                                           New SqlParameter("@Status", card.Status)}
        Sql = "Insert into Card_Info Values(@CNO,@SNO,@Date,@Time,@Balance,@ID,@Check,@Type,@Status)"
        table = sqlhelper.ExecAddDelUpdate(Sql, CommandType.Text, sqlParams)
        Return table
    End Function
	这里实现的就是增加,在这一层,不一样的也就仅仅是有无参数或不同情况下的SQL语句写法不同。值得注意的是SQL语句的写法,往往会因为写法不正确而报错。“=”前后分别是数据库中的字段和封装好的实体的名称,两个不能有任何不同。
	第六B层方法封装,实现抽象工厂和接口的方法。

 ''' <summary>
    ''' 注册卡号
    ''' </summary>
    ''' <param name="card"></param>
    ''' <returns></returns>
    ''' <remarks>2015-2-5 21:19:31</remarks>
    Public Function AddCard(ByVal card As Model.EntityCard) As Boolean
        Dim factory As New Factory.ChargeSystemFactory()
        Dim Acard As IDAL.Card
        Dim flag As Boolean
        Acard = factory.CreateCard
        flag = Acard.RegisterCard(card)
        If flag = True Then
            MsgBox("注册卡号成功", , "提示")
        Else
            MsgBox("未成功注册", , "提示")
        End If
        Return flag
    End Function
	第七完成外观层对应方法的封装,以便调用B层方法将U层数据传入B层或返回数据给U层。

 ''' <summary>
    ''' 注册卡号
    ''' </summary>
    ''' <param name="card"></param>
    ''' <returns></returns>
    ''' <remarks>2015-2-6 10:04:41</remarks>
    Public Function AddCard(ByVal card As Model.EntityCard) As Boolean
        Dim add As New BLL.CardBLL()
        Dim flag As Boolean
        flag = add.AddCard(card)
        Return flag
    End Function
	第八U层传值。视情况,将需要传入的参数赋值给实体,从而通过调用外观层的方法,进行传参。

Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
        Dim CardFacade As New Facade.CardFacade
        Dim card As New Model.EntityCard
        '调用卡
        card.ID = frmLogin.txtUserName.Text
        card.BL = txtMoney.Text
        card.Student = txtStudentNo.Text
        card.Card = txtCardNo.Text
        card.CardType = comboType.Text
        card.RDate = DateTime.Now.ToLongDateString()
        card.RTime = DateTime.Now.ToLongTimeString()
        card.Status = txtStatus.Text
        card.Check = "未结账"
        Dim flag1 As Boolean
        flag1 = CardFacade.AddCard(card)
    End Sub
	上面八条就是这些天自己敲完后总结出来的一个思路吧。每次一个新功能的开始,亦或是功能的调错,自己都是按着以上八步一步一步进行的,脑海中有个清晰的思路,才得以保证自己后面的路可以走下去,或者不会因为前方一片迷茫而不敢前行。
学习心得:
	机房重构进行的这些天,真是涨了不少知识。不论是遇到的错误还是一个功能的实现,都有一种收获的感觉。现在觉得七层中的这些东西也都是大同小异的了,所以说重复就是力量。刚开始的感觉一定是最不好的,但也是必须要经过的,这就是一个过程。接下来,依旧一步一步往上爬,机房重构,继续奋战~~
上一篇:通过CLR同步SQL Server和Sharepoint List数据(一)


下一篇:递归查询文件目录下所有的文件(八)