30、ADO.NET、事務、DataSet

ADO.NETweb

ADO.NET是一組用於和數據源進行交互的面向對象類庫。一般數據源是數據庫,但也能夠是文本文件、Excel表格、XML文件。sql

說白了就是使用.net操做數據庫的一套類庫。數據庫

ADO.NET 命名空間express

System.Data; //描述數據的命名空間
編程

System.Data.SqlClient; //針對SQL Server的命名空間c#

System.Data.OleDB; //access用這個windows

System.Data.Odbc;數組

System.Data.OracleClinet; //Oracle用這個緩存

ADO.NET 四大類庫安全

Connection //用來和數據庫創建鏈接。

Command //執行查詢、修改、刪除等命令。

  ExecuteNonQuery() //執行非查詢語句,返回受影響行數 若是執行非增、刪、改操做返回-1。

  ExecuteScalar() //返回第一行第一列結果 返回Object值

  ExecuteReader() //讀取多行數據

DataReader //讀取一串數據,從DataReader返回的數據都是快速的只是向前的數據流。

DataAdapter //讀取後緩存下來,至關於離線數據庫,包含對鏈接對象以及當對數據庫進行讀取或者寫入的時候自動的打開或者關閉鏈接的引用

OledbConnection、OdbcConnection、SqlConnection 用來連接不一樣的數據庫,換個前綴便可。

Connection 類

connection 用於和數據源創建鏈接,在執行任何操做以前必須創建鏈接。

建立Connection對象時,須要提供鏈接字符串,鏈接字符串是用;分號分割的一系列名稱/值的選項,用來描述鏈接的基本信息。

IDbConnection 接口定義了核心的connection屬性和方法,該接口由全部connection類實現。

一、使用當前登錄到windows的用戶身份訪問數據庫,無需輸入密碼。

string conStr = "Data Source=.; Initial Catalog=hzsweb; Integrated Security=SSPI";

二、使用帳號密碼鏈接數據庫。

string conStr = "Server=localhost;DataBase=hzsweb;uid=sa;pwd=123;";

鏈接字符串中須要指定數據庫所在的服務器(locahost 和. 都是本地的意思),數據庫名稱,以及驗證方式。

由於數據庫放到服務器後 ip 數據庫名都要常常改變,因此常把鏈接字符串放在配置文件中。

設置web.config

<configuration>
  <connectionStrings>
    <add name="connStr" connectionString="Server=localhost;DataBase=hzsweb;uid=sa;pwd=123;"/>
  </connectionStrings>
</configuration>

添加引用

引用->右鍵->添加->框架->選擇->System.Configuration。

在ConnectionStrings集合中獲取鏈接字符串。

var conStr = System.Configuration.ConfigurationManager.ConnectionStrings["connStr"].ToString();

之後若是數據庫參數有變更,只須要修改配置文件便可。

測試鏈接

var conStr = System.Configuration.ConfigurationManager.ConnectionStrings["connStr"].ToString();

//建立connection對象
System.Data.SqlClient.SqlConnection con = new System.Data.SqlClient.SqlConnection();
con.ConnectionString = conStr; //也可使用用構造方法寫在括號中

//打開鏈接
con.Open();

context.Response.Write(con.State.ToString()); //輸出鏈接狀態

//關閉鏈接
con.Close();

結果爲Open,已鏈接狀態。須要注意的是,每次使用完畢須要關閉鏈接,釋放資源。con.Close()效果等同於con.Dispose()。

使用using語句自動釋放資源

using (con)
{
    //打開鏈接
    con.Open();
}

context.Response.Write(con.State.ToString()); //輸出鏈接狀態

輸出結果爲Closed,已關閉。能夠看出在using語塊後是自動釋放資源的。必須實現IDispose接口,纔可使用using語句自動釋放。

鏈接池

鏈接池保持已經打開的數據庫鏈接,這些鏈接在使用相同數據源的會員間共享,這樣就省了不斷建立和銷燬鏈接的時間。

當客戶端調用open()方法請求打開鏈接時,鏈接直接由鏈接池提供而不是再次建立,當調用Close()方法釋放時,它並無被釋放,而是從新回到池中等待下一次請求。

<add name="connStr" connectionString="Server=localhost;DataBase=hzsweb;uid=sa;pwd=123;pooling=true;min pool size=5;max pool size=10"/>

pooling : 是否打開鏈接池,默認爲true。

min pool size : 鏈接池中最小鏈接數。

max pool size : 鏈接池中容許最大鏈接數。

查看鏈接池狀態可使用sql系統存儲過程sp_who2如:

exec sp_who2

若是設置最小鏈接池數量爲5,則數據庫中就有 5 個對應數據庫名的鏈接,若是沒有操做,其狀態就會處於 sleeping 狀態。

Command 類

command類能夠執行全部類型的SQL語句,和connection類同樣,實現了IDbCommand接口。

在使用command類操做數據時,須要指明三個屬性。CommandType,CommandText和Connection。

CommandType 枚舉值:Text(一條SQL語句,默認值),StoredProcedure(存儲過程),TableDirect(表的名稱,獲取表的全部記錄)。

使用ExecuteNonQuery()插入一條數據

int result;

var conStr = ConfigurationManager.ConnectionStrings["connStr"].ToString();

//建立connection對象
using (var con = new SqlConnection(conStr))
{
    //建立command對象
    using (var cmd = new SqlCommand())
    {
        cmd.CommandType = System.Data.CommandType.Text; //默認值 sql語句
        cmd.CommandText = "insert into article ([title],[content],[createTime]) values ('title','content','2015-08-31')"; //執行腳本
        cmd.Connection = con; //cmd使用的鏈接
        con.Open(); //打開鏈接
        result = cmd.ExecuteNonQuery(); //返回受影響行數 插入、修改、刪除 都用這個
    }
}

context.Response.Write(result);

返回結果爲1。

使用ExecuteScalar()返回表中的總記錄數

var conStr = ConfigurationManager.ConnectionStrings["connStr"].ToString();

int count;

using (var con = new SqlConnection(conStr))
{
    using (var cmd = new SqlCommand("select count(*) from news", con)) //構造函數 type默認就是text
    {
        con.Open();
        count = Convert.ToInt32(cmd.ExecuteScalar()); //返回第一行第一個字段的值
    }
}

DataReader 類

能夠讀取多條數據,從DataReader返回的數據都是快速的只是向前的數據流。

DataReader的經常使用方法:

Read() : 判斷下一行是否讀取到數據,若是有返回true,不然爲false。

GetValue() : 返回當前行中指定須要的字段值。

getValues() : 將當前行中的值保存到數組中。可使用DataReader.FieldCount屬性肯定一行記錄的列數。

NextResult() : 多個結果集時,用來獲取下一個結果集

Close() : 關閉Reader

使用ExecuteReader()方法查詢表中全部記錄

var conStr = ConfigurationManager.ConnectionStrings["connStr"].ToString();

System.Text.StringBuilder sb = new System.Text.StringBuilder();

using (var con = new SqlConnection(conStr))
{
    using (var cmd = new SqlCommand("select * from news", con))
    {
        con.Open();
        var rd = cmd.ExecuteReader();
        while (rd.Read()) //一直讀取到沒有爲止
        {
            //sb.Append(rd["title"]);
            sb.Append(rd.GetValue(1));
            sb.Append(rd.GetString(2));
            sb.Append("<br/>");
        }
     rd.Close(); } }

CommandBehavior 自動關掉關聯鏈接

ExecuteReader()方法有一個重載,能夠在關閉DataReader的同時,自動關掉關聯的鏈接。//con.Close()

var rd = cmd.ExecuteReader(CommandBehavior.CloseConnection);

封裝爲方法調用時,頗有用。

多個結果集

當在一個sql語句中使用;分號分割多個查詢結果集時,可使用rd.Nextresult()來找到下一個結果集。

var conStr = ConfigurationManager.ConnectionStrings["connStr"].ToString();

System.Text.StringBuilder sb = new System.Text.StringBuilder();

using (var con = new SqlConnection(conStr))
{
    using (var cmd = new SqlCommand("select top 10 * from news;select top 10 *  from article", con))
    {
        con.Open();
        var rd = cmd.ExecuteReader(CommandBehavior.CloseConnection);

        int i = 1;
        do
        {
            sb.Append("<h2>第" + i + "個結果集:</h2>");

            while (rd.Read())
            {
                for (int j = 0; j < rd.FieldCount; j++)
                {
                    sb.Append("<li>");
                    sb.Append(rd.GetName(j).ToString()); //獲取字段名
                    sb.Append("");
                    sb.Append(rd.GetValue(j).ToString()); //獲取字段值
                    sb.Append("</li>");
                }
            }
            i++;
        } while (rd.NextResult());
        rd.Close();
    }
}

結果爲 拆分的兩組前10條數據。

SQL注入

因爲拼接字符串的不安全,前臺輸入追加條件 or '1' = '1' 則永遠爲真。以及輸入些其餘對數據庫表操做的語句。或者輸入--把你後面的代碼都註釋掉。

解決辦法1: string.Replace("\'","\'\'"); 將全部1個單引號轉換爲兩個單引號

解決方法2:參數化編程,將須要拼接值的地方,用一個參數變量表示,而操做數據庫的時候,給這個參數賦值。

參數化編程

一、將須要拼接字符串的值,用一個@引導的變量名代替。

二、使用SqlParameter類型將參數變量與值綁定在一塊兒。

三、將SqlParameter對象交給Command對象的Prarmeters集合。

using (var con = new SqlConnection(conStr))
{
    using (var cmd = new SqlCommand("delete from article where id = @id", con))
    {
        con.Open();
        cmd.Parameters.Add("@id", 1197);
        sb.Append(cmd.ExecuteNonQuery());
    }
}

調用存儲過程

建立一個存儲過程,添加指定數據,並返回新增的id。

use hzsweb
go

create proc InsertArticle
    @title varchar(250),
    @content varchar(250),
    @createTime datetime,
    @lastId int output
as
begin
    insert into article (title,content,createTime)
        values (@title,@content,@createTime);

    set @lastId = @@IDENTITY;
end

在c#中調用存儲過程

var conStr = ConfigurationManager.ConnectionStrings["connStr"].ToString();

using (var con = new SqlConnection(conStr))
{
    using (var cmd = new SqlCommand("InsertArticle", con))
    {
        cmd.CommandType = CommandType.StoredProcedure; //存儲過程
        con.Open();
        cmd.Parameters.AddRange(new SqlParameter[]{
            new SqlParameter("@title","標題"),
            new SqlParameter("@content","內容"),
            new SqlParameter("@createTime",DateTime.Now.ToString()),
            new SqlParameter("@lastId",SqlDbType.Int,4)
        });
        cmd.Parameters["@lastId"].Direction = ParameterDirection.Output; //設置爲 輸出參數
        int result = cmd.ExecuteNonQuery();
        int lastId = (int)cmd.Parameters["@lastId"].Value;
        context.Response.Write(lastId);
    }
}

事務

事務是一組必須所有成功或所有失敗的操做,事務的目標是保證數據總能處於有效一致的狀態。

事務有4個被稱爲ACID屬性的特徵。

Atomic(原子性):事務中的全部步驟必須同時成功或失敗。

Consist(一致性):事務使底層數據庫在穩定狀態間轉換。

lsolated(隔離性):每一個事務都是獨立的實體,不會互相影響。

Durable(持久性):在事務成功前,事務產生的變化永久的存儲在硬盤上。

使用事務時不要在事務中間使用select語句返回數據,應該在事務開始前返回數據,這樣能夠減小事務鎖定數據的數目。

在T-SQL中使用事務

在T-SQL中使用Begin transaction 開始事務,使用commit提交事務,使用rollback回滾事務。

create proc updateSort
(
    @sortA int,
    @sortB int,
    @id_a int,
    @id_b int
)
as
    begin try
        begin transaction
            update article set sort = @sortA where id = @id_a;
            update article set sort = @sortB where id = @id_b;
        commit --提交
    end try

    begin catch
        if(@@trancount>0)
            rollback --回滾
    end catch

exec updateSort 50,51,1,2

在C#中使用事務

使用connection對象.BeginTransaction()方法返回一個transaction對象,用於管理事務。

var conStr = ConfigurationManager.ConnectionStrings["connStr"].ToString();

using (var con = new SqlConnection(conStr))
{
    var cmd1 = new SqlCommand("update article set sort = @sortA where id = @idA", con);
    var cmd2 = new SqlCommand("update article set sort = @sortB where id = @idB", con);
    cmd1.Parameters.AddRange(new SqlParameter[] { 
        new SqlParameter("@sortA",100),
        new SqlParameter("@idA",1)
    });
    cmd2.Parameters.AddRange(new SqlParameter[] { 
        new SqlParameter("@sortB",101),
        new SqlParameter("@idB",2)
    });
    SqlTransaction tran = null;
    try
    {
        con.Open();
        tran = con.BeginTransaction();
        cmd1.Transaction = tran;
        cmd2.Transaction = tran;

        cmd1.ExecuteNonQuery();
        cmd2.ExecuteNonQuery();

        tran.Commit();
    }
    catch
    {
        tran.Rollback();
    }
}

可使用tran.save("abc");方法指定保存點,並使用tran.Rollback("abc");回滾到某保存點。回滾所有無需輸入參數。

DataSet

DataSet(數據集)內存中的數據庫。

在SQL Server中

數據庫對象(實例\Sqlexpress)

架構(dbo,表的命名空間)

表(列數據)

DataSet中

DataSet實例(new DataSet())

Tables表集合 newDataTable(表名,命名空間)

DataColumn (DataTable中的列)

DataRow(DataTable中的行)

DataAdapter 類

要在DataSet中提取記錄並將它們填入表中,須要使用DataAdapter對象,它含有查詢和更新的所有命令。

SelectCommand 查詢數據。

var conStr = ConfigurationManager.ConnectionStrings["connStr"].ToString();

using (var con = new SqlConnection(conStr))
{
    //建立DataAdapter對象,並設置查詢語句和數據庫鏈接
    SqlDataAdapter sda = new SqlDataAdapter();

    //或直接使用構造方法new SqlDataAdapter("sql",con);
    sda.SelectCommand = new SqlCommand();
    sda.SelectCommand.Connection = con;
    sda.SelectCommand.CommandText = "select * from article";

    DataSet ds = new DataSet();
    //將數據填充到數據集,使用Fill()時自動打開鏈接
    sda.Fill(ds);

    DataTable dt = ds.Tables[0]; //第一個數據表
    foreach (DataRow row in dt.Rows)
    {
        context.Response.Write(row[0]);
        context.Response.Write(row[1]);
        context.Response.Write(row[2]);
        context.Response.Write(row[3]);
        context.Response.Write("<hr>");
    }
}

DeleteComand 刪除數據

var conStr = ConfigurationManager.ConnectionStrings["connStr"].ToString();

using (var con = new SqlConnection(conStr))
{
    SqlDataAdapter sda = new SqlDataAdapter("select top 10 id,title from article",con);
    DataSet ds = new DataSet();
    sda.Fill(ds);

    sda.DeleteCommand = new SqlCommand("delete  from article where id = 1",con);
    sda.Update(ds);

    DataTable dt = ds.Tables[0];
    foreach (DataRow row in dt.Rows)
    {
        context.Response.Write(row[0]);
        context.Response.Write(row[1]);
        context.Response.Write("<hr>");
    }
}

修改使用UpdateCommand,添加使用InsertCommand 不要忘記Update(ds) 不然不會更新。

手動建立一個DataSet

var ds = new DataSet("web");
var dt = new DataTable("table1");
var colId = new DataColumn("id", typeof(int));
var colName = new DataColumn("name", typeof(string));
var colSex = new DataColumn("sex", typeof(string));

colId.AutoIncrement = true; //自增
colId.AutoIncrementSeed = 1; //起始1
colId.AutoIncrementStep = 1; //遞增
colId.Unique = true; //惟一

dt.Columns.Add(colId);
dt.Columns.Add(colName);
dt.Columns.Add(colSex);

ds.Tables.Add(dt);

var row = dt.NewRow();
row[0] = 1;
row[1] = "奉先";
row[2] = "";

dt.Rows.Add(row);

foreach (DataRow r in dt.Rows)
{
    context.Response.Write(r[0]);
    context.Response.Write(r[1]);
    context.Response.Write(r[2]);
    context.Response.Write("<hr>");
}

SQLHelper

將一些重複的數據庫鏈接Connection,Command,DataReader等封裝成一個類,調用方法時只須要傳入一些參數和數據庫鏈接字符串就能夠訪問數據庫了。

public static class SQLHelper
{
    public static readonly string conStr = ConfigurationManager.ConnectionStrings["conStr"].ToString();

    /// <summary>
    /// 執行增、刪、改操做,返回受影響行數
    /// </summary>
    /// <param name="sql"></param>
    /// <param name="cmdParams"></param>
    /// <returns></returns>
    public static int ExecuteNonQuery(string sql, params SqlParameter[] cmdParams)
    {
        using (var con = new SqlConnection(conStr))
        {
            using (var cmd = new SqlCommand(sql, con))
            {
                cmd.Parameters.AddRange(cmdParams);
                con.Open();
                return cmd.ExecuteNonQuery();
            }
        }
    }

    /// <summary>
    /// 返回首行首列信息
    /// </summary>
    /// <param name="sql"></param>
    /// <param name="cmdParams"></param>
    /// <returns></returns>
    public static object ExecuteScalar(string sql, params SqlParameter[] cmdParams)
    {
        using (var con = new SqlConnection(conStr))
        {
            using (var cmd = new SqlCommand(sql, con))
            {
                cmd.Parameters.AddRange(cmdParams);
                con.Open();
                return cmd.ExecuteScalar();
            }
        }
    }

    /// <summary>
    /// 返回只進的讀取流
    /// </summary>
    /// <param name="sql"></param>
    /// <param name="cmdParams"></param>
    /// <returns></returns>
    public static SqlDataReader ExecuteReader(string sql, params SqlParameter[] cmdParams)
    {
        var con = new SqlConnection(conStr);
        try
        {
            using (var cmd = new SqlCommand(sql, con))
            {
                cmd.Parameters.AddRange(cmdParams);
                con.Open();
                return cmd.ExecuteReader(CommandBehavior.CloseConnection);
            }
        }
        catch (Exception e)
        {
            con.Close();
            throw e;
        }
    }

    /// <summary>
    /// 返回DataTable
    /// </summary>
    /// <param name="sql"></param>
    /// <param name="cmdParams"></param>
    /// <returns></returns>
    public static DataTable getDataTable(string sql, params SqlParameter[] cmdParams)
    {
        var ds = new DataSet();
        using (var sda = new SqlDataAdapter(sql, conStr))
        {
            sda.SelectCommand.Parameters.AddRange(cmdParams);
            sda.Fill(ds);
        }
        return ds.Tables[0];
    }

}

調用

//修改
string sql = "update article set sort = @sort where id = @id";
var prams = new SqlParameter[]{
    new SqlParameter("@sort",15),
    new SqlParameter("@id",3)
};
int result = SQLHelper.ExecuteNonQuery(sql);

//條數
string sqlCount = "select count(*) from article";
var count = SQLHelper.ExecuteScalar(sqlCount);

//DataReader
string readerSql = "select id,title,createTime from article";
using (var rd = SQLHelper.ExecuteReader(readerSql))
{
    while (rd.Read())
    {
        ////
    }
}

//DataTable
string dtSql = "select id,title,createTime from article";
var dt = SQLHelper.getDataTable(dtSql);
foreach (DataRow row in dt.Rows)
{
    ///
}
相關文章
相關標籤/搜索