Kết nối C# với Database

Các bài viết trong: Quan hệ C# và Database
Phần 1: Kết nối C# với Database
Phần 2: Sql Command
Phần 3: SqlDataReader & Dataset
Phần 4: Stored Procedure
Phần 5: Function và Trigger

Việc hiểu quan hệ giữa C# và Database trong việc xây dựng các ứng dụng (website) là rất quan trọng, Trong loạt bài viết này sẽ cung cấp các kiến thức nền tảng trong việc kết nối C# với Database. Bạn xem và bổ xung kiến thức cho khả năng lập trình của mình.

Trong bài viết đầu tiên này mình sẽ giới thiệu về  Kết nối C# với Database

Microsoft cung cấp các "data provider" như sau:

Data Provider Namespace Connection Class
ODBC System.Data.Odbc OdbcConnection
OLE DB System.Data.Oledb OledbConnection
Oracle System.Data.OracleClient OracleConnection
SQL Server System.Data.SqlClient SqlConnection
SQL Server CESystem.Data.SqlServerCe SqlCeConnection

Trong đó thường sử dụng nhất là: ODBC, OLE DB, SQL Server. Trong đó SQL là được dùng rộng rãi nhất, vì vậy SQL sẽ được chọn để minh họa. Và mình sẽ sử dụng Northwind database là cơ sở dữ liệu mẫu có sẵn khi bạn cài SQL server.
Ta sẽ bắt đầu bằng việc mở kết nối như ví dụ dưới đây:

 

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Data.SqlClient;

public partial class Database : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string connectString = @"Server =.\SQL2005;Initial Catalog=Northwind;User ID=sa;Password=******";
        // Tạo một connection tới máy chủ
        SqlConnection conn = new SqlConnection(connectionString);
        try
        {
            conn.Open();
            //thuc hien cac cong viec can thiet o day        
        }
        catch (SqlException ex)
        {
            Console.WriteLine("Error: " + ex);
        }
        finally
        {
            conn.Close();
        }
    }
}

Như vậy là xong việc kết nối.

 


Lưu ý:

  • Khối lệnh try..catch..finally là khối lệnh bạn nên luôn luôn thực hiện vì những lý do sau: Nếu bạn thực hiện lệnh conn.Open() thành công nó sẽ nhảy xuống thực hiện khối lệnh trong finally là conn.Close(), còn nếu kết nối không thành công nó sẽ xử lý ngoại lệ (Khối catch), sau đó cũng nhảy xuống thực hiện conn.Close(). Như vậy ta thấy dù kết nối thành công hay ko thành công thì việc conn.Close() trong khối lệnh finally đều được thực thi và nó sẽ hạn chế sự hao tổn tài nguyên. Vậy là cứ mở kết nối thực thi các yêu cầu xong ta lại đóng kết nối lại -> Sẽ không tốn tài nguyên.
  • Nếu bạn sử dụng  Windows Authentication thì chuỗi kết nối như sau: string connectString = @"Server =.\SQL2005;database=Northwind;Integrated Security = true";

Xử lý từ khóa tìm kiếm cho bài viết bằng SQL

Trong bài viết thương yêu cầu có tìm kiếm từ khóa và mình cần hiển thị cho người dùng biết các từ khóa có liên quan đến bài viết. Bài viết này mình sẽ giới thiệu một cách để thực hiện.

Tuy chưa thực sự tối ưu vì trong thuật toán có dùng con trỏ để tìm kiếm. Nếu có thời gian chịu khó phân tích lại thì sẽ tối ưu thuật toán hơn.

Ví dụ bạn có bài viết được mô tả như sau:

Bạn sẽ thấy bên dưới phần diễn giải của bài viết có các từ khóa liên quan.
Để làm được điều đó bạn cần tạo một bảng (TB_TuKhoa) để lưu trữ những từ khóa (Gọi nôm na là kho từ khóa).

 

CREATE TABLE [dbo].[TB_TuKhoa](
    [idKey] [int] IDENTITY(1,1) PRIMARY KEY  NOT NULL,
    [TuKhoa] [nvarchar](250) NOT NULL,
    [GhiChu] [nvarchar](250) NULL,
)

 Ví dụ dữ liệu của bạn như sau:

 

Giờ chúng ta sẽ viết thuật toán để lấy ra các từ khóa.

 Bạn viết 1 hảm để trả về kết quả là từ khóa của bài viết như sau

* Input NoiDung bài viết (Nvarchar)
* Output: Từ khóa liên quan

CREATE FUNCTION GetKeyword(
    @NoiDung nvarchar(4000)
)
RETURNS NVarChar(4000) 
AS
BEGIN
    DECLARE @KetQua nvarchar(4000) SET @KetQua='Từ khóa: '
    -- Khai báo 1 bảng tạm
    DECLARE @_Table TABLE  (Noidung nvarchar(4000) null)
    INSERT INTO @_Table(Noidung) VALUES (@NoiDung)
    DECLARE @Count int
    --Dùng con trỏ (vòng lặp) để tìm kiếm từ khóa
    DECLARE @TuKhoa Nvarchar(250) 
    DECLARE TuKhoa_Cursor CURSOR FOR 
    SELECT TuKhoa  FROM TB_TuKhoa order by newid()
    OPEN TuKhoa_Cursor 
    FETCH NEXT FROM TuKhoa_Cursor INTO @TuKhoa 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
    -- Nếu tìm thấy trong nội dung có từ khóa thì cộng thêm kết quả
     SELECT @Count =Count(*) FROM @_Table 
        WHERE Noidung LIKE N'%'+@TuKhoa+'%'
     IF @Count>0
        BEGIN
            SET @KetQua=@KetQua+@TuKhoa+', '
        END
    FETCH NEXT FROM TuKhoa_Cursor INTO @TuKhoa 
    END
    CLOSE TuKhoa_Cursor
    DEALLOCATE TuKhoa_Cursor

RETURN @KetQua
END
 Để sử dụng hàm trên và tránh việc mỗi lần view bài viết bạn lại yêu cầu thực hiện hàm đó thì  trong bảng bài viết (TB_BaiViet) của bạn tạo thêm trường Từ khóa
Dữ liệu trường này = dbo.GetKeyword(NoiDung)
 
Ví dụ:
SELECT dbo.GetKeyword(N'Bài viết này giới thiệu một số thủ thuật 
và lời khuyên liên quan đến những thắc mắc mà người dùng văn phòng 
thường gặp phải trong quá trình sử dụng Excel, Word.')
Sẽ cho kết quả là:  Từ khóa: WORD, Thủ thuật, EXCEL, lời khuyên, thắc mắc, người dùng , người dùng văn phòng , sử dụng Excel, 

Cách Insert và tạo bảng dữ liệu "động" trong SQL Server

Như chúng ta đã biết đối với các hệ thống Logging thì dữ liệu luôn được cập nhật hàng tháng, hàng ngày vì vậy đòi hỏi CSDL phải tương thích có nghĩa là Log trong tháng nào thì hệ thống sẽ Insert vào tháng đó tương ứng tráng việc Insert nhiều trong cùng 1 bảng dẫn đến việc Select chậm khi trả về kết quả.

Vì vậy đối với Log của mỗi tháng chúng ta sẽ tạo ra các bảng vd:

Tháng 1 năm 2010 hệ thống sẽ tự động tạo ra bảng tbl_Month_1_2010
Tháng 2 năm 2010 hệ thống sẽ tự động tạo ra bảng tbl_Month_2_2010
...
Tháng 12 năm 2010 hệ thống sẽ tự động tạo ra bảng tbl_Month_12_2010

Như vậy làm sao để hệ thống có thể tự tạo ra các bảng tương ứng với năm và tháng như vậy ?

Giải pháp của tôi là sử dụng SQL Job trong SQL Server, cái này bắt buộc bạn phải có Full quyền Administrator trên Server Database

Các bước như sau:

B1: Trong SQL Server bạn kéo xuống dưới có phần SQL Server Agent bạn phải Start nó lên nếu nó đang Stop

Bạn click chuột phải vào Jobs chọn New Job

B2: Tại đây bạn được yêu cầu nhập Name: Tên của Job ở đây tôi chọn là CreateTableLogging và owner là sa

B3: Sau khi ấn OK bạn chọn tiếp phần Steps và bạn chọn New Step, tại đây bạn được yêu cầu nhập Step Name tôi chọn là StepCreateTable và Type là Transact-SQL script (T-SQL) có nghĩa là chạy câu lệnh SQL.

Bạn chọn tiếp Database là tên Database bạn muốn thực thi
Phần Command bạn gõ câu lệnh sau để tạo bảng động

DECLARE @strCreateTable AS VARCHAR(1000)
SET @strCreateTable = 'CREATE TABLE tbl_Month_'
SET @strCreateTable += Cast(MONTH(GETDATE()) as varchar)
SET @strCreateTable += '_'
SET @strCreateTable += Cast(YEAR(GETDATE()) as varchar)
SET @strCreateTable +='('
SET @strCreateTable +='ID int PRIMARY KEY IDENTITY,'
SET @strCreateTable +='Name nvarchar(50)'
SET @strCreateTable +=')'
--PRINT(@strCreateTable)
EXEC(@strCreateTable)

Sau đó nhấn OK

B4: Tiếp đến bạn chọn phần Schedules (để đặt lịch chạy cho Job) ban chọn New Schedule ở đây bạn được yêu cầu nhập Schedule Name cho Job tôi chọn là ScheduleCreateTable, Phần tần suất (Frequency) tôi chọn Occurs chạy vào ngày 15 hàng tháng (Monthly) lúc 12h đêm.

B5: sau đó bạn ấn OK để hoàn tất việc đặt lịch chạy cho Job và OK để hoàn tất Job
và đây là kết quả

B6: Để chắc là Job của bạn có chạy đúng hay không thì bạn Click chuột phải vào Job vừa tạo chọn Start Job at Step...

B7: Nếu nó báo thế này thì có nghĩa là Job của bạn đã chạy chính xác

B8: Kiểm tra

Như vậy mới hoàn thành xong phần tạo bảng bước tiếp chúng ta phải insert vào bảng đó thế nào

DECLARE @strValue AS NVARCHAR(50)
DECLARE @strSQL AS VARCHAR(1000)
SET @strValue = 'Justin___'
SET @strValue += Cast(GETDATE() as varchar)

SET @strSQL = 'INSERT INTO tbl_Month_'
SET @strSQL += Cast(MONTH(GETDATE()) as varchar)
SET @strSQL += '_'
SET @strSQL += Cast(YEAR(GETDATE()) as varchar)
SET @strSQL += '([Name]) VALUES('''
SET @strSQL += @strValue
SET @strSQL += ''')'
--PRINT(@strSQL)
EXEC(@strSQL)

Trong bảng tbl_Month_xx_xxxx  này tôi có 2 trường là ID (tự tăng) và Name
sau đó tôi insert test các giá trị mà gán trong biến @strValue là "Justin___" + ngày hiện tại
sau khi insert xong dữ liệu của tôi sẽ là thế này

ID   Name
6    Justin___Dec 12 2009  9:25AM
7    Justin___Dec 12 2009  9:25AM
8    Justin___Dec 12 2009  9:25AM
9    Justin___Dec 12 2009  9:25AM

Kỹ thuật phân trang trong trang asp.net dùng Store

Trên website này có viết bài "Kỹ thuật phân trang bằng Store Procedure" nhưng nhiều bạn chưa áp dụng được nó để phân trang trong asp.net và có nhiều bạn hỏi cách làm. Bài này mình sẽ hướng dẫn các bạn sử dụng thuật toán của bài viết đó vào trang asp.net.

Trong bài viết "Kỹ thuật phân trang bằng Store Procedure" thì dữ liệu hiển thị sẽ được chia làm 2 phần, một phần là dữ liệu truy vấn, một phần là chuỗi html hiển thị phân trang. Vậy để sử dụng nó bạn cần đổ dữ liệu từ Store vào DataSet. Khi đó phần dữ liệu truy vấn sẽ là Table[0] và phần hiển thị phân trang là Table[1]. Vậy bạn cần viết hàm để thực hiện nó. Để thực hiện mình lấy database Northwind, truy vấn dữ liệu từ bảng Customrs. Với Stored Procedure spPhanTrangSQL bạn xem ở bài viết  "Kỹ thuật phân trang bằng Store Procedure"  còn Store truy vẫn dữ liệu mình viết như sau:

CREATE PROCEDURE [dbo].[spCustomers_PhanTrang]
@currPage int,
@recodperpage int,
@Pagesize int
AS
Begin
    Begin
    WITH s AS
    (
        SELECT ROW_NUMBER() 
            OVER(ORDER BY CustomerID, 
            CompanyName) AS RowNum, 
            CustomerID, 
            CompanyName, 
            ContactName, City 
        FROM Customers  
    )
    Select * From s 
    Where RowNum Between 
        (@currPage - 1)*@recodperpage+1 
            AND @currPage*@recodperpage
    END
    -- Tính tổng số bản ghi
    DECLARE @Tolal int
    SELECT @Tolal=Count(*) FROM Customers

    EXEC spPhanTrangSQL 
            @Tolal, 
            @currPage, 
            @Pagesize, 
            @recodperpage
END

Trong trang aspx bạn cần một Gridview và một Literal để hiển thị dữ liệu như sau:

<table cellpadding="0" cellspacing="0" width="620">
    <tr>
        <td style="width: 100%">
        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" BackColor="White"
        BorderColor="#CC9966" BorderStyle="None" BorderWidth="1px" CellPadding="4" Width="620px">
        <RowStyle BackColor="White" ForeColor="#330099" />
        <Columns>
            <asp:BoundField DataField="RowNum" HeaderText="RowNum" SortExpression="RowNum" />
            <asp:BoundField DataField="CustomerID" HeaderText="CustomerID" SortExpression="CustomerID" />
            <asp:BoundField DataField="CompanyName" HeaderText="CompanyName" SortExpression="CompanyName" />
            <asp:BoundField DataField="ContactName" HeaderText="ContactName" SortExpression="ContactName" />
            <asp:BoundField DataField="City" HeaderText="City" SortExpression="City" />
        </Columns>
        <FooterStyle BackColor="#FFFFCC" ForeColor="#330099" />
        <PagerStyle BackColor="#FFFFCC" ForeColor="#330099" HorizontalAlign="Center" />
        <SelectedRowStyle BackColor="#FFCC66" Font-Bold="True" ForeColor="#663399" />
        <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="#FFFFCC" />
    </asp:GridView>
        </td>
    </tr>
    <tr>
        <td style="width: 100%">
            <asp:Literal ID="ltlPhanTrang" runat="server"/></td>
    </tr>
</table>

Khi chạy dữ liệu được hiển thị như hình minh họa sau:

 

Giờ chúng ta sẽ viết  Các hàm cần thực hiện hiển thị dữ liệu:
1. Hàm thực thi một Store đổ dữ liệu vào DataSet

private static DataSet ThucThiStore_DataSet(
        string StoredProcedure, 
        params SqlParameter[] Parameters)
    {
    //Khai báo cuỗi kết nối
        string ConnectionString = 
        @"Server =.\SQL2005;Initial Catalog=Northwind;User ID=sa;Password=sa"; 
        SqlConnection Conn = new SqlConnection(ConnectionString);
        SqlCommand Command = new SqlCommand(StoredProcedure, Conn);
        if (Parameters != null)
        {
            Command.Parameters.Clear();
            Command.Parameters.AddRange(Parameters);
        }
        DataSet ds = new DataSet(); SqlDataAdapter da = 
            new SqlDataAdapter(StoredProcedure, Conn);
        Command.CommandType = CommandType.StoredProcedure;
        da.SelectCommand = Command;
        try
        {
    //Mở kết nối
            Conn.Open();
            da.Fill(ds);
        }
        finally
        {
    // Đóng kết nối
            if (Conn.State == ConnectionState.Open)
                Conn.Close();
            Conn.Dispose();
        }
        return ds;
    }

2. Hàm lấy dữ liệu từ Store trả về một DataSet

private DataSet StoreToDataSet(
        int currPage, 
        int recodperpage, 
        int Pagesize)
    {
        DataSet dts = new DataSet();
        SqlParameter[] arrParam = {
                new SqlParameter("@currPage", SqlDbType.Int),
                new SqlParameter("@recodperpage", SqlDbType.Int),
                new SqlParameter("@Pagesize", SqlDbType.Int)
                };
        arrParam[0].Value = currPage;
        arrParam[1].Value = recodperpage;
        arrParam[2].Value = Pagesize;
        return ThucThiStore_DataSet("spCustomers_PhanTrang", arrParam);
    }

3. Hàm nạp dữ liệu để hiển thị

private void NapDuLieu(int currPage, int recodperpage, int Pagesize)
   {
       DataSet ds = StoreToDataSet(currPage, recodperpage, Pagesize);
       DataTable dtbData = ds.Tables[0];
       DataTable dtbPhanTrang = ds.Tables[1];
       if (dtbData.Rows.Count > 0)
       {
           GridView1.DataSource = dtbData;
           GridView1.DataBind();
           if (dtbPhanTrang.Rows.Count > 0)
           {
               ltlPhanTrang.Text = dtbPhanTrang.Rows[0]["PhanTrang"] + "";
           }
       }
   }

Trong hàm này bạn thấy dữ liệu được chia làm 2 phần như đã nói ở trên và Phân dữ liệu truy vấn là Table[0] được bind vào Gridview1 còn phần dữ liệu phân trang là Table[1] (Chỉ là 1 bản ghi dữ liệu dang html được gán vào ltlPhanTrang).
 
Trong hàm Page_Load bạn cần khai báo 1 biến page và hiển thị dữ liệu như sau:

protected void Page_Load(object sender, EventArgs e)
    {
        int page = int.Parse("0" + Request.QueryString["page"]);
    if (page == 0) page = 1;
        if (!IsPostBack)
        {
            NapDuLieu(page, 10, 5);
        }
    }

Xem hàm Page_Load bạn thấy mình truyền giá trị 10 là số bản ghi hiển thị cho mỗi trang (rowperpage) và giá trị 5 là số trang hiển thị mỗi phân đoạn (@PageSize)
 
Bạn có thể download mã nguồn trang asp mình viết về tham khảo tại đây.

PhanTrangaspx.rar (1,92 kb)

Tham khảo thêm bài viết về phân trang trong Gridview

Chúc bạn thành công

Ví dụ về phân trang trong gridview

Trong bài này mình sẽ hướng dẫn các bạn viết phân trang đơn giản trong gridview. Với cách chọn trang hiển thị từ DropDownList. Trong ví dụ này hiển thị 10 bản ghi mỗi trang. Khi chọn trang từ DropDownList thì dữ liệu sẽ hiển thị theo trang được chọn. Bạn có thể xem demo tại đây

Dữ liệu minh họa mình lấy từ Access. Để làm phân trang như môt tả sau:

Để làm được điều đó bạn cần tạo gridview và tạo PageTemplate trong gridview.
Code trang asp.net như sau:

<%@ Page Language="C#" AutoEventWireup="true" 
CodeFile="Default.aspx.cs" Inherits="Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>hmweb.com.vn</title>
    <%--create by <a href="mailto:hungbv@hmweb.com.vn--%%3E%3C/head%3E%3Cbody%3E">hungbv@hmweb.com.vn--%>
</head>
<body>
</a>    <form id="form1" runat="server">
    <div style="text-align: center">
        <asp:Panel ID="Panel1" runat="server" 
        GroupingText="Ví dụ về phân trang Gridview"
            Width="610px">
        <asp:GridView id="CustomersGridView" 
        DataSourceID="AccessDataSource1"   
        autogeneratecolumns="False"
        allowpaging="True"
        ondatabound="CustomersGridView_DataBound"  
        runat="server" DataKeyNames="CustomerID" 
        BackColor="White" BorderColor="#CC9966" 
        BorderStyle="None" BorderWidth="1px" 
        CellPadding="4" Width="100%">
        <PagerStyle forecolor="#330099"
          backcolor="#FFFFCC" HorizontalAlign="Center"/>
        <PagerTemplate>
          <table width="100%">                    
            <tr>                        
              <td style="width:70%">

                <asp:Label id="MessageLabel"
                  forecolor="Blue"
                  text="Chọn trang:" 
                  runat="server"/>
                <asp:DropDownList id="PageDropDownList"
                  autopostback="true"
                  onselectedindexchanged = "PageDropDownList_SelectedIndexChanged" 
                  runat="server"/>
              </td>   
              <td style="width:70%; text-align:right">
                <asp:Label id="CurrentPageLabel"
                  forecolor="Blue"
                  runat="server"/>
              </td>
            </tr>                    
          </table>
        </PagerTemplate> 
          <Columns>
              <asp:BoundField DataField="CustomerID" 
              HeaderText="CustomerID" ReadOnly="True" 
              SortExpression="CustomerID" />
              <asp:BoundField DataField="CompanyName" 
              HeaderText="CompanyName" SortExpression="CompanyName" />
              <asp:BoundField DataField="Address" 
              HeaderText="Address" SortExpression="Address" />
              <asp:BoundField DataField="City" 
              HeaderText="City" SortExpression="City" />
          </Columns>
            <RowStyle BackColor="White" ForeColor="#330099" />
            <FooterStyle BackColor="#FFFFCC" ForeColor="#330099" />
            <SelectedRowStyle BackColor="#FFCC66" Font-Bold="True" ForeColor="#663399" />
            <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="#FFFFCC" />
      </asp:GridView>
        </asp:Panel>
        <asp:AccessDataSource ID="AccessDataSource1" 
        runat="server" DataFile="~/SP.mdb" 
        SelectCommand="SELECT [CustomerID], [CompanyName], [ContactName], [Address], [City] FROM [Customers]">
        </asp:AccessDataSource>
    </div>
    </form>
</body>
</html>

Trong code behind bạn làm như sau:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    protected void PageDropDownList_SelectedIndexChanged(Object sender, EventArgs e)
    {
        GridViewRow pagerRow = CustomersGridView.BottomPagerRow;
        DropDownList pageList = (DropDownList)pagerRow.Cells[0].FindControl("PageDropDownList");
        // Thiết lập thuộc tính PageIndex Để hiển thị trang đuợc chọn.
        CustomersGridView.PageIndex = pageList.SelectedIndex;
    }

    protected void CustomersGridView_DataBound(Object sender, EventArgs e)
    {
        GridViewRow pagerRow = CustomersGridView.BottomPagerRow;
        DropDownList pageList = (DropDownList)pagerRow.Cells[0].FindControl("PageDropDownList");
        Label pageLabel = (Label)pagerRow.Cells[0].FindControl("CurrentPageLabel");

        if (pageList != null)
        {
            for (int i = 0; i < CustomersGridView.PageCount; i++)
            {
                int pageNumber = i + 1;
                ListItem item = new ListItem(pageNumber.ToString());
                if (i == CustomersGridView.PageIndex)
                {
                    item.Selected = true;
                }
                pageList.Items.Add(item);
            }
        }

        if (pageLabel != null)
        {

            int currentPage = CustomersGridView.PageIndex + 1;
            // Cập nhật thông tin lable hiển thị trang
            pageLabel.Text = "Page " + currentPage.ToString() +
              " of " + CustomersGridView.PageCount.ToString();
        }
    }
}

Bạn có thể download mã nguồn về tham khảo

 

phantrang.rar (18,48 kb)