應用說明
數據適配器有SelectCommand、InsertCommand、DeleteCommand、UpdateCommand四種命令對象。分別給每種命令對象賦予相應的命令,就可以用數據適配器對數據集進行更新操作了。
使用數據適配器進行更新有一個很重要的方法,這裏需要特別的說明一下。
SqlDataAdapter一般和SqlCommandBuilder配合使用,可以省略很多SQL語句的編寫工作。
string source ="server=.;integratedsecurity=SSPI;database=wl_client";
string select ="select * fromWlcStore_StoreInfo where storecode = '001'";
SqlConnection conn =new SqlConnection(source);
// 構造適配器的時候提供了一個查詢語句,此時會自動初始化適配器的SelectCommand,此時適配器的其他幾個命令對象未初始化,此時只能填充數據集,還不能更新數據集。
SqlDataAdapter da =new SqlDataAdapter(select, conn);
DataSet ds =new DataSet();
da.Fill(ds, "Customers");
// 注意此語句, 用適配器去初始化一個SqlCommandBuilder,就這麼簡單的一個語句,運行庫自動爲我們完成適配器的三種更新操作的命令對象的初始化工作,運行庫自動生成SQL語句,前面提供的查詢語句就是運行庫生成更新操作SQL語句的依據,所以第一步的查詢語句不能少。
SqlCommandBuilder scb =new SqlCommandBuilder(da);
// 在數據集中新增一行
DataRowdr = ds.Tables[0].NewRow();
dr[0] ="002";
dr[1] ="新增倉庫一";
ds.Tables[0].Rows.Add(dr);
// 修改數據集第一行,第一列的值
ds.Tables[0].Rows[0][0]= "00001";
// 刪除數據集中第二列的值
ds.Tables[0].Rows[1].Delete();
// 直接一個更新語句,ds中所有的更新都會反映到數據庫中。
da.Update(ds,"Customers");
下面解釋一個這個語句的原理:SqlCommandBuilder爲我們自動生成了更新操作語句,包括插入操作,修改操作和刪除操作,語句的生成依據是我們提供的查詢語句(包含數據表,列信息)。DataRow可以記錄行中每一列的版本狀態值(初始值,默認值,修改值等)。那麼在執行Update這個語句時,運行庫會自動查找每一行的主鍵的初始值,根據這個值在數據庫中定位到唯一的一行,然後把這一行的修改保存起來,即使這一行的主鍵值發生了改變,運行庫還是能定位到正確的行上進行修改。
從上面的描述,我們可以看到,要使用這個操作,必須要確保更新的表必須包含主鍵,否則運行庫無法準確定位。
這個方法非常適合那種不要每個操作都實時反映到數據庫,操作完成後集中點擊提交按鈕同一保存的那種場景。
注意事項
如果使用SqlDataAdapter來查詢數據返回給DataSet或者DataTable時需要注意以下幾點:
1、如果SqlDataAdapter的SelectCommand的連接並沒有打開,使用SqlDataAdapter的Fill方法時會自動打開數據庫連接,並在方法執行完畢自動關閉連接。如果連接在使用Fill方法之前已經打開,方法執行結束後會保持連接的現有狀態,不會關閉連接。
2、如果你在同一個Connection上有一系列的連續操作,例如執行多個Fill操作,你應該在最開始使用Connection的Open()方法打開連接,避免使用Fill方法時執行額外的打開連接/關閉連接操作,從而提高了程序的性能。
3、在使用SqlDataAdapter中的SqlCommand對象時,你可以重複的使用同一個SqlCommand去多次執行相同類型的操作,比如說執行多次查詢,但是不要使用同一個SqlCommand去執行不同類型的操作。
4、當SqlDataReader沒有關閉之前,數據庫連接會一直保持open狀態,所以在使用SqlDataReader時,使用完畢應該馬上調用SqlDataReader.Close()關閉它。
5、一個連接只能被一個SqlDataReader使用,這也是爲什麼要儘早關閉SqlDataReader的原因。
6、使用完SqlDataReader後,你可以在程序中顯示的調用數據庫連接對象的Close方法關閉連接,也可以在調用Command對象的 ExecuteReader方法時傳遞CommandBehavior.CloseConnection 這個枚舉變量,這樣在調用SqlDataReader的Close方法時會自動關閉數據庫連接。
7、使用SqlDataReader時儘量使用和數據庫字段類型匹配的方法來取得相應的值,比如對於整形的字段使用GetInt32,對字符類型的字段使用GetString。這樣會減少因爲類型不一致而額外增加的類型轉換操作。
8、使用SqlDataReader獲取多條記錄時,如果沒有訪問到取出記錄的末尾時想要關閉SqlDataReader,應該先調用Command對象的 Cancel方法,然後再調用SqlDataReader的Close方法。Command對象的Cancel方法使得數據庫不再把 SqlDataReader中未訪問的數據發送到調用端,如果不調用此方法直接關閉SqlDataReader,數據庫會發送和 SqlDataReader未訪問數據等長的空數據流到調用端。
9、如果想通過SqlCommand的ExecuteReader方法獲取存儲過程的返回值或者輸出參數,必須先調用SqlDataReader的Close方法後,才能獲取輸出參數的值或者返回值。
10、如果使用SqlDataReader只返回一條記錄,那麼在調用Command的ExecuteReader方法時,指定
CommandBehavior.SingleRow參數,這個參數的是否使用對SQL Server .NET Data Provider沒有什麼影響,但是當你使用OLE DB .NET Data Provider時,指定這個參數後,DataPrivider內部將使用IRow接口,而不是使用相對來說耗費資源的IRowSet接口。
應用場景
1. 有時候需要緩存的時候,比如說在一個商品選擇界面,選擇好商品,並且進行編輯/刪除/更新後,
最後一併交給數據庫,而不是每一步操作都訪問數據庫,因爲客戶選擇商品可能進行n次編輯/刪除
更新操作,如果每次都提交,不但容易引起數據庫衝突,引發錯誤,而且當數據量很大時在用戶執行
效率上也變得有些慢。
2. 有的界面是這樣的有的界面是這樣的,需求要求一定用緩存實現,確認之前的操作不提交到庫,點擊
頁面專門提交的按鈕時才提交商品選擇信息和商品的其它信息. 我經常遇到這樣的情況
3. 有些情況下只往數據庫裏更新,不讀取. 也就是說沒有從數據庫裏讀,sqldataadapter也就不知道是
更新哪張表了,調用update就很可能出錯了。這樣的情況下可以用sqlcommandbuilder 了.
4. 只能更新一個表,不能更新兩個或兩個以上相關聯的表, 表中必須有主鍵
5. 更新的表中字段不能有image類型的
6. 優點:節省代碼量,節省時間,這個方法可以代替所有的: 更新/刪除/插入操作語句
7. 缺點:訪問兩次數據庫(select * tablename,就是這句,要確認是哪個表,除非是很大的數據量,一般是感覺不到的),效率有些慢
使用事務
適配器也可以和事務配合使用,同時提交多個表的更新動作。參照下面這個方法,可以把適配器和事務關聯起來。
public void datasetupdate(dataset ds, sqlconnection sqlconnect, string tablename, string sqlstr,sqltransaction sqltrans)
{
try
{
sqldataadapter adapter = new sqldataadapter(sqlstr, sqlconnect);
sqlcommandbuilder cmdbuilder = new sqlcommandbuilder(adapter);
//創建sqldataadapter 對像的command對像,並將連接對像及事務對像綁定到command對像上
adapter.deletecommand = new sqlcommand("", sqlconnect, sqltrans);
adapter.insertcommand = new sqlcommand("", sqlconnect, sqltrans);
adapter.updatecommand = new sqlcommand("", sqlconnect, sqltrans);
adapter.selectcommand = new sqlcommand(sqlstr, sqlconnect, sqltrans);
//使用getdeletecommand將相對應的sqlcommand對像傳入
adapter.deletecommand = cmdbuilder.getdeletecommand();
adapter.insertcommand = cmdbuilder.getinsertcommand();
adapter.updatecommand = cmdbuilder.getupdatecommand();
//cmdbuilder.refreshschema();
int val = adapter.update(ds, tablename);
ds.tables[tablename].acceptchanges();
}
catch
{
throw;
}
}