• Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA
  • Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA

    lehongduc > 20-06-12, 06:51 PM

    Chào các Bạn,

    Hôm nay tôi muốn trao đổi với các Bạn về vấn đề "Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA" như một giải pháp tối ưu cho các ứng dụng được thiết kế với VBA trong Microsoft Access. Trao đổi này có đính kèm file nguồn để làm ví dụ minh họa.

    Để lấy một ví dụ cụ thể, ở đây giả định ta có nhu cầu thiết kế 1 ứng dụng Microsoft Access dùng để quản lý 1 danh bạ điện thoại.

    Ứng dụng của chúng ta sẽ bao gồm 1 file dữ liệu và 1 file ứng dụng. Các Bạn có thể tạo File dữ liệu bằng Microsoft Access hoặc SQL SERVER. Ở đây tôi tạo file dữ liệu bằng SQL SERVER.
    File dữ liệu đã được nạp trên 15.000 mẫu tin.
    Khi file ứng dụng được nạp, ta sẽ cho kết nối với file dữ liệu bằng thủ tục Log-In.

    Mục đích của tôi thông qua cách thiết kế trên nhằm:
    + minh họa khả năng của Access VBA có thể lập trình theo hướng đối tượng;
    + kết nối được với nguồn dữ liệu ngoài, ở đây là nguồn SQL SERVER;
    + có thể tạo được những Unbound Form nhằm đáp ứng nhu cầu truy xuất dữ liệu với nhiều người dùng qua mạng máy tính, đồng thời cải thiện được tốc độ xử lý dữ liệu.


    Về Cấu trúc của file dữ liệu:
    Với ứng dụng này ta chỉ cần có 1 file dữ liệu với 1 bảng dữ liệu. Tất nhiên các Bạn có thể tùy biến thêm nếu thấy cần.
    - Tôi đặt tên file dữ liệu này là danhba
    - Và tạo 1 bảng dữ liệu có tên là tblDanhsach, với các cột dữ liệu như sau:
    + Ten: tên của 1 người cụ thể trong danh bạ, kiểu dữ liệu Text
    + HoChulot: họ và chữ lót, kiểu dữ liệu Text
    + Gioitinh: xác định giới tính, kiểu dữ liệu Yes/No (mặc định là Nam, với giá trị là True)
    + Ngaysinh: ngày sinh, kiểu dữ liệu Date
    + Dtdd: số điện thoại di động, kiểu dữ liệu Text
    + Dtnha: số điện thoại ở nhà riêng, kiểu dữ liệu Text
    + Dtvp: số điện thoại ở văn phòng làm việc, kiểu dữ liệu Text

    Với ứng dụng làm ví dụ sẽ cho ta biết cách:
    1. Kết nối với nguồn dữ liệu bên ngoài MS. Access, ở đây là SQL SERVER
    2. Viết 1 Class module như thế nào
    3. Tạo 1 Unbound Form và gắn kết dữ liệu trên đó như thế nào

    Trong bài sau tôi sẽ trình bày tiếp vào nội dung chính của chuyên đề này.
    Rất mong các Bạn cùng tham gia nghiên cứu và trao đổi.

    Nội dung các file đính kèm:
    1. File ứng dụng MS. Access với định dạng mdb có mã nguồn
    2. File SQL (Text) dùng để tạo database trên SQL SERVER cục bộ (local) nếu các Bạn muốn tạo.

    Cũng xin trao đổi rõ thêm: File ứng dụng và file dữ liệu nêu trên mới chỉ là "sườn" còn "thô", để nó trở thành 1 ứng dụng hoàn chỉnh, chúng ta còn phải tinh chỉnh nhiều thứ; đó cũng chính là công việc mà tôi muốn mời các Bạn cùng tham gia trao đổi, qua đó chúng ta thu hoạch được những kiến thức căn bản chắc chắn hơn về chuyên đề này.

    Tài liệu tham khảo:
    Tài liệu tôi dùng để tham khảo chính để viết loạt bài này (bao gồm ứng dụng làm ví dụ) là loạt sách:
    Beginning Access 2003 VBA, Beginning Access 2007 VBA
    của Denise M. Gosnell

    Chào các Bạn,
    Xin nói thêm về chuyện ứng dụng và dữ liệu còn "thô":

    Nói chúng "thô" bởi lẽ:

    1. File dữ liệu SQL SERVER chỉ mới có các bảng dữ liệu thôi. Như vậy chúng chỉ mới là chỗ để lưu dữ liệu phát sinh, chưa làm được việc xử lý dữ liệu (ta dễ thấy một phần những việc đơn giản trong việc xử lý dữ liệu này như: lưu, xóa, trích xuất thông tin, lọc thông tin).
    Bản thân SQL SERVER là 1 hệ thống quản trị cơ sở dữ liệu mạnh, chứ không chỉ đơn thuần là nơi để lưu dữ liệu. Ta sẽ bàn tới cách giao nhiệm vụ xử lý dữ liệu cho cái file dữ liệu SQL SERVER đã tạo ở trên. Hiện nay việc xử lý dữ liệu vẫn còn do file ứng dụng đãm trách thông qua các câu lệnh SQL trong các module.

    2. Nếu chạy file ứng dụng đang có ta sẽ thấy khi mở Form "frmContact" (dùng để cập nhật và xem dữ liệu) sẽ còn mất 1 ít thời gian mà ta có thể cảm nhận được. Mục tiêu của chúng ta là phải làm sao cho nhanh đến mức không cảm thấy phải chờ một chút nào.
    Tôi đã kiểm tra thử mở form nói trên với kết nối internet qua 1 USB 3G của Viettel (loại 7.2 Mbps) trên xe hơi đang chạy: thời gian nạp xong form mất khoảng 25 giây.

    Có Bạn nào tìm được lý do nào khác không?

    Link tải File ứng dụng minh họa, bản cập nhật ngày 15/7/2014:
    http://www.mediafire.com/download/j5v854...150714.rar
  • RE: Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA

    lehongduc > 20-06-12, 11:37 PM

    Class là gì và tại sao ta nên dùng Class trong VBA?

    1. Class là gì?
    Class dùng để tạo ra những Object theo ý muốn của người thiết kế dữ liệu trong Access VBA. Thông qua Class ta có thể tạo ra được những Object với đầy đủ Properties, Method, Even tương tự như những Object có sẵn trong Access VBA.

    Với ứng dụng mẫu đính kèm, ta thấy:
    - Để quản lý đối tượng là danh sách trong danh bạ điện thoại ta tạo ra 1 Object có tên là clsDanhba, thông qua Object này ta có thể:
    + cập nhật hoặc lấy các thông tin chi tiết về từng người có trong danh bạ được lập như: Họ tên, Địa chỉ, số điện thoại, ...
    + Cũng thông qua Object này ta có thể thực hiện được việc xóa, thêm mới danh sách trong danh bạ

    Xem ví dụ trong ClsDanhba trong file ứng dụng, ta tạo được 1 Object với tên là clsDanhba có đầy đủ:
    + các properties như: Danhbaid, Diachi, Dtdd, Dtnha, Dtvp
    + các method như: Delete, Save

    2. Tại sao ta nên dùng Class trong VBA?
    - Sẽ làm cho bộ mã (VBA code) của ứng dụng gọn gàng hơn:
    + Nếu không có Class, ta sẽ phải viết và lặp lại rất nhiều đoạn code giống nhau trong ứng dụng để quản lý thông tin của Danh bạ (lấy và cập nhật thông tin chi tiết, tạo mới, xóa bớt, ...), như vậy sẽ khó khăn cho việc bảo trì và làm cồng kềnh bộ mã ứng dụng, chắc chắn sẽ làm ứng dụng sử nhiều bộ nhớ máy tính hơn.
    - Cũng thông qua Class, ta chỉ cần viết mã 1 lần, sau đó có thể sử dụng Object đã tạo cho nhiều ứng dụng cùng 1 nhóm (như 1 Add-in).
    Các Bạn có thể thấy rằng, để thiết kế 1 ứng dụng quản lý công việc bán hàng chẳng hạn, nếu tạo ra 1 Object để quản lý danh sách khách hàng. Sau đó ta có thể gọi Object này ra để sử dụng trong nhiều phân hệ khác nhau như: phân hệ quan hệ khách hàng, phân hệ công nợ, ...
    Rộng ra một chút, nếu tạo ra được 1 Object để quản lý các chứng từ nhập xuất phát sinh. Sau đó ta có thể gọi Object này ra để sử dụng trong các phân hệ như: phân hệ quản lý biến động kho hàng, phân hệ quản lý chế độ chiết khấu – khuyến mại, phân hệ quản lý công nợ phát sinh do việc mua bán hàng,...

    Như vậy, ta đã viết 1 lần và sử dụng ở nhiều nơi khác nhau, mà không phải viết lại bộ mã để quản lý trong từng phân hệ của ứng dụng.

    Các Bạn có để ý thấy bằng việc Bác Bill chỉ cần viết 1 lần thư viện quản lý dữ liệu ADO, ta đã có thể sử dụng thư viện ADO này trong bất kỳ ứng dụng quản trị dữ liệu nào, chỉ cần “nạp và yên tâm xài thôi”. Ta viết Class cũng nhằm như vậy.
    Trong một dịp khác, chúng ta sẽ trao đổi sâu hơn về cách thiết kế một thư viện kiểu như vậy với Access VBA, còn lúc này hãy tập trung cho cái chuyên đề chính này đã.

    Vậy cách thức để tạo ra 1 Class trong Access VBA như thế nào? Xin xem bài sau sẽ rõ.

    Các Bạn có thể tham khảo giải thích chính thức của Bác Bill về Class ở link sau nhé: Source: http://msdn.microsoft.com/en-us/libr...4(v=office.10)
  • RE: Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA

    lehongduc > 21-06-12, 01:04 PM

    Chào các Bạn,

    Hôm qua, thông qua email gửi trực tiếp cho tôi một số Bạn đã phát hiện được 2 vấn đề trong file ứng dụng minh họa:
    1. Nếu bỏ trống 1 vài chi tiết trên form nhập danh sách sẽ phát sinh lỗi và không cập nhật được.
    2. Nhập vào rồi làm sao tìm, và các Bạn này muốn thêm công cụ tìm danh sách.

    Tôi đã định những vấn đề trên sẽ được bổ sung dần trong quá trình chúng ta trao đổi về chuyên đề này, song nhận thấy có ít ý kiến tham gia trao đổi, nên hôm nay tôi tải lên đây file ứng dụng đã được bổ sung 2 vấn đề trên.

    Xin tải file về từ link sau: http://www.mediafire.com/?ewyxee99pn6212i

    Xin nói rõ thêm về những bổ sung trong file ứng dụng mới này:
    1. Thay vì bổ sung thêm 1 cửa sổ tìm kiếm, tôi sử dụng ngay form fmContacts để làm việc này luôn. Khi nào cần tìm, các Bạn bấm vào nút "Nhập mới" để xóa trống các ô dữ liệu, sau đó nhập các yếu tố cần tìm vào ô tương ứng và bấm nút "Tìm kiếm"

    2. Với chi tiết "Ngày sinh", một số Bạn cho rằng có nhu cầu bỏ trống khi chưa thu thập được thông tin cá nhân này. Do vậy tôi đã thay đổi Class clsDanhba với khai báo biến tương ứng thành Variant (thay vì Date như bản trước) để cho phép bỏ trống chi tiết này.

    Rất mong các Bạn nào có thắc mắc gì xin cứ đăng ý kiến thảo luận lên diễn đàn cho mọi người cùng tham khảo sẽ có hiệu quả chung lớn hơn.

    Chiều tối hôm nay tôi sẽ đăng tiếp bài về cách thức tạo 1 Class trong Access VBA. Mời các Bạn đón đọc và tham gia trao đổi.
  • RE: Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA

    lehongduc > 22-06-12, 08:35 AM

    Cách thức tạo 1 Class trong Access VBA

    1. Chèn 1 Class module:
    - Trong cửa sổ Database, chọn Modules và bấm nút lệnh New
    - Trong cửa sổ “Microsoft Visual Basic” đã được mở ngay sau đó, bấm menu “Insert” sổ xuống và chọn mục lệnh “Class module”, 1 trang Class module được mở ra. Ta sẽ viết code trong trang Class module này để tạo ra 1 Class

    2. Viết Class:
    - Như bài 2 đã đề cập, ta dùng Class modules để thiết kế những Object theo ý riêng của mình. Mỗi Object như vậy sẽ có các property, method và cũng có thể có các event
    Với ứng dụng ta đang sử dụng để minh họa:
    + Property: tương ứng với từng cột dữ liệu trong bảng dữ liệu
    + Method: tương ứng với các tác vụ như: lưu mới hoặc cập nhật các thay đổi trong bảng dữ liệu, xóa dòng trong bảng dữ liệu

    Với Object mà ta định thiết kế để quản lý tập trung Danh bạ điện thoại (ta gán cho cái tên là clsDanhba):

    - Ta sẽ có các properties chính là các cột dữ liệu trong bảng Danh sách, đó là:
    + Tên, Họ và Chữ lót, giới tính, ngày sinh, địa chỉ, số điện thoại di dộng, số điện thoại ở nhà riêng, số điện thoại ở văn phòng làm việc.

    Như vậy, ta sẽ có 8 properties tương ứng của Object clsDanhba, gồm: Ten, Hochulot, Gioitinh, Ngaysinh, Diachi, Dtdd, Dtnha, Dtvp

    - Ta cũng sẽ cần có các Method:
    + Để lưu và cập nhật dữ liệu, ở đây tôi đặt tên method này là “Save”
    + Để xóa dữ liệu, , ở đây tôi đặt tên method này là “Delete”
    + Để nạp các giá trị là giá trị từ các cột trong bảng dữ liệu cho các properties của clsDanhba, ở đây tôi đặt tên method này là “PopulatePropertiesFromRecordset”.
    Mục đích tạo ra method này nhằm cho nạp các giá trị của bảng dữ liệu vào các ô dữ liệu tương ứng trên form.
    + Để nạp các giá trị là giá trị từ các ô dữ liệu trên form danh sách cho các properties của clsDanhba, ở đây tôi đặt tên method này là “PopulatePropertiesFromForm”.
    Mục đích tạo ra method này nhằm cho ghi lại các giá trị đã nhập trên form vào bảng dữ liệu

    - Để khai báo các properties cho clsDanhba:
    + Mỗi một property ta viết 2 procedure: 1 procedure để lấy giá trị của property (Get value), và 1 procedure để gán giá trị cho property (Let value).
    Xem file minh họa với clsDanhba, ta lấy ra 1 đoạn với 4 procedure:
    Mã:
    'Ten
    Public Property Get Ten() As String
        On Error Resume Next
        Ten = strTen
    End Property

    Public Property Let Ten(ByVal Value As String)
        On Error Resume Next
        strTen = Value
    End Property
    ‘-----------------------------------------------------------------------
    'HoChulot
    Public Property Get HoChulot() As String
        On Error Resume Next
        HoChulot = strHochulot
    End Property

    Public Property Let HoChulot(ByVal Value As String)
        On Error Resume Next
        strHochulot = Value
    End Property
    Từ đó ta dễ dàng rút ra nhận xét về dạng chung của 1 procedure phải không các Bạn.


    - Nếu cần, trong Class module ta cũng có thể viết thêm các Event cho Object ta định quản lý, nhằm mục đích bẩy 1 sự kiện nào đó có liên quan đến Object này.
    Thông thường, ta có các Event để làm nhiệm vụ nạp Class (Class_Initialize) và đóng Class (Class_Terminate) theo theo dạng thức như sau:
    Mã:
    Private Sub Class_Initialize()
       ‘viết code của Bạn ở vùng này
    End Sub

    Private Sub Class_Terminate()
       ‘viết code của Bạn ở vùng này
    End Sub
    Các Bạn có thể tham khảo chi tiết hướng dẫn của Bác Bill về cách viết các Event Procedure tại link sau nhé: http://msdn.microsoft.com/en-us/library/...office.10)

    Để hiểu rõ hơn xin mời các Bạn mở file ứng dụng minh họa và xem nội dung Class module “clsDanhba” nhé.

    Bài viết còn nữa, xin mời các bạn xem bài sau sẽ rõ.
  • RE: Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA

    lehongduc > 23-06-12, 08:51 PM

    Chào các Bạn,

    Trước khi tiếp tục nội dung chính của chuyên đề này, tôi xin trao đổi cùng các Bạn một số vấn đề mang tính chất "bếp núc" với file ứng dụng chúng ta đang sử dụng.

    1. Vấn đề 1: Linh hoạt việc kết nối file ứng dụng với file dữ liệu bất kỳ.
    Như các Bạn đã thấy trong file ứng dụng, chúng ta có thể tùy ý kết nối đến file dữ liệu SQL SERVER bất kỳ mà ta muốn. Các thủ tục kết nối dữ liệu trong file ứng dụng này hoàn toàn không cố định phải kết nối đến 1 file dữ liệu nào cả.
    Để làm được điều đó, ứng dụng có 1 Procedure để kết nối đến file dữ liệu có thể tùy chọn được, như các Bạn thấy trong module "modQuanlyDulieu":
    Mã:
    Sub OpenDbConnection()
    '
    'Co the tham khao chuoi ket noi den cac nguon du lieu khac nhau
    'tai dia chi sau: www.connectstring.com


        On Error GoTo HandleError
        Dim vServer, vData, vUser, vPsw, vLogInDft As Boolean


        With Forms("frmLogIn")
            vLogInDft = !chkLogIn.Value
            If vLogInDft = True Then
                vServer = "mssql.quantribanhang.vn"
                vData = "danhba"
                vUser = "nhanvien1"
                vPsw = "Nv001"
            Else
                vServer = !txtServer
                vData = !txtData
                vUser = !txtUser
                vPsw = !txtPsw
            End If
        End With
        Set cnConn = New ADODB.Connection
        cnConn.Open _
            "Provider = sqloledb;" & _
            "Data Source=" & vServer & ";" & _
            "Initial Catalog=" & vData & ";" & _
            "User ID=" & vUser & ";" & _
            "Password=" & vPsw & ";"
        
        Exit Sub


    HandleError:
        GeneralErrorHandler Err.Number, Err.Description, DB_QUANLY, "OpenDbConnection"
        Exit Sub


    End Sub
    Đồng thời thiết kế 1 form để LogIn vào server và file dữ liệu xác định. Form này có tên là "frmLogIn".
    Ngay trong procedure trên cũng đã tham chiếu đến các giá trị được người chạy ứng dụng khai báo trên Form này khi mở form Cập nhật Danh bạ (form "frmContacts").

    Có Bạn đã hỏi tôi, nếu muốn kết nối đến file dữ liệu thiết kế bằng Microsoft Access có được không?
    Hoàn toàn được các Bạn ạ. Chỉ cần khai báo lại đoạn sau trong procedure nêu trên:
    Mã:
    cnConn.Open _
            "Provider = sqloledb;" & _
            "Data Source=" & vServer & ";" & _
            "Initial Catalog=" & vData & ";" & _
            "User ID=" & vUser & ";" & _
            "Password=" & vPsw & ";"
    thành chuỗi kết nối đến dữ liệu Microsoft Access. Các Bạn có thể tra cứu chuỗi kết nối thích hợp tại trang www.connectstring.com
    Trong trường hợp này, các Bạn phải chú ý sửa lại form "frmLogIn" và các đoạn code có liên quan trong procedure nêu trên cho phù hợp nhé.

    Vấn đề 2. Xử lý những thông tin của Object bị bỏ trống (Null value) như thế nào?
    Các giá trị bị bỏ trống nói ở đây có thể là giá trị trong các ô dữ liệu trên form "frmContacts" hoặc trong bảng dữ liệu SQL SERVER.
    Để xử lý trường hợp này ứng dụng có 1 Function có tên là FixNull cũng ở bên trong module nêu trên:
    Mã:
    Function FixNull(varIn As Variant) As String
        If IsNull(varIn) Then
            FixNull = ""
        Else
            FixNull = varIn
        End If
    End Function
    Và trong 2 procedure có liên quan trong Class module "clsDanhba" ứng dụng đã sử dụng Function FixNull này để khử các giá trị Null như các Bạn đã thấy:
    Mã:
    Sub PopulatePropertiesFromForm()
    'Lay thong tin tu Form frmContacts de gan gia tri cac thuoc tinh cho objDanhba
        On Error GoTo HandleError
        
        With Me
            .Ten = FixNull(Forms("frmContacts")!txtTen)
            .HoChulot = FixNull(Forms("frmContacts")!txtHoChulot)
            .Diachi = FixNull(Forms("frmContacts")!txtDiachi)
            .Dtdd = FixNull(Forms("frmContacts")!txtDtdd)
            .Dtnha = FixNull(Forms("frmContacts")!txtDtnha)
            .Dtvp = FixNull(Forms("frmContacts")!txtDtvp)
            If Len(Forms("frmContacts")!txtNgaysinh) > 0 Then
                .Ngaysinh = Forms("frmContacts")!txtNgaysinh
            Else
                .Ngaysinh = Null
            End If
            .Gioitinh = Forms("frmContacts")!frmGioitinh.Value
        End With
        Exit Sub

    HandleError:
        GeneralErrorHandler Err.Number, Err.Description, CLS_DANHBA, "PopulatePropertiesFromForm"
        Exit Sub

    End Sub

    Mã:
    Sub PopulatePropertiesFromRecordset(rsCont As ADODB.Recordset)
    'Lay thong tin tu Recordset rsCont de gan gia tri cac thuoc tinh cho objDanhba

        On Error GoTo HandleError
        
        With Me
            .DanhbaId = rsCont!DanhbaId
            .Ten = Trim(FixNull(rsCont!Ten))
            .HoChulot = Trim(FixNull(rsCont!HoChulot))
            .Diachi = Trim(FixNull(rsCont!Diachi))
            .Dtdd = Trim(FixNull(rsCont!Dtdd))
            .Dtnha = Trim(FixNull(rsCont!Dtnha))
            .Dtvp = Trim(FixNull(rsCont!Dtvp))
            If Not IsNull(rsCont!Ngaysinh) Then
                .Ngaysinh = rsCont!Ngaysinh
            Else
                .Ngaysinh = ""
            End If
            .Gioitinh = rsCont!Gioitinh
        End With
        Exit Sub

    HandleError:
        GeneralErrorHandler Err.Number, Err.Description, CLS_DANHBA, "PopulatePropertiesFromRecordset"
        Exit Sub

    End Sub
    Vấn đề 3: Vấn đề bẩy lỗi trong các module:
    Như các Bạn đã thấy, ứng dụng có 1 procedure để bẩy các lỗi có thể phát sinh khi chạy các thủ tục trong ứng dụng:
    Mã:
    Public Sub GeneralErrorHandler(lngErrNumber As Long, strErrDesc As String, strModuleSource As String, strProcedureSource As String)

        On Error Resume Next
        
        Dim strMessage As String
        
        'build the error message string from the parameters passed in
        strMessage = "An error has occurred in the application."
        strMessage = strMessage & vbCrLf & "Error Number: " & lngErrNumber
        strMessage = strMessage & vbCrLf & "Error Description: " & strErrDesc
        strMessage = strMessage & vbCrLf & "Module Source: " & strModuleSource
        strMessage = strMessage & vbCrLf & "Procedure Source: " & strProcedureSource
        
        'display the message to the user
        MsgBox strMessage, vbCritical
        
        Exit Sub

    End Sub
    Và trong các procedure viết trong ứng dụng, đều có khai báo dòng bẩy lỗi tham chiếu đến procedure GeneralErrorHandler nêu trên:
    Mã:
    ...
    On Error GoTo HandleError
    ...
    HandleError:
        GeneralErrorHandler Err.Number, Err.Description, <Tên module>, <Tên procedure>
        Exit Sub
    Với cách làm như vậy, chúng ta sẽ dễ dàng quản lý được lỗi phát sinh, thậm chí xác định chính xác lỗi phát sinh ở procedure nào nằm trong module nào.

    Tạm thời xin trao đổi với các Bạn 3 chuyện bếp núc như vậy. Mời các Bạn cho thêm ý kiến nhé.
  • RE: Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA

    lehongduc > 24-06-12, 07:16 AM

    Chào các Bạn,

    Xin trao đổi thêm một chuyện bếp núc nữa mà rất đông các Bạn khi mới sử dụng Access VBA để thực hiện các câu lệnh SQL hay mắc phải đó là:
    Vấn đề 4: Cập nhật chuỗi Unicode: Với file ứng dụng minh hoạ, trong module "modQuanlyDulieu" tại procedure "BuildSQLInsertDanhba" ta thấy có đoạn code sau:
    Mã:
    ...
        strSQLInsert = "INSERT INTO " & sChemaName & ".tblDanhsach(ten,hochulot, diachi,dtdd, dtnha, dtvp,ngaysinh, gioitinh)"
        strSQLInsert = strSQLInsert & " VALUES ("
        strSQLInsert = strSQLInsert & "N'" & objDanhba.Ten & "', "
        strSQLInsert = strSQLInsert & "N'" & objDanhba.HoChulot & "', "
        strSQLInsert = strSQLInsert & "N'" & objDanhba.Diachi & "', "
        strSQLInsert = strSQLInsert & "'" & objDanhba.Dtdd & "', "
        strSQLInsert = strSQLInsert & "'" & objDanhba.Dtnha & "', "
        strSQLInsert = strSQLInsert & "'" & objDanhba.Dtvp & "', "
    ...
    Trong đoạn code nêu trên các Bạn chú ý ký tự N đặt trước các biến chuỗi khi cho ghép thành câu lệnh SQL. Đó chính là quy ước để cập nhật chuỗi unicode trong trường hợp ta đang bàn đến.
    Nếu không có ký tự N đặt trước chuỗi, chuỗi unicode sẽ được lưu thành chuỗi thường, và khi lấy giá trị các chuỗi đó ra từ bảng dữ liệu ta sẽ có chuỗi không còn dấu tiếng Việt đầy đủ nữa.

    Xin chú ý: N phải đặt trước dấu nháy trên rồi mới tới chuỗi unicode nhé. Các Bạn xem lại câu lệnh đã được phóng to lên cho dễ thấy dấu nháy trên ngay sau ký tự N nhé:

    Code:
    strSQLInsert = strSQLInsert & "N'" & objDanhba.Ten & "', "
  • RE: Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA

    lehongduc > 26-06-12, 08:32 AM

    Chào các Bạn,

    Hôm nay xin tiếp tục trao đổi cùng các Bạn về nội dung chính của chuyên đề này:

    Khai báo biến đối tượng để sử dụng class đã tạo như thế nào?

    Rất đơn giản, ta khai báo biến đối tượng và sau đó gán biến đối tượng đã khai báo là 1 thành phần mới của class như đoạn code dưới đây:
    Mã:
    Dim objDanhba As clsDanhba
    Set objDanhba = New clsDanhba
    ...
    Khi khai báo và gán biến đối tượng ta phải chú ý "câu thần chú sau: [COLOR="red"]mở ra xài rồi phải đóng lại[/COLOR]", nghĩa là: khi không còn nhu cầu sử dụng biến đối tượng đã khai báo và đã gán nữa thì ta phải cho đóng lại theo cách tương tự như đoạn code bên dưới:
    Mã:
    ...
        rsDanhba.Close
        Set rsDanhba = Nothing
    ...
    Câu lệnh đầu "rsDanhba.Close" có tác dụng đóng Class clsDanhba lại, câu lệnh thứ hai "Set rsDanhba = Nothing" có tác dụng xoá biến đối tượng đã gán. "Đóng" và "Xoá" ở đây để giải phóng bộ nhớ máy tính đã được cấp phát để quản lý đối tượng ta đã khai báo trước đó. Như vậy là các Bạn đã rõ việc này có vai trọng như thế nào rồi phải không.

    Vấn đề ở đây là phải xác định đúng "khi nào cần dùng" và "khi nào không cần dùng nữa" để "mở" và "đóng" đúng lúc.
    Xin dẫn ra đây ví dụ cụ thể ngay trong file ứng dụng minh hoạ chúng ta đang dùng:

    Với form "frmContacts", do mục đích sử dụng form này là để cập nhật và trình bày các thông tin chi tiết của danh bạ nên ta sẽ cần phải dùng đến đối tượng ta đã tạo trong class "clsDanhba", vì vậy:

    + Khi form này được nạp lên màn hình, ta phải gán biến đối tượng danh bạ đã tạo ngay tại sự kiện "Form được nạp - Load_Event với thủ tục Form_Load".
    Đồng thời ta cũng nhận thấy rằng biến đối tượng này ta sẽ phải sử dụng đến từ lúc form này được mở (khi cần dùng đến) cho đến lúc đóng nó lại (khi không cần dùng nữa), nên ta sẽ khai báo biến đối tượng này là biến dùng chung cho tất cả các thủ tục (procedure) có trong form,
    Và chỉ đóng nó lại khi ta đóng form lại (sự kiện Unload_Event với thủ tục Form_Unload).

    Vậy ta khai báo biến đối tượng này ở đâu? Ở trong từng thủ tục bên trong form "frmContacts" chăng?

    Các Bạn xem trang code của form "frmContacts" sẽ thấy các biến dùng chung trong "nội bộ" form này được khai báo ở đầu trang code, các dòng khai báo này đều nằm bên ngoài các thủ tục (procedure) như đoạn code được trích bên dưới đây.

    Nếu khai báo trong từng thủ tục sẽ không đạt được nhu cầu sử dụng biến đối tượng ta đã nêu ở trên (xét trong trường hợp cụ thể là file ứng dụng minh hoạ mà chúng ta đang dùng), vì khi thủ tục sự kiện hoàn tất (nghĩa là sự kiện đã hoàn thành) các biến đã khai báo và được gán sẽ bị đóng lại, các thủ tục khác không thể dùng (kế thừa) chúng được.

    Mã:
    Option Compare Database
    Option Explicit

    Dim blnAddMode As Boolean
    Dim rsDanhba As ADODB.Recordset
    Dim objDanhba As clsDanhba
    Const Danhba_FORM = "frmDanhba"
    Dim intCurrDanhbaRecord As Integer
    Dim rsSearch As ADODB.Recordset
    Dim RecSearch As Boolean

    Sáng hôm nay ta tạm thời trao đổi chừng ấy. Xin hẹn các Bạn sẽ bàn tiếp vào chiều hôm nay
  • RE: Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA

    lehongduc > 26-06-12, 11:12 AM

    Chào các Bạn,

    Có Bạn vừa gọi hỏi tôi 2 vấn đề:

    1. Có thể đổi tên class module "clsDanhba" thành tên khác (chẳng hạn như "LopDanhba") được không?

    Đổi tên khác được, nhưng phải làm thêm một việc rất mất công là phải thay đổi tất cả các tham chiếu đến class module đã đổi tên. Cũng cần nói rõ hơn, nếu đổi tên "clsDanhba" thành "LopDanhba" thì khi tham chiếu đến đối tượng tương ứng cũng phải tham chiếu theo tên đã đổi.

    Thí dụ:
    Nếu đã khai báo biến đối tượng objDanhba bằng câu khai báo: Dim objDanhba As clsDanhba
    thì cũng phải đổi dòng khai báo tên thành: Dim objDanhba As LopDanhba

    Tuy nhiên, nếu Bạn muốn chuẩn hóa công việc thiết kế ứng dụng của mình khi viết code (là yêu cầu bắt buộc của làm việc khoa học), phải tuân thủ quy tắc thống nhất trong cách đặt tên biến, tên module, tên thủ tục, ... Để làm gì vậy? Để dễ nhận diện và quản lý chúng. Đừng bao giờ đặt tên tùy hứng rồi sẽ đến ngày Bạn phải trả giá rất đắt khi phải xới tung đám rừng code trong ứng dụng để tìm được đúng cái mình cần đấy.

    Về cái sự "chuẩn hóa" này, như các Bạn đã thấy trong file ứng dụng minh họa, việc đặt tên đều theo 1 quy ước thông nhất đấy nhé:
    + Tên form bắt đầu bằng tiền tố "frm", như: frmContacts, frmLogIn
    + Tên module bắt đầu bằng tiền tố "mod", như: modQuanlyDulieu, modQuanlyRecord
    + Tên class module bắt đầu bằng tiền tố "cls", như: "clsDanhba"
    + Tên 1 biến đối tượng (object variabe) bắt đầu bằng tiền tố "obj", như: objDanhba
    ...
    Bấy giờ sang vấn đề thứ hai:
    2. Từ khóa "Me" trong một số thủ tục bên trong class module "clsDanhba" có ý nghĩa như thế nào? Có phải chỉ form đang mở?

    Từ khóa "Me" mà các Bạn thấy ở một số thủ tục trong class module "clsDanhba" là để chỉ bản thân class mình đang mở đấy. Chằng hạn thủ tục sau bên trong class module này:
    Mã:
    Sub PopulatePropertiesFromRecordset(rsCont As ADODB.Recordset)
    'Lay thong tin tu Recordset rsCont de gan gia tri cac thuoc tinh cho objDanhba

        On Error GoTo HandleError
        
        With Me
            .DanhbaId = rsCont!DanhbaId
            .Ten = Trim(FixNull(rsCont!Ten))
            .HoChulot = Trim(FixNull(rsCont!HoChulot))
            .Diachi = Trim(FixNull(rsCont!Diachi))
            .Dtdd = Trim(FixNull(rsCont!Dtdd))
            .Dtnha = Trim(FixNull(rsCont!Dtnha))
            .Dtvp = Trim(FixNull(rsCont!Dtvp))
            If Not IsNull(rsCont!Ngaysinh) Then
                .Ngaysinh = rsCont!Ngaysinh
            Else
                .Ngaysinh = ""
            End If
            .Gioitinh = rsCont!Gioitinh
        End With
        Exit Sub

    HandleError:
        GeneralErrorHandler Err.Number, Err.Description, CLS_DANHBA, "PopulatePropertiesFromRecordset"
        Exit Sub

    End Sub
    Nếu chú ý, bên trong 1 thủ tục đang viết trong class module này, khi Bạn nhập vào từ khóa "Me" với dấu "." liền ngay sau đó sẽ thấy 1 popup sổ xuống liệt kê tất cả các properties và method đã viết trong class module này (đó cũng chính là properties và method của đối tượng ta tự tạo thông qua class module đang mở)

    Như vậy, trong class module, từ khóa "Me" không phải chỉ form đang mở các Bạn nhé.
  • RE: Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA

    lehongduc > 28-06-12, 09:07 AM

    Hôm nay chúng ta sẽ trao đổi tiếp tục về chuyên đề này.

    Làm sao để khai báo 1 property của Object tự tạo là Read-Only (chỉ đọc mà thôi), nghĩa là ta chỉ có thể lấy được giá trị của property này, chứ không thể gán giá trị cho nó được.
    Rất đơn giản, ta chỉ cần không khai báo thủ tục Property Let trong Class module là xong.
    Lấy ví dụ cụ thể trong file ứng dụng minh họa mà chúng ta đang khảo sát, giả định ta muốn property DanhbaId là Read-Only, ta sẽ xóa thủ tục Public Property Let DanhbaId ra khỏi clsDanhba, chính là bỏ đi thủ tục ghi dưới đây:
    Mã:
    Public Property Let DanhbaId(ByVal Value As Long)
        On Error Resume Next
        lngDanhbaid = Value
    End Property
    Ngược lại, nếu muốn khai báo 1 property của Object tự tạo là Write-Only (chỉ ghi mà thôi), nghĩa là ta chỉ có thể gán giá trị cho property này, chứ không thể đọc giá trị của nó ra được.
    Cũng tương tự như trên, ta chỉ cần không khai báo thủ tục Property Get trong Class module là xong.
    Với file ứng dụng minh họa, nếu ta muốn property DanhbaId là Write-Only, ta sẽ xóa thủ tục Public Property Get DanhbaId ra khỏi clsDanhba, chính là bỏ đi thủ tục ghi dưới đây:
    Mã:
    Public Property Get DanhbaId() As Long
        On Error Resume Next
        DanhbaId = lngDanhbaid
    End Property

    Có Bạn hỏi tôi: có thể tạo ra nhiều Object trong cùng 1 class module hay không? Câu trả lời dứt khoát là không.
    Mỗi Class module chỉ được dùng để tạo 1 Object thôi.

    Như vậy là chúng ta đã khảo sát xong những vấn đề cơ bản về cách thức tạo 1 Object theo ý riêng thông qua công cụ Class module trong Access VBA.

    Trong bài kế tiếp ta sẽ trao đổi về 1 vấn đề có liên quan là làm sao để quản lý được tất cả các thành phần riêng lẻ của 1 Object tự tạo? Kiểu như quản lý tập hợp nguyên cả cái Danh bạ, bao gồm các công việc như: thêm , xóa, đếm số lượng thành phần, ...
  • RE: Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA

    lehongduc > 29-06-12, 11:48 AM

    Rất mong các Bạn tham gia trao đổi về chuyên đề này. Các Bạn có thể trao đổi về:
    1. Nội dung bài viết, đúng, sai?
    2. Những giải pháp khác của Bạn xung quanh vấn đề chúng ta đang bàn
    3. Những thắc mắc phát sinh khi chạy file ứng dụng minh họa
    Và những vấn đề có liên quan khác.
    Thú thật, một mình độc thoại thấy cũng hơi vắng thiếu.

    Mong các Bạn nhiệt tình tham gia trao đổi.