iT邦幫忙

DAY 29
8

程式設計心法系列 第 29

程式設計心法:28.小技巧--Table V.S. Class

另外一個我比較常用的例子是,用 Class 來寫 Table 的對應程式。
(不過應該有這種 Tool 可以幫您將 DB 的 Table 自動產生 Class 程式碼的)

不過因為物件的溝通是靠訊息及 Event 的傳遞,如果欄位太多,則對於物件屬性的存取反而會影響到 Performance(用 Debug Trace 一下就知道是怎麼回事了)。

那就讓我們就進入程式吧!
假設我有一個員工的資料表,Employee

CREATE TABLE [dbo].[Employee] (
	[empy_id] [varchar] (6) NOT NULL ,
	[empy_nm] [varchar] (8) NOT NULL ,
	[dep_id] [varchar] (6) NOT NULL ,
	[status] [char] (1) NOT NULL ,
	[role] [char] (6) NOT NULL ,
	[email] [varchar] (50) NULL 
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Employee] ADD 
	CONSTRAINT [PK_Employee] PRIMARY KEY  CLUSTERED 
	(
		[empy_id]
	) WITH  FILLFACTOR = 90  ON [PRIMARY] 
GO

有 Employee ID, Employee Name, Department ID, Role, Email, Status 等欄位。

我會建構一個 Class,clsEmployee 來與之對應:

Imports System.Data.SqlClient

Public Class clsEmployee
  Private m_empy_id As String
  Private m_empy_nm As String
  Private m_dep_id As String
  Private m_role As String
  Private m_email As String
  Private m_status As String

  Public Property empy_id() As String
    Get
      Return m_empy_id
    End Get
    Set(ByVal value As String)
      m_empy_id = Value
    End Set
  End Property

  Public Property empy_nm() As String
    Get
      Return m_empy_nm
    End Get
    Set(ByVal value As String)
      m_empy_nm = value
    End Set
  End Property

  Public Property dep_id() As String
    Get
      Return m_dep_id
    End Get
    Set(ByVal value As String)
      m_dep_id = value
    End Set
  End Property

  Public Property role() As String
    Get
      Return m_role
    End Get
    Set(ByVal value As String)
      m_role = value
    End Set
  End Property

  Public Property email() As String
    Get
      Return m_email
    End Get
    Set(ByVal value As String)
      m_email = value
    End Set
  End Property

  Public Property status() As String
    Get
      Return m_status
    End Get
    Set(ByVal value As String)
      m_status = value
    End Set
  End Property

  Public Sub GetData(ByVal uid As String)
    Dim conn As New SqlConnection
    Dim cmd As New SqlCommand
    Dim dr As SqlDataReader

    Try

      conn.ConnectionString = My.Settings.ConnectionString
      conn.Open()

      cmd.Connection = conn

      cmd.CommandText = "select * from Employee where empy_id='" + uid + "'"
      dr = cmd.ExecuteReader()

      Do While dr.Read()
        m_empy_id = dr.GetString(dr.GetOrdinal("empy_id"))
        m_empy_nm = dr.GetString(dr.GetOrdinal("empy_nm"))
        m_dep_id = dr.GetString(dr.GetOrdinal("dep_id"))
        m_role = dr.GetString(dr.GetOrdinal("role"))
        m_email = dr.GetString(dr.GetOrdinal("email"))
        m_status = dr.GetString(dr.GetOrdinal("status"))
      Loop

      dr.Close()
    Catch ex As Exception
      Throw New System.Exception(ex.Message + vbCrLf + ex.StackTrace)
    Finally
      If conn.State = ConnectionState.Open Then
        conn.Close()
      End If

      dr = Nothing
      conn = Nothing
    End Try
  End Sub

  Public Sub SaveData()
    Dim conn As New SqlConnection
    Dim cmd As New SqlCommand
    Dim dr As SqlDataReader
    'Dim trans As SqlTransaction

    Try

      conn.ConnectionString = My.Settings.ConnectionString
      conn.Open()

      'trans = conn.BeginTransaction()

      cmd.Connection = conn
      'cmd.Transaction = trans

      cmd.CommandText = "select * from Employee where empy_id='" + empy_id + "'"
      dr = cmd.ExecuteReader()
      If dr.HasRows Then
        dr.Close()
        cmd.CommandText = "delete from Employee where empy_id='" + empy_id + "'"
        cmd.ExecuteNonQuery()
      End If

      If Not dr.IsClosed Then dr.Close()

      cmd.CommandText = "insert into Employee (empy_id,empy_nm,dep_id,role,email,status) values (?,?,?,?,?,?)"
      cmd.Parameters.Clear()
      cmd.Parameters.Add(New SqlParameter("?", m_empy_id))
      cmd.Parameters.Add(New SqlParameter("?", m_empy_nm))
      cmd.Parameters.Add(New SqlParameter("?", m_dep_id))
      cmd.Parameters.Add(New SqlParameter("?", m_role))
      cmd.Parameters.Add(New SqlParameter("?", m_email))
      cmd.Parameters.Add(New SqlParameter("?", m_status))
      cmd.ExecuteNonQuery()

      'trans.Commit()

    Catch ex As Exception
      'If Not trans Is Nothing Then
      '  trans.Rollback()
      'End If
      Throw New System.Exception(ex.Message + vbCrLf + ex.StackTrace)
    Finally
      If conn.State = ConnectionState.Open Then
        conn.Close()
      End If

      'trans = Nothing
      dr = Nothing
      conn = Nothing
    End Try
  End Sub
End Class

我可以在程式中宣告一個 Global 的物件,讓程式可以直接使用他,在 User 登入後,即將使用者的相關資料抓出來,存放於物件的屬性中(GetData()),如果使用者更改基本資料,那麼我也可以透過 SaveData() 將更新的資料回存到 Table 中,這樣在我的程式中,就不需要去撰寫那麼多的 SQL 存取指令,只要針對物件操作即可。

    Dim oEmp As New clsEmployee

    oEmp.GetData("A0001")

    MsgBox(oEmp.email)

執行結果:

如果這個 Class 會經常用到,那麼將他做成 DLL 或者加入目前的專案,就可以重複使用了。

本系列文章


上一篇
程式設計心法:27.小技巧--每月的最後一天
下一篇
程式設計心法:29.程式設計師的特質
系列文
程式設計心法31
0
fillano
iT邦超人 1 級 ‧ 2009-11-04 10:37:51

這是ActiveRecord嘛。

jamesjan iT邦高手 1 級 ‧ 2009-11-04 12:24:07 檢舉

也許是吧...我沒有研究 ^^
應該是有更好的作法,只是沒有去找

感謝 fillano 大的提醒
http://en.wikipedia.org/wiki/Active\_record\_pattern

.Net 也有這個 Open Source 的 Framework
http://en.wikipedia.org/wiki/Castle\_Project

Castle Project
http://www.castleproject.org/

感謝,讓我多學到好多!^^b

0
my407sw
iT邦新手 5 級 ‧ 2009-11-04 12:33:27

很好的概念,可是您的程式碼不會動,是不是可以修正一下

jamesjan iT邦高手 1 級 ‧ 2009-11-04 13:25:20 檢舉

感謝大大提醒,確實有錯誤(寫了也沒有實際測試,真是慚愧)

GetData() 的 cmd 沒有指定 Connection 就使用是不合法的

還有 Class 宣告成 clsEmployee

物件宣告時卻 dim oEmp as Employee
所以會找不到

0
my407sw
iT邦新手 5 級 ‧ 2009-11-04 13:45:23

所以咯,以後我們發表文章一定要測試再測試,很多人若是看了文章然後跑不動就又懶得找出原因的話就會影響他們的學習興趣,這跟大大您的初衷就有相違背了。

jamesjan iT邦高手 1 級 ‧ 2009-11-04 17:22:42 檢舉

您說得沒錯,確實給我寶貴的一課 ^^b

0
丁丁 (Dean)
iT邦大師 6 級 ‧ 2009-11-04 15:29:45
<pre class="c" name="code">Private _x As Integer
Public Property x() As Integer
    Get
        Return _x
    End Get
    Set(ByVal value As Integer)
        _x = value
    End Set
End Property

在 .NET 3.0 以上可以改寫成

<pre class="c" name="code">Public Property x As Integer = 10
Public Property y As New List(Of Integer)()

簡潔很多呦~ ^o^
參考:http://www.panopticoncentral.net/archive/2008/03/27/23050.aspx

jamesjan iT邦高手 1 級 ‧ 2009-11-04 17:21:43 檢舉

嗯 這樣看起來就舒爽許多

當然,偷懶的話,也可以直接宣告 Public 直接拿來使用
不過這樣就比較沒有 Information Hiding 的概念了

感謝您提供資訊,現在 .Net 4.0 已經 Beta 了 Visual Studio 2010 Beta 2 也免費提供測試,得再 Renew 一下

0
海綿寶寶
iT邦大神 1 級 ‧ 2009-11-05 10:18:06

我看到這個範例
就好希望只要寫一個XML
然後就有什麼XSLT或是什麼工具之類的
1.把XML轉成SQL DDL Script
2.把XML轉成class source code
3.把XML轉成HTML system document
一次滿足三個願望
豈不快哉 ^_^

fillano iT邦超人 1 級 ‧ 2009-11-05 12:21:09 檢舉

ROR(不過Ruby我不熟)或是CakePHP,就可以達到類似的功能。

CakePHP有一個console程式叫做bake,在根據CakePHP的naming conversion做好DB後,就可以用bake產生module/view/controller的程式(scaffolding),當然要做出產品還是要花功夫。

相對於現成工具提供的產生器
或許自已開發一個產生器
會比較符合實際需求

功能不必非常強大
只要達到最小要求即可 ^_^

fillano iT邦超人 1 級 ‧ 2009-11-05 13:13:17 檢舉

我有看過一些人這樣做,也是以xml為base。

0
weihsinchiu
iT邦新手 4 級 ‧ 2009-11-08 14:54:39

嗯!我也都是習慣自己開發工具或是物件來協助我處理這類的資料存取
自訂XML格式,利用物件自動去存取DB
或是將DB中的TABLE Schema,匯出成XML格式!
自動產生Insert\Update的SQL Command
根據要抓的欄位,自動產生Select命令,以及外加條件式!
自行開發這些物件滿好玩的..!!
因為可以完全按照自己的想法和需求,自動化處理資料存取的功能!
有時候一行指令,就可以抓到我所需要的資料列以及所需的欄位..!!

我要留言

立即登入留言