Bài 49: (ADO.NET) Giới thiệu ado.net và kết nối SQL Server với SqlConnection

Ngày đăng: 12/29/2022 10:49:47 AM

Giới thiệu về ADO.NET

ADO.NET (ActiveX Data Object) là tập hợp các thư viện lớp qua đó cho phép ứng dụng tương tác (lấy về, cập nhật, xóa) với các nguồn dữ liệu (Như SQLServer, XML, MySQL, Oracle Database ...).

Kiến trúc để truy cập dữ liệu với ADO.NET được phân ra nhiều phần rời rạc, mỗi phần có thể sử dụng độc lập hay đồng thời nhiều thành phần được sử dụng. Cơ bản thì nó phân chia ra hai khu vực như hình dưới:

Kiến trúc ADO.NET

  • Phần thứ nhất gọi là Data Provider: là các thư viện lớp cung cấp chức năng tạo kết nối đến nguồn dữ liệu, thi hành các lệnh trên nguồn dữ liệu đó inset, update, delete, read.


    SQL Server: Loại Data Provider mặc định trong .NET Core là SqlClient ở namespace System.Data.SqlClient cung cấp khả năng kết nối đến SQL Server

    MySQL: Nếu muốn có loại Data Provider truy cập đến MySQL thì cài đặt package MySql.Data, sẽ có Data Provider MySql.Data.MySqlClient

    SQLite thì cài đặt Data Provider Microsoft.Data.SQLite

  • Phần thứ 2 gọi là DataSet là các thư viện lớp (độc lập với Data Provider) tạo ra các đối tượng để quản lý dữ liệu không phụ thuộc ngồn dữ liệu đến từ đâu, đã ở trong ứng dụng (local) hay từ nguồn XML.

    DataSet thường gồm nhiều DataTable, trong DataTable lại gồm DataColumn, các dàng buộc, các khóa chính ... Vậy DataSet là sự trừu tượng hóa một CSDL thực.

Trước tiên tìm hiểu sử dụng Data Provider với trường hợp cụ thể là SqlClient để truy cập đến CSDL MS SQL Server. Hãy gõ lệnh sau để thêm package SqlClient vào dự án:

 dotnet add package System.Data.SqlClient

Sau đó trong code sử dụng thêm namespace:

 using System.Data;
 using System.Data.SqlClient;
 

Chuẩn bị SQL Server với dữ liệu mẫu

Do thực hành sử dụng SqlClient kết nối SQL Server, nên nếu chưa có hệ quản trị CSDL này thì có thể tạo một SQL Server với dữ liệu mẫu - chạy trong một Container Docker, hãy làm theo hướng dẫn phần MSSQL Container, nếu đã có SQL Server - bạn có thể phục hồi dữ liệu mẫu từ file bak xtlab.bak vào CSDL xtlab để thực hành.

Hoặc bạn có thể tải về docker-compose.yml và dữ liệu mẫu tại SQLDocker, sau đó giải nén, từ dòng lệnh vào thư mục MSSQL rồi chạy lệnh:

 docker-compose up -d
 docker exec sqlserver-xtlab /var/opt/mssql/backup/restore.sh
 

Bằng cách như vậy bạn sẽ có một SQL Server với dữ liệu mẫu có tên xtlab có cấu trục như tại Công cụ SQL Online , lắng nghe ở cổng 1433, tài khoản sa với password là Password123

Tạo kết nối với DbConnection

DbConnection là lớp biểu diễn sự kết nối tới máy chủ CSDL. Từ lớp này các thư viện triển khai cụ thể cho từng loại CSDL như SQL Server có lớp triển khai (kế thừa) DbConnection là SqlConnection.

SqlConnection sử dụng với các bước cơ bản sau:

  • Tạo đối tượng SqlConnection từ một chuỗi kết nối tới SQL Server
  • Mở kết nối với phương thức Open()
  • Thực hiện các truy vấn bằng cách sử dụng các lớp như SqlCommandSqlDataAdapterSqlDataReader ...
  • Không còn sử dụng đến kết nối nữa thì cần đóng lại bằng phương thức Close

Ví dụ, có SQL Server ở địa chỉ localhost (hoặc 127.0.0.1), cổng kết nổi 1433, tên tài khoản là SA, password là Password123, thì tạo và mở kết nối như sau:

 string sqlconnectStr = "Data Source=localhost,1433;Initial Catalog=xtlab;User ID=SA;Password=Password123";
 DbConnection connection = new SqlConnection(sqlconnectStr);
 
 connection.Open();                      // Mở kết nối - hoặc  connection.OpenAsync(); nếu dùng async
 
 //..                                    // thực hiện cá tác  vụ truy vấn CSDL (CRUD - Insert, Select, Update, Delete)
 
 connection.Close();                     // Đóng kết nối
 
 

Tạo chuỗi kết nối với SQL Server

Chuỗi kết nối (connection string) là tham số để khởi tạo ra đối tượng SqlConnection, chuỗi này chứa các thông tin cơ bản để thực hiện kết nối đến một SQL Server, các thông tin được chứa theo cặp key=value; ví dụ chuỗi "key 1=value1;key 2=value2", dưới dây tham khảo một số key

KEY

Mô tả

Connect Timeout

Số giây cố gắng kết nối trước khi phát sinh lỗi (mặc định 15s)

Timeout
Connection Timeout
Data Source

Key này dùng để gán địa chỉ mạng (tên máy hoặc IP hoặc domain) của SQL Server, hoặc là tên của hiện hành đang chạy của SQL Server. Nếu muốn gán cả cổng thì sẽ thêm vào phía sau địa chỉ này ,port

Server
Address
Addr
Initial Catalog

Tên của Database

Database
Password

Password để kết nối

PWD
User ID

Tài khoản (account) dùng để đăng nhập

UID

Tạo chuỗi kết nối cơ bản

Chuỗi có dạng như sau:

 "Server=ServerAddress;Database=DataBaseName;User Id=Username;Password=myPassword;"

Hoặc

 "Data Source=ServerAddress;Database=DataBaseName;User Id=Username;Password=myPassword;"

Ví dụ

SQL Server ở máy có địa chỉ IP 192.168.1.10, có CSDL tên exampledb, cho phép kết nối với User/Password là testuser/testpass, thì tạo ra chuỗi kết nối và đối tượng SqlConnection như sau:

 String connectionString = "Server=192.168.1.10;Database=exampledb;User Id=testuser;Password=testpass;";
 var sqlConnection = new SqlConnection(connectionString);
 sqlConnection.Open();   //Mở kết nối
 //...Code truy vấn, cập nhật dữ dữ liệu ở đây
 sqlConnection.Close();  //Đóng kết nối sau khi sử dụng
 

Nếu sử dụng using thì kết nối sẽ tự động đóng lại ở cuối khối using, nên không cần phải gọi Close

 String connectionString = "Server=192.168.1.10;Database=exampledb;User Id=testuser;Password=testpass;";
 using (var sqlConnection = new SqlConnection(connectionString))
 {
     sqlConnection.Open();//Mở kết nối
     //...Code truy vấn, cập nhật dữ dữ liệu ở đây
 }
 

Chú ý, trong một phạm vi phương thức có thể sử dụng using mà không cần tạo khối lệnh

 void myfunction()
 {
     using var sqlConnection = new SqlConnection(connectionString);
     sqlConnection.Open();//Mở kết nối
     //...
 }
 

SqlConnectionStringBuilder

SqlConnectionStringBuilder giúp tạo ra chuỗi kết nối, bằng cách thiết lập từng loại key ở trên, sau đó nó phát sinh chuỗi kết nối giúp bạn. Ví dụ:

 var DbCStringBuilder = new SqlConnectionStringBuilder();
 DbCStringBuilder["Server"] = "127.0.0.1,1433";
 DbCStringBuilder["Database"] = "xtlab";
 DbCStringBuilder["User Id"] = "SA";
 DbCStringBuilder["Password"] = "Password123";
 
 string sqlConnectStr = DbCStringBuilder.ToString();
 using var connection = new SqlConnection(sqlConnectStr);
 
 connection.Open();
 // Thực hiện  các tác vụ ...
 
 

Thông tin kết nối SqlConnection

Ở phần trên đã biết tạo ra một chuỗi kết nối, từ đó sinh ra đối tượng SqlConnection, đối tượng này được sử dụng bởi các lớp khác nhau như SqlCommand, SqlDataAdapter ... để thực hiện các truy vấn đến dữ liệu. Những đối tượng này sẽ tìm hiều ở phần sau, ở đây nói thêm một chút về SqlConnection

Một số phương thức, thuộc tính SqlConnection

State

Kiểu ConnectionState trạng thái kết nối:

  • ConnectionState.Closed kết nối đã đóng
  • ConnectionState.Connecting đang kết nối
  • ConnectionState.Executing đang thi hành lệnh nào đó
  • ConnectionState.Fetching đang nhận dữ liệu về
  • ConnectionState.Open kết nối đang mở

Để kiểm tra cần thực hiện phép toán bitwise bằng phương thức FlagsAttribute, ví dụ:

 if ((connection.State.HasFlag(ConnectionState.Open))
     && (connection.State.HasFlag(ConnectionState.Fetching)))
     {
         Console.WriteLine("Kết nối mở và đang nhận dữ liệu");
     }
Database

Trả về tên Database - sau khi kết nối mở

StatisticsEnabled

Mặc định là false, nếu thiết lập bằng true thì nó cho phép thu thập thông tin về kết nối. Để lấy thông tin thù thập được dùng phương thức RetrieveStatistics()

Open()

Mở kết nối, sử dụng OpenAsync() nếu dùng kỹ thuật async

Close()

Đóng kết nối

CreateCommand()

Tạo đối tượng SqlCommand để thực hiện các lệnh SQL

RetrieveStatistics()

Lấy thông tin thống kê (trả về IDictionary)

StateChange

Event - phát sinh khi thay đổi trạng thái kết nối, muốn bắt sự kiện gán nó bằng delegate dạng

(object sender, StateChangeEventArgs e) => { /.. }

Ví dụ: Ví dụ sau có tạo ra một chuỗi kết nối tới SQL Server ở địa chỉ 127.0.0.1, sau đó bắt thông tin cho biết mỗi khi trạng thái kết nối thay đổi (open, close), có thực hiện một câu truy vấn SQL lấy dữ liệu về, cuối cùng là cho biết các thông tin thông kế đã thực hiện trên kết nối

 public class Exam1
 {
   public static void Test()
   {
 
     // TẠO CHUỖI KẾT NỐI bằng SqlConnectionStringBuilder
     var stringBuilder = new SqlConnectionStringBuilder();
     stringBuilder["Server"] = "127.0.0.1,1433";
     stringBuilder["Database"] = "xtlab";
     stringBuilder["User Id"] = "SA";
     stringBuilder["Password"] = "Password123";
     String sqlConnectionString = stringBuilder.ToString();
 
     var connection = new SqlConnection(sqlConnectionString);
     // kích hoạt chế độ thu thập thông tin thống kê khi truy vấn
     connection.StatisticsEnabled = true;
 
     Console.WriteLine($"{"ConnectionString ",17} : {stringBuilder}");
     Console.WriteLine($"{"DataSource ",17} : {connection.DataSource}");
 
     // Bắt sự kiện trạng thái kết nối thay đổi
     connection.StateChange += (object sender, StateChangeEventArgs e) =>
     {
       Console.WriteLine($"Kết nối thay đổi: {e.CurrentState}, trạng thái trước: " + $"{e.OriginalState}");
     };
 
     // mở kết nối
     connection.Open();
 
     // Dùng SqlCommand thi hành SQL  - sẽ  tìm hiểu sau
     using (DbCommand command = connection.CreateCommand())
     {
       // Câu truy vấn SQL
       command.CommandText = "select top(5) * from Sanpham";
       var reader = command.ExecuteReader();
       // Đọc kết quả truy vấn
       Console.WriteLine("
 CÁC SẢN PHẨM:");
       Console.WriteLine($"{"SanphamID ",10} {"TenSanpham "}");
       while (reader.Read())
       {
         Console.WriteLine($"{reader["SanphamID"],10} {reader["TenSanpham"]}");
       }
     }
 
     // Lấy thống kê và in số liệu thống kê
     Console.WriteLine("Thông tin thống kê các tương tác đã thực hiện trên kết nôis");
     var dicStatics = connection.RetrieveStatistics();
     foreach (var key in dicStatics.Keys)
     {
       Console.WriteLine($"{key,17} : {dicStatics[key]}");
     }
 
     // Không dùng đến kết nối thì phải đóng lại (giải phóng)
     connection.Close();
   }
 }
 

Khi chạy trong Main

 static void Main(string[] args)
 {
     Exam1.Test();
 }
 

Đọc thông tin kết nối từ file config

Bạn có thể lưu chuỗi kết nối ở một cấu hình sau đó khi chạy chương trình nó sẽ đọc vào tạo thông tin kết nối, có thể sử dụng kỹ thuật Configuration lưu thông tin kết nối ở các định dạng file như json, ini, xml ... giả sử dùng định dạng json, hãy thêm các package như hướng dẫn tại config với json

 dotnet add package Microsoft.Extensions.Configuration
 dotnet add package Microsoft.Extensions.Options.ConfigurationExtensions
 dotnet add package Microsoft.Extensions.Configuration.Json

Giả sử tạo file config có tên là appconfig.json có lưu chuỗi kết nối như sau:

 {
     "csdl" :  {
         "ketnoi1"  : "Data Source=127.0.0.1,1433;Initial Catalog=xtlab;User ID=SA;Password=Password123",
         "ketnoi2"  : "Data Source=localhost,1433;Initial Catalog=xtlab;User ID=SA;Password=Password123"
     }
 }
 

Ví dụ:

 using System;
 using System.Data;
 using System.Data.SqlClient;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Configuration.Json;
 using System.IO;
 
 namespace ADO_01_SqlConnection
 {
     class Program
     {
         // Lấy chuỗi kết nối từ file config định dạng json,
         // Điểm lưu: csl:ketnoi2
         public static string GetConnectString() {
             var configBuilder = new ConfigurationBuilder()
                        .SetBasePath(Directory.GetCurrentDirectory())      // file config ở thư mục hiện tại
                        .AddJsonFile("appconfig.json");                    // nạp config định dạng JSON
             var configurationroot = configBuilder.Build();                // Tạo configurationroot
             return configurationroot["csdl:ketnoi2"];
 
         }
 
         static void Main(string[] args)
         {
             // Exam1.Test();
 
             String sqlConnectString = GetConnectString();
             var connection = new SqlConnection(GetConnectString());
             connection.StatisticsEnabled = true;
             connection.FireInfoMessageEventOnUserErrors = true;
 
             connection.StateChange += (object  sender, StateChangeEventArgs e) => {
                     Console.WriteLine($"Trạng thái hiện tại: {e.CurrentState}, trạng thái trước: " + $"{e.OriginalState}");
             };
 
             // Mở kết nối
             connection.Open();
 
             // Thực hiện các truy vấn tại đây ...
 
             connection.Close();
 
         }
     }
 }
 

Sử dụng ADO.NET với MySQL Server

Để nhanh chóng, sử dụng docker tạo ra một dịch vụ MySQL Server, tạo ra file docker-compose.yaml có nội dung như sau:

 version: "3"
 
 services:
     netcore-mysql:
       image: "mysql:5.7"
       container_name: mysql-net
       restart: always
       hostname: mysqlnet
       networks:
         - my-network
       environment:
         MYSQL_ROOT_PASSWORD: abc123   #Thiết lập password cho root
       volumes:
         - storedb:/var/lib/mysql
       ports:
         - "3307:3306"                # Ánh xạ cổng 3307 vào cổng mặc định 3306 SQL
 
 networks:                            # TẠO NETWORK
   my-network:
     driver: bridge
 
 volumes:                              # TẠO Ổ ĐĨA LƯU DB
   storedb:
 

Với file trên, sử dụng MySQL bản 5.7, cổng mặc định của MySQL 3306 khi expose ra máy host là cổng 3307 (localhost:3307 là truy cập vào MySQL), sau đó thực hiện lệnh tạo dịch vụ

 docker-compose up -d

Sau khi dịch vụ chạy, có thể sử dụng bất kỳ công cụ quản lý MySQL nào để quản trị nó, ví dụ hãy cài MySQL Workbench, rồi tạo kết nối đến MySQL (chú ý cổng 3307)

Nếu muốn nhanh chóng có CSDL ví dụ giống trên, hãy tạo một db tên xtlab và thực thi trong cửa sổ truy vấn các câu SQL script_create_db_mysql

Tích hợp thư viện MySql.Data

Thư viện MySql.Data trên Nuget cho phép ứng dụng .NET sử dụng ADO.NET để truy vấn MySQL, hãy thực hiện lệnh

 dotnet add package MySql.Data

Thư viện này triển khai các Interface, Abstract ... của ADO.NET, ví dụ:

  • DbConnectionStringBuilder triển khai thành MySqlConnectionStringBuilder
  • DbConnection triển khai thành MySqlConnection
  • DbCommand triển khai thành MySqlCommand
  • ...

Đoạn code kết nối, truy vấn đến SQL Server có thể sửa lại để truy vấn MySQL như sau:

   class Program
   {
     public class Exam1
     {
       public static void Test()
       {
 
         // TẠO CHUỖI KẾT NỐI bằng SqlConnectionStringBuilder
         var stringBuilder = new MySqlConnectionStringBuilder();
         stringBuilder["Server"] = "127.0.0.1";
         stringBuilder["Database"] = "xtlab";
         stringBuilder["User Id"] = "root";
         stringBuilder["Password"] = "abc123";
         stringBuilder["Port"] = "3307";
 
 
         String sqlConnectionString = stringBuilder.ToString();
 
         var connection = new MySqlConnection(sqlConnectionString);
 
         Console.WriteLine($"{"ConnectionString ",17} : {stringBuilder}");
         Console.WriteLine($"{"DataSource ",17} : {connection.DataSource}");
 
         // Bắt sự kiện trạng thái kết nối thay đổi
         connection.StateChange += (object sender, StateChangeEventArgs e) =>
         {
           Console.WriteLine($"Kết nối thay đổi: {e.CurrentState}, trạng thái trước: " + $"{e.OriginalState}");
         };
 
         // mở kết nối
         connection.Open();
 
         // Dùng SqlCommand thi hành SQL  - sẽ  tìm hiểu sau
         using (DbCommand command = connection.CreateCommand())
         {
           // Câu truy vấn SQL
           command.CommandText = "select * from Sanpham Limit 5";
           var reader = command.ExecuteReader();
           // Đọc kết quả truy vấn
           Console.WriteLine("
 CÁC SẢN PHẨM:");
           Console.WriteLine($"{"SanphamID ",10} {"TenSanpham "}");
           while (reader.Read())
           {
             Console.WriteLine($"{reader["SanphamID"],10} {reader["TenSanpham"]}");
           }
         }
         // Không dùng đến kết nối thì phải đóng lại (giải phóng)
         connection.Close();
 
       }
     }
 
     static void Main(string[] args)
     {
         Exam1.Test();
     }
   }
 
 // Kết quả chạy
 /*
 ConnectionString  : server=127.0.0.1;database=xtlab;user id=root;password=abc123;port=3307
       DataSource  : 127.0.0.1
 Kết nối thay đổi: Connecting, trạng thái trước: Closed
 Kết nối thay đổi: Open, trạng thái trước: Open
 
 CÁC SẢN PHẨM:
 SanphamID  TenSanpham
          1 Bia 333
          2 Nước ngọt Coca cola
          3 Tương Ớt Chin-Su (250g)
          4 Dầu Đậu Nành Simply
          5 Bột cần tây sấy lạnh
 Kết nối thay đổi: Closed, trạng thái trước: Open
 */
 

Chú ý sử dụng namespace MySql.Data.MySqlClient

Source Code

Mã nguồn ADO_01_SqlConnection hoặc tải về ex039

Nguồn tin: XuanThuLab