ADO.NET新手上路教程

前言

利用ADO.NET連接數據庫進行相關操作可以說是每個.NET新手必須學習的一項內容。從學習的角度來看,我個人其實反對新手一開始就學Entity Framework之類的ORM框架,因爲Entity Framework本質上還是基於ADO.NET的二次封裝,所以紮實掌握SQLADO.NET才能讓新手更好的學習之後的ORM框架。下面就來介紹一下ADO.NET中常用的幾個類。

數據準備

爲了方便,我這裏選用VS2015自帶的LocalDB作爲數據源,在其中新建了一個數據庫DBSchool,然後新建一張數據表[TStudent],其字段結構如下圖所示:
在這裏插入圖片描述
其中Id字段爲自增主鍵,其餘字段用於描述學生信息,然後在數據表中添加三條數據,我們的實驗數據就準備好了,如下圖所示:
在這裏插入圖片描述

1、數據庫的連接

想要對數據庫進行操作,首先肯定得連接數據庫,連接數據庫就需要相應的數據庫連接字符串,本文的連接字符串如下所示:

@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DBSchool;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

ADO.NET中,SqlConnection類主要負責連接和關閉數據庫,下面代碼演示了數據庫的連接和關閉:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static string ConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DBSchool;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

        static void Main(string[] args)
        {
            SqlConnection connection = new SqlConnection(ConnectionString);
            connection.Open();
            if (connection.State == ConnectionState.Open)
            {
                Console.WriteLine("數據庫連接已開啓");
                Console.WriteLine("數據源:{0}", connection.DataSource);
                Console.WriteLine("數據庫名:{0}", connection.Database);
                Console.WriteLine("連接時間:{0}", connection.ConnectionTimeout);
            }

            // 關閉連接
            connection.Close();
            connection.Dispose();
            if (connection.State == ConnectionState.Closed)
            {
                Console.WriteLine("數據庫連接已關閉");
            }
            Console.ReadKey();
        }
    }
}

運行結果如下所示:

數據庫連接已開啓
數據源:(localdb)\MSSQLLocalDB
數據庫名:DBSchool
連接時間:30
數據庫連接已關閉

一般connection.Open()用於開啓數據庫連接,connection.Close()、connection.Dispose()用於關閉數據庫連接,在這兩者之間的區域就是你對數據庫操作的具體代碼,但一般我們不推薦上面這種寫法,取而代之的是利用using關鍵字將一段數據庫操作代碼包裝起來,代碼如下所示:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static string ConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DBSchool;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

        static void Main(string[] args)
        {
            using (SqlConnection connection = new SqlConnection(ConnectionString))
            {
                connection.Open();
                Console.WriteLine("數據庫連接已開啓");
                Console.WriteLine("數據源:{0}", connection.DataSource);
                Console.WriteLine("數據庫名:{0}", connection.Database);
                Console.WriteLine("連接時間:{0}", connection.ConnectionTimeout);
            }
            Console.ReadKey();
        }
    }
}

運行結果如下所示:

數據庫連接已開啓
數據源:(localdb)\MSSQLLocalDB
數據庫名:DBSchool
連接時間:30

2、數據庫的增刪改

數據庫的增刪改操作主要利用SqlCommand實現,其中的幾個重要屬性和方法如下表所示:

名稱 作用
CommandType 指定執行SQL語句還是執行存儲過程
CommandText 設置SQL語句或存儲過程名稱
Parameters SQL命令參數,避免SQL注入
ExecuteNonQuery 執行增刪改等命令,返回受影響的行數

2.1、添加數據:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static string ConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DBSchool;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

        static void Main(string[] args)
        {
            using (SqlConnection connection = new SqlConnection(ConnectionString))
            {
                connection.Open();

                // SQL參數
                SqlParameter[] parameters =
                {
                    new SqlParameter("@Sid", SqlDbType.NVarChar, 20),
                    new SqlParameter("@Sname", SqlDbType.NVarChar, 10),
                    new SqlParameter("@Sgender", SqlDbType.NVarChar, 2),
                    new SqlParameter("@Sage", SqlDbType.Int),
                    new SqlParameter("@Sphone", SqlDbType.NVarChar, 15),
                };

                // 參數賦值
                parameters[0].Value = "1004";
                parameters[1].Value = "吳六";
                parameters[2].Value = "男";
                parameters[3].Value = 20;
                parameters[4].Value = "15874513687";

                // 設置SQL語句
                SqlCommand command = new SqlCommand();
                command.Connection = connection;
                command.CommandType = CommandType.Text;
                command.CommandText = "insert into [TStudent] values(@Sid,@Sname,@Sgender,@Sage,@Sphone)";
                command.Parameters.AddRange(parameters);

                // 執行SQL
                try
                {
                    command.ExecuteNonQuery();
                    command.Parameters.Clear();
                    Console.WriteLine("添加數據成功");
                }
                catch
                {
                    Console.WriteLine("添加數據失敗");
                }
            }
            Console.ReadKey();
        }
    }
}

在這裏插入圖片描述
2.2、修改記錄

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static string ConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DBSchool;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

        static void Main(string[] args)
        {
            using (SqlConnection connection = new SqlConnection(ConnectionString))
            {
                connection.Open();

                // SQL參數
                SqlParameter[] parameters =
                {
                    new SqlParameter("@Sid", SqlDbType.NVarChar, 20),
                    new SqlParameter("@Sname", SqlDbType.NVarChar, 10)
                };

                // 參數賦值
                parameters[0].Value = "1004";
                parameters[1].Value = "吳老六";

                // 設置SQL語句
                SqlCommand command = new SqlCommand();
                command.Connection = connection;
                command.CommandType = CommandType.Text;
                command.CommandText = "update [TStudent] set Sname=@Sname where Sid=@Sid";
                command.Parameters.AddRange(parameters);

                // 執行SQL
                try
                {
                    command.ExecuteNonQuery();
                    command.Parameters.Clear();
                    Console.WriteLine("修改數據成功");
                }
                catch
                {
                    Console.WriteLine("修改數據失敗");
                }
            }
            Console.ReadKey();
        }
    }
}

在這裏插入圖片描述
2.3、刪除記錄

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static string ConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DBSchool;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

        static void Main(string[] args)
        {
            using (SqlConnection connection = new SqlConnection(ConnectionString))
            {
                connection.Open();

                // SQL參數
                SqlParameter[] parameters =
                {
                    new SqlParameter("@Sid", SqlDbType.NVarChar, 20)
                };

                // 參數賦值
                parameters[0].Value = "1004";

                // 設置SQL語句
                SqlCommand command = new SqlCommand();
                command.Connection = connection;
                command.CommandType = CommandType.Text;
                command.CommandText = "delete from [TStudent] where Sid=@Sid";
                command.Parameters.AddRange(parameters);

                // 執行SQL
                try
                {
                    command.ExecuteNonQuery();
                    command.Parameters.Clear();
                    Console.WriteLine("刪除數據成功");
                }
                catch
                {
                    Console.WriteLine("刪除數據失敗");
                }
            }
            Console.ReadKey();
        }
    }
}

在這裏插入圖片描述
在以上的增刪改代碼中,一般推薦使用SqlParameter封裝相關參數,一是方便SQL的書寫和閱讀,二是能夠有效避免SQL注入問題

3、數據庫的查詢

數據庫的查詢用得最爲廣泛,關於查詢操作,ADO.NET中一般有三種寫法,下面逐一介紹。
3.1、查詢方法一

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static string ConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DBSchool;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

        static void Main(string[] args)
        {
            DataTable dataTable = new DataTable();
            using (SqlConnection connection = new SqlConnection(ConnectionString))
            {
                connection.Open();

                // 設置SQL語句
                SqlCommand command = new SqlCommand();
                command.Connection = connection;
                command.CommandType = CommandType.Text;
                command.CommandText = "select * from [TStudent]";

                // 獲取查詢結果,填充到DataTable
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = command;
                adapter.Fill(dataTable);
            }

            foreach (DataRow row in dataTable.Rows)
            {
                Console.Write(row["Id"].ToString() + "\t");
                Console.Write(row["Sid"].ToString() + "\t");
                Console.Write(row["Sname"].ToString() + "\t");
                Console.Write(row["Sgender"].ToString() + "\t");
                Console.Write(row["Sage"].ToString() + "\t");
                Console.Write(row["Sphone"].ToString() + "\t");
                Console.WriteLine();
            }
            Console.ReadKey();
        }
    }
}

3.2、查詢方法二

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static string ConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DBSchool;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

        static void Main(string[] args)
        {
            DataTable dataTable = new DataTable();
            using (SqlDataAdapter adapter = new SqlDataAdapter("select * from [TStudent]", ConnectionString))
            {
                adapter.Fill(dataTable);
            }

            foreach (DataRow row in dataTable.Rows)
            {
                Console.Write(row["Id"].ToString() + "\t");
                Console.Write(row["Sid"].ToString() + "\t");
                Console.Write(row["Sname"].ToString() + "\t");
                Console.Write(row["Sgender"].ToString() + "\t");
                Console.Write(row["Sage"].ToString() + "\t");
                Console.Write(row["Sphone"].ToString() + "\t");
                Console.WriteLine();
            }
            Console.ReadKey();
        }
    }
}

3.3、查詢方法三

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static string ConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DBSchool;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

        static void Main(string[] args)
        {
            SqlConnection connection = new SqlConnection(ConnectionString);
            connection.Open();

            // 設置SQL語句
            SqlCommand command = new SqlCommand();
            command.Connection = connection;
            command.CommandType = CommandType.Text;
            command.CommandText = "select * from [TStudent]";

            // 遍歷數據行
            SqlDataReader reader = command.ExecuteReader();
            while (reader.Read())
            {
                Console.Write(reader.GetInt32(reader.GetOrdinal("Id")) + "\t");
                Console.Write(reader.GetString(reader.GetOrdinal("Sid")) + "\t");
                Console.Write(reader.GetString(reader.GetOrdinal("Sname")) + "\t");
                Console.Write(reader.GetString(reader.GetOrdinal("Sgender")) + "\t");
                Console.Write(reader.GetInt32(reader.GetOrdinal("Sage")) + "\t");
                Console.Write(reader.GetString(reader.GetOrdinal("Sphone")) + "\t");
                Console.WriteLine();
            }
            
            // 關閉連接
            connection.Close();
            connection.Dispose();
            Console.ReadKey();
        }
    }
}

運行結果如下所示:

1       1001    張三    男      20      13745258478
2       1002    李四    女      21      18754124736
3       1003    王五    女      22      14963587415

上面三種方法都可以進行數據庫查詢操作,但也需要注意它們之間的區別。方法一和方法二查詢後將結果放入內存中的DataTable,接下來我們只需要操作DataTable即可。方法三則是在數據讀取完畢之前一直保持與數據庫的連接,直到讀取操作完成。一般更推薦方法一和方法二的寫法。

4、SQL參數化操作

同志們一定發現了,在上面的代碼中有一個類的出現頻率很高,那就是SqlParameter。之前也介紹過SqlParameter的作用主要是防止SQL注入問題。那麼到底什麼是SQL注入問題?下面來看一個WinForm實現用戶登錄的demo。首先建立一張用戶表[TUser],加入兩條數據,如下圖所示:
在這裏插入圖片描述
登錄界面代碼

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private static string ConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DBSchool;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

        public Form1()
        {
            InitializeComponent();
        }

        private void btnLogin_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txtUserName.Text) || string.IsNullOrEmpty(txtPassword.Text))
            {
                MessageBox.Show("請輸入用戶名和密碼", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            DataTable dataTable = new DataTable();
            using (SqlConnection connection = new SqlConnection(ConnectionString))
            {
                connection.Open();

                // 設置SQL語句
                SqlCommand command = new SqlCommand();
                command.Connection = connection;
                command.CommandType = CommandType.Text;
                command.CommandText = "select * from [TUser] where UserName='" + txtUserName.Text + "' and Password='" + txtPassword.Text + "'";

                // 填充DataTable
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = command;
                adapter.Fill(dataTable);

                // 判斷登錄是否成功
                if (dataTable.Rows.Count > 0)
                {
                    MessageBox.Show("登陸成功");
                }
                else
                {
                    MessageBox.Show("登陸失敗");
                }
            }
        }
    }
}

WinForm登錄界面如下圖所示,輸入admin123456後點擊按鈕,發現能夠正常登陸,這看起來好像沒什麼問題。
在這裏插入圖片描述
現在我們將密碼改一改,輸入abcd' or '1'='1,如下圖所示:
在這裏插入圖片描述
事實就是數據庫中根本就沒這條記錄,但最後竟然也能登陸!!!這其實就是一個典型的SQL注入問題。問題就出在下面這條語句:

command.CommandText = "select * from [TUser] where UserName='" + txtUserName.Text + "' and Password='" + txtPassword.Text + "'";

很多同志都喜歡這麼寫,其實這是一個很大的安全隱患。這種情況我們要麼使用存儲過程,要麼使用參數化的SQL語句,而SqlParameter就是幹這個活的。我們將登陸代碼改一改,如下所示:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private static string ConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DBSchool;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

        public Form1()
        {
            InitializeComponent();
        }

        private void btnLogin_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txtUserName.Text) || string.IsNullOrEmpty(txtPassword.Text))
            {
                MessageBox.Show("請輸入用戶名和密碼", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            DataTable dataTable = new DataTable();
            using (SqlConnection connection = new SqlConnection(ConnectionString))
            {
                connection.Open();

                // SQL查詢參數
                SqlParameter[] parameters =
                {
                    new SqlParameter("@UserName", SqlDbType.NVarChar, 20),
                    new SqlParameter("@Password", SqlDbType.NVarChar, 20),
                };
                parameters[0].Value = txtUserName.Text;
                parameters[1].Value = txtPassword.Text;

                // 設置SQL語句
                SqlCommand command = new SqlCommand();
                command.Connection = connection;
                command.CommandType = CommandType.Text;
                command.CommandText = "select * from [TUser] where UserName=@UserName and Password=@Password";
                command.Parameters.AddRange(parameters);

                // 填充DataTable
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = command;
                adapter.Fill(dataTable);

                // 判斷登錄是否成功
                command.Parameters.Clear();
                if (dataTable.Rows.Count > 0)
                {
                    MessageBox.Show("登陸成功");
                }
                else
                {
                    MessageBox.Show("登陸失敗");
                }
            }
        }
    }
}

首先輸入admin123456,如下圖所示,發現能夠正常登陸。
在這裏插入圖片描述
然後輸入abcd' or '1'='1,如下圖所示,發現登陸失敗。
在這裏插入圖片描述
使用參數化的SQL語句有兩個好處:一是書寫和閱讀都很清晰,二是能夠有效避免SQL注入問題。以後同志們在傳遞參數做查詢時,一定要記得利用SqlParameter封裝你的查詢參數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章