在上兩篇文章中,對於dataset做了個簡單的探討.很多次提到,可以把dataset看作是內存中的數據庫.既然是數據庫,那麼首要任務就是存儲數據用的.而之前,我所舉的的例子都是手工往dataset裡添加數據,其實這個並不是我們最想要的.我們最想要的是能用程序把數據庫裡的數據讀取出來然後裝到dataset裡,然後用程序來處理dataset裡的數據.當處理好dataset裡的數據後,我們還想把這些數據再更新到數據庫中. 這樣就能與數據庫斷開連接而處理數據了(把數據庫中的數據讀取出來並裝到dataset裡就可以斷開程序與數據庫的連接了,此時可以對dataset裡數據處理).
那麼要實現上面所說的,就有一個關鍵的問題.我們知道程序可以與數據庫連接並讀取數據,但是把這些讀取到的數據怎麼裝到dataset裡呢?用前面的知識,我們知道可以用SqlDataAdapter裡的read()方法,讀一行,然後在把每行的數據按字段名取出來,一個一個的添加到dataset裡的datatable中.這個過程要求datatable先創建列,然後再一行一行往裡添加SqlDataAdapter.read()方法讀取的數據.這樣也可以把數據裡的數據添加到dataset裡,但是本質還是手工往dataset中的datatable裡添加數據.(手工添加,就是用代碼在datatable裡創建列,再加行的意思).要是都是這樣來把數據庫裡的數據裝到dataset中是不是太麻煩了!現在我們就想一下把數據庫裡的很多記錄甚至一張表裡的數據,一下裝到datatable裡,不要一行一行的手工添加進去,這樣又方便又高效.為了滿足這樣一個願望,SqlDataAdapter類就引出來了.
SqlDataAdapter類,可以把數據庫裡的數據讀出來,然後把這些數據一下就裝到了dataset中.而且還可以把dataset裡的數據一下又裝到數據庫裡(或更新到數據裡).那麼SqlDataAdapter類是不是就相當於dataset與數據庫只之間的橋梁? 像這樣:dataset------SqlDataAdapter-------數據庫
SqlDataAdater如何將數據裝到dataset裡呢?它有個重要的方法叫:SqlDataAdater.Fill(...).調用這個方法的時候就可以把數據填充到dataset裡的,這個方法的參數,看一下文檔就知道了.可以直接填到一個datatable裡如:Fill(datatable)也可以這樣Fill(dataset).等等.不管怎麼樣,數據最終是存在datatable裡的.SqlDataAdater怎麼從數據庫裡得到數據呢?第一篇文章講了程序如何與數據庫操作,說到一個類叫:SqlCommand,它就是負責傳遞命令給數據庫的.要想得到數據庫的數據肯定也要用這個傳遞命令,而且上傳遞一個查詢的命令.然後SqlDataAdater可以支配這個SqlCommand.當數據庫得到SqlCommand傳來的命令後執行查詢命令,並返回查詢的數據.SqlDataAdater此時就可以把這些數據庫返回的數據填充到了datatable裡了.
看一段代碼,以sql2000中Northwind數據庫為例.
Code1
1 static DataSet ds;
2 protected void Page_Load(object sender, EventArgs e)
3 {
4 if (!Page.IsPostBack)
5 {
6 bindGridView();
7 }
8 }
9 public void bindGridView()
10 {
11 string strSql = "select employeeid,firstname,city,country from Employees";
12 SqlConnection cn = new SqlConnection();
13 cn.ConnectionString = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString;
14
15 ds = new DataSet();
16
17 SqlCommand cmd = new SqlCommand(strSql, cn);
18
19 SqlDataAdapter da = new SqlDataAdapter();
20
21 //SelectCommand表示是SqlCommand傳送給數據庫的是查詢語句
22 da.SelectCommand = cmd;
23
24 //開始發送命令(SqlCommand)給數據庫,把返回的數據填充到dataset裡的表名叫table1的datatable裡
25 da.Fill(ds, "table1");
26
27 //上面已經把ds裡的table1填充上數據了,所以可以綁定到table1
28 GridView1.DataSource = ds.Tables["table1"].DefaultView;
29 GridView1.DataBind();
30
31 }
32
上面代碼就可以完成一次從數據庫讀取數據並填充到dataset裡.看了上面一個代碼可發現一個問題?沒有調用cn.open()方法,怎麼連上數據庫的.前面不是講必須調用cn.open()方法才能連上數據庫嗎?在這裡其實也調用了,只不過是SqlDataAdapter會自動的完成,不需要我們來顯示的操作了.也就是SqlDataAdapter能自動打開連接.SqlDataAdapter不僅能自動打開一個連接,而且,它還能控制SqlCommand發送命令.再看da.Fill(ds, "table1")總參數是什麼意思,是把數據填充到ds的table1裡.ds裡有名為table1的表嗎?我們創建ds的時候並未添加表.這個也是SqlDataAdapter控制的.da.Fill(ds, "table1")其實是往ds中名為table1的表中加數據,如果沒有這個表就自動創建一個名叫table1的表.
可見da.Fill(ds, "table1")方法需要完成事還挺多的,需要完成的事有:1、打開一個連接;2、發配SqlCommand去數據庫;3、把數據庫返回的數據填充到ds裡,還指定了往ds的哪個表裡裝數據;4、關閉連接.由此看來SqlDataAdapter是挺忙的.
我們再看如下代碼:
Code2
1 static DataSet ds;
2 protected void Page_Load(object sender, EventArgs e)
3 {
4 if (!Page.IsPostBack)
5 {
6 bindGridView();
7 }
8 }
9 public void bindGridView()
10 {
11 string strSql = "select employeeid,firstname,city,country from Employees";
12 SqlConnection cn = new SqlConnection();
13 cn.ConnectionString = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString;
14
15 ds = new DataSet();
16
17 //SqlCommand cmd = new SqlCommand(strSql, cn);
18
19 SqlDataAdapter da = new SqlDataAdapter(strSql,cn);
20
21 //SelectCommand表示是SqlCommand傳送給數據庫的是查詢語句
22 //da.SelectCommand = cmd;
23
24 //開始發送命令(SqlCommand)給數據庫,把返回的數據填充到dataset裡的表名叫table1的datatable裡
25 da.Fill(ds, "table1");
26
27 //上面已經把ds裡的table1填充上數據了,所以可以綁定到table1
28 GridView1.DataSource = ds.Tables["table1"].DefaultView;
29 GridView1.DataBind();
30
31 }
32
運行後發現也可以,效果和上面的是一樣.但是沒有SqlCommand對象啊,誰把查詢命令傳遞給數據庫呢?(SqlCommand是用來傳遞命令的).其實這是另一中寫法.也就是在創建SqlDataAdapter對象的時候,給了兩個參數strsql和cn,這樣在SqlDataAdapter內部就會自動生成SqlCommand對象.最終發送命令的還是SqlCommand對象,SqlDataAdapter對象是不能通知數據庫的(這是我猜測的).
上面代碼運行後通過gridview可以發現,顯示出來的字段名和數據庫表中的字段名是一樣的(employeeid等).有時我們想在datatable中的字段名改下,怎麼做?在SqlDataAdapter有個叫做映射的東西,什麼叫映射?簡單的理解就是誰對應誰的意思,在這當然是數據庫表字段的名稱對於datatable中的字段的名稱.比如說在數據庫表裡的字段名為:employeeid.這個字段對應的在datatable中的字段名叫id號.這就是映射的意思.如何用映射關系呢?是在SqlDataAdapter中添加的,SqlDataAdapter裡可以有很多組映射,一個表嘛添加一個映射就行了,然後設置所對應的字段的映射名.
看如下代碼:
Code3
1 static DataSet ds;
2 protected void Page_Load(object sender, EventArgs e)
3 {
4 if (!Page.IsPostBack)
5 {
6 bindGridView();
7 }
8 }
9 public void bindGridView()
10 {
11 string strSql = "select employeeid,firstname,city,country from Employees";
12 SqlConnection cn = new SqlConnection();
13 cn.ConnectionString = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString;
14
15 ds = new DataSet();
16 SqlDataAdapter da = new SqlDataAdapter(strSql,cn);
17
18 //添加一個映射,源表名為:employees,dataset裡表名稱為:table1
19 da.TableMappings.Add("employees", "table1");
20 //字段名稱的對應關系
21 da.TableMappings[0].ColumnMappings.Add("employeeid", "id號");
22 da.TableMappings[0].ColumnMappings.Add("firstname", "姓名");
23 da.TableMappings[0].ColumnMappings.Add("city", "城市");
24 da.TableMappings[0].ColumnMappings.Add("country", "國家");
25
26 //如果添加了映射,那麼da.Fill(ds,源表名)方法的第二個參數必須和da.TableMappings.Add("employees", "table1")第一個參數相等
27 da.Fill(ds, "employees");
28
29 //上面已經把ds裡的table1填充上數據了,所以可以綁定到table1
30 GridView1.DataSource = ds.Tables["table1"].DefaultView;
31 GridView1.DataBind();
32
33 }
34
上面代碼就添加了一組映射關系,無須解釋了,一看便知道什麼意思.運行後發現,在gridview上,列名顯示的就是id號,姓名,城市,國家了,而不是源表中的英文了.當然dataset裡表"table1"的列名就是映射這些名字.
值得注意的是在添加映射的時候da.TableMappings.Add("employees", "table1")中第一個參數,也就是源表名可以隨便寫的,比如寫成adb,123都可以,但是這裡怎麼寫,da.Fill(ds, "employees")中第二個參數就要怎麼寫.否則出錯.為什麼可以隨便寫,這個好理解嘛,我只要知道你源表裡的字段就行了,你表名我又不管你,要映射的是字段名稱.表名隨便寫就隨便寫,只要字段確實是原來那字段就行了.當然如果把原來的字段也隨便寫了如:da.TableMappings[0].ColumnMappings.Add("abc", "id號")那麼這個字段的映射就不起作用了,源表總並無abc這個字段.gridview上面這個字段顯示的還是employeeid.再一,da.TableMappings.Add("employees", "table1")中第二個參數的意思是要映射的dataset裡datatable的名稱,如果沒有這個名稱的表,那麼就創建一個.可見gridview綁定到dataset裡的哪個表也要與映射的表名(table1)一致.
上面講了如何用SqlDataAdapter把數據庫裡的數據填充到dataset中以及列的映射.下面我們看看如何把dataset裡的數據更新到數據庫表中.
我們先從數據庫表裡讀取數據,然後把這些數據填充到dataset裡.接著我們把dataset裡的數據修改一下,再把此時修改後的dataset裡的數據更新到數據中
看一段代碼:
Code4
1 protected void Button5_Click(object sender, EventArgs e)
2 {
3 SqlConnection cn = new SqlConnection();
4 cn.ConnectionString = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString;
5
6 SqlDataAdapter da = new SqlDataAdapter();
7 //頁面一打開ds中的table1已經被填充上數據了,下面把table1中字段為"國家"的值改為china,每行都改.
8 foreach (DataRow dr in ds.Tables["table1"].Rows)
9 {
10 dr["國家"] = "CHINA";
11 }
12 //指定是更新命令,SqlCommand用的是匿名對象@country表示它為參數
13 da.UpdateCommand = new SqlCommand("update employees set country=@country", cn);
14
15 //在上面的SqlCommand中加一個參數,並指定這個參數的值等於ds裡table1的"國家"字段下的值
16 da.UpdateCommand.Parameters.Add("@country", SqlDbType.VarChar, 50, "國家");
17
18 //設定SqlCommand發送命令給數據庫,數據庫執行命令後不返回執行命令的情況(不加這行會出錯)
19 da.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;
20
21 //設置數據庫每次更新的行數,此處設置為9行
22 da.UpdateBatchSize = 9;
23
24 //開始更新,把ds中table1裡的數據更新到數據裡
25 da.Update(ds,"table1");
26
27 }
28
上面的代碼一執行就就可以更新數據庫了.看起來好象不好理解.怎麼理解呢?
1,table1裡的數據是從數據庫裡讀取到的,我們把table1裡的某列("國家")下的值修改了,也就上面那個循環
2,用sqlcommand傳遞更新語句給數據庫,那更新的值為多少呢?用了一個參數(參數是sqlcommand裡添加的,應用sql語句封裝在sqlcommand裡面),這個參數可以指定用datatable中那一列,上面代碼指定的是"國家"了
3,設置與數據庫一次更新幾行.這裡如果設置為1,表示sqlcommand發送一次命令給數據庫,數據庫只更新一行,然後再發送一次,數據再更新一行,table1裡有幾行就發送幾次,直到每行都更新完.不是說執行完了,數據庫只更新了一行.而是把table1裡的每行都更新到數據裡了,不過是一次一次的更新的.顯示一次更新多行的效率比較高.
4,怎麼更新呢?第25行就把table1裡的數據更新到了數據裡.(其實更新語句就是sqlcommand裡的sql語句,只是添加參數並把參數的值對應到table1裡的某列時不好形象的理解)
看著糊塗,動手寫遍代碼就清楚了.
我們再來看看如何增加一條記錄到數據裡其實和上面是一個原理,我們把dataset中的datatable添加一行,然後用SqlDataAdapter把dataset更新到數據裡,數據庫也就添加一行了.
代碼如下:
Code6
1 protected void Button4_Click(object sender, EventArgs e)
2 {
3 string strSql = "insert into employees(firstname,city,country,lastname) values(@firstname,@city,@country,@lastname)";
4 SqlConnection cn = new SqlConnection();
5 cn.ConnectionString = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString;
6
7 SqlDataAdapter da = new SqlDataAdapter();
8 da.InsertCommand = new SqlCommand(strSql, cn);
9
10 da.InsertCommand.Parameters.Add("@firstname", SqlDbType.VarChar, 50, "姓名");
11 da.InsertCommand.Parameters.Add("@city", SqlDbType.VarChar, 50, "城市");
12 da.InsertCommand.Parameters.Add("@country", SqlDbType.VarChar, 50, "國家");
13
14 //下面三行代碼有點意思
15 SqlParameter sp = new SqlParameter("@lastname", SqlDbType.VarChar, 50);
16 sp.Value = "小三";
17 da.InsertCommand.Parameters.Add(sp);
18
19 //下面就往table1裡添加一條記錄了
20 DataRow dr = ds.Tables["table1"].NewRow();
21 dr["姓名"] = "張三";
22 dr["城市"] = "合肥";
23 dr["國家"] = "中國";
24 ds.Tables["table1"].Rows.Add(dr);
25 //此時的table1是多一條記錄的更新到數據裡
26 da.Update(ds, "table1");
27
28 GridView1.DataSource = ds.Tables["table1"].DefaultView;
29 GridView1.DataBind();
30
31 }
32
上面就添加了一行數據到數據庫裡,添加了四個字段的值.看15,16,17行代碼,挺有意思.
我們說把dataset跟新到數據庫裡.其實就是在構建sql語句的時候,指定sql語句中參數的值來自dataset裡,並指定到具體的哪列.看10,11,12行代碼,指定了@firstname,@city,@country三個參數的值來自dataset裡表table1裡的三列(姓名,城市,國家).table1裡只有三個字段,是不是就只能更新數據表裡三個字段呢?非也.這兩個是沒有關系的.雖然table1裡只有三個字段,但是我也可以更新數據庫表裡的多個字段,上面代碼中table1裡並沒有lastname這個字段,那麼我要更新的數據庫的值我不用table1裡的是了,我自己隨便設個("小三").@lastname參數,添加到sqlcommand裡就行了.sqlcommand在哪?就是da.InsertCommand啊!
上面無非在說,我們可以把sql語句中的參數需要的值等於dataset裡的數據,然後會自動對應到我們設好的列上(像10,11,12行樣),當然我們不用dataset的數據作為參數的值當然可以.上面代碼運行後發現,gridview中所顯示的新添加的行,列"id號"上沒有值,大家自己想想為什麼,很簡單的.
突然發現對於sqlcommand參數這部分前面的文章沒有講,不過不難,看看就知道了.......
下面我們來看看SqlDataAdapter.Fill(dataset,int類型,int類型,string類型)方法中有四個有趣的參數.這個四個參數是什麼意思呢?,第一個要填充的dataset,第二個的意思是從第幾條記錄開始填充,第三個的意思是填充的記錄數,第四個源表的名稱.比如:Fill(dataset,0,1,"table1")是什麼意思呢?就是往dataset裡的table1裡填充數據,從數據庫裡讀取的數據中,從第0條開始填充,填充1條.那麼dataset裡被裝了幾條記錄,當然是一條.
上面一段話,我能想到什麼?我們可以從數據裡讀取若干條的記錄,然後指定從哪條開始填充幾條到dataset中,而不是把從數據庫讀取的數據全部填充到dataset中.我們可以想到分頁效果,是不是?比如:我們第一次把數據庫表中數據讀取出來,然後把第0條記錄裝到dataset裡,點"下一頁"再把數據庫表裡數據讀出來然後把第1條裝到dataset裡.依次類推是不是就可以完成分頁效果了.我們用代碼來說明.
代碼如下:
Code8
1 int maxCount = 9; //最大頁數,當然我數據裡只有9條記錄所以這麼寫,一般都是查詢下數據裡的記錄總數
2 static int pageN = 0; //第幾頁的意思,0就第0條記錄,1就是第一條記錄嘛.
3 static DataSet ds;
4 protected void Page_Load(object sender, EventArgs e)
5 {
6 if (!Page.IsPostBack)
7 {
8 //頁面第一次加載,顯示第0條記錄(第一頁)
9 bindGridView(0);
10 }
11 }
12 public void bindGridView(int pageN)
13 {
14 string strSql = "select employeeid,firstname,city,country from Employees";
15 SqlConnection cn = new SqlConnection();
16 cn.ConnectionString = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString;
17
18 ds = new DataSet();
19 SqlDataAdapter da = new SqlDataAdapter(strSql,cn);
20
21 //添加一個映射,源表名為:employees,dataset裡表名稱為:table1
22 da.TableMappings.Add("employees", "table1");
23 //字段名稱的對應關系
24 da.TableMappings[0].ColumnMappings.Add("employeeid", "id號");
25 da.TableMappings[0].ColumnMappings.Add("firstname", "姓名");
26 da.TableMappings[0].ColumnMappings.Add("city", "城市");
27 da.TableMappings[0].ColumnMappings.Add("country", "國家");
28
29 //往ds裡填充數據,在從數據庫中讀取的記錄中從第pageN開始填充,填充1條
30 da.Fill(ds, pageN, 1, "employees");
31
32
33 GridView1.DataSource = ds.Tables["table1"].DefaultView;
34 GridView1.DataBind();
35
36 }
37 //上一頁
38 protected void Button1_Click(object sender, EventArgs e)
39 {
40 if (pageN >= 0)
41 {
42 pageN--;
43 if (pageN < 0)
44 {
45 pageN = maxCount - 1; //最後一頁
46 }
47 bindGridView(pageN);
48
49 }
50 }
51 //下一頁
52 protected void Button2_Click(object sender, EventArgs e)
53 {
54 if (pageN < maxCount)
55 {
56 pageN++;
57 if (pageN == maxCount)
58 pageN = 0;
59
60 bindGridView(pageN);
61
62 }
63 }
64
上面代碼就是簡單的分頁,每次顯示一頁的.原來很簡單.每次往dataset裡裝一條記錄,下一頁就往dataset裡裝下一條記錄,上一頁就往dataset裡裝上一條記錄.
要注意,從數據庫讀出的記錄SqlDataAdapter是把這些記錄從0開始記數的.比如數據庫裡讀9條記錄,那麼SqlDataAdapter把這9條記錄表示為0~8.所以上面代碼第45行要減一. 上一頁,下一頁裡的代碼看不懂,單步調試一下就差不多搞清楚了(這個也不好解釋).
這種分頁其實並不常用,因為牽涉到效率問題,你想啊,每次翻頁的時候SqlDataAdapter都要把表裡的數據都讀出來然後只要一條記錄(填充到dataset裡).是不是對性能有一定的影響.數據庫表裡記錄少還好,要是多,那就做了很多無用功了.
忘了講一個問題,就是當我們更新dataset裡的數據到數據裡時,有的字段不想讓他改變,比如id號,我們並不想讓它改變
這樣update employees set firstname=@firstname,city=@city,country=@country where employeeid=@employeeid
employeeid我們並不想讓它,而我們在dataset更改了它怎麼辦?肯定更新到數據庫裡會出錯的.可以這樣解決,看下面代碼
Code7
1 string strSql="update employees set firstname=@firstname,city=@city,country=@country where employeeid=@employeeid";
第7行的意思是:設置datarow中行的版本為原始值.什麼意思?就把datast裡id號字段的值改了,它用是它未改前的值(原始值).也就是確保了id為原來的id,讓修改無效.
2 SqlDataAdapter da = new SqlDataAdapter();
3 da.UpdateCommand = new SqlCommand(strSql, cn);
4
5 SqlParameter sp = da.UpdateCommand.Parameters.Add("@employeeid", SqlDbType.Int);
6 sp.SourceColumn = "id號";
7 sp.SourceVersion = DataRowVersion.Original;
8
不講了,真累人.今天就講這麼多.感覺這次講的最不好..............
下次再講把.
還有並發控制,2.0部分新特性,對xml的操作,事物處理,數據緩存,SQLCRL,零碎知識.等等...等等...
越講越多,講不完了!!!!!!!!!!