| DataBase和DataSet同步數據 l DataAdapter:它起著橋梁的作用,在DataSet 和其源數據存儲區(qū)之間進行數據檢索和保存. l DataAdapter對象可以隱藏和Connection、Command對象溝通的細節(jié),通過DataAdapter對象建立、初始化DataTable,從而和DataSet對象結合起來在內存存放數據表副本,實現離線式數據庫操作. 數據適配器的屬性 – InsertCommand – SelectCommand – DeleteCommand – UpdateCommand l TableMappings:用于維持數據集中的列和數據源中列的關系 l AcceptChangesDuringFill:決定AccepChnages方法是否被添加到數據集中的每一行所調用,默認為true TableMappings集合 – 數據集并不清除它所包含的數據從哪里來,而Connection也不知道它所檢索的數據都發(fā)生了什么改變。 – 數據適配器用于維持這兩者之間的聯系,它通過TableMappings集合來實現這一目標。 l 包括 – DataTableMappings – DataColumnMappings 1. SourceColumn 2. DataSetColumn 
 
 
 [參考代碼] protected System.Web.UI.WebControls.DropDownList ddlClassCode; //省略 SqlDataAdapter da = new SqlDataAdapter("select Distinct classCode from tbClassInfo",con); DataSet ds = new DataSet("myDs"); da.Fill(ds,"Class"); ddlClassCode.DataTextField = "ClassCode"; ddlClassCode.DataSource = ds.Tables["Class"].DefaultView; ddlClassCode.DataBind(); 
 ddlClassCode.SelectedIndex = nIndex; string strSelectedClass = ddlClassCode.SelectedItem.Text; string strSql = "select * from tbStudentInfo where StudentID in (select studentid from tbClassInfo where classcode='"+strSelectedClass+"')"; da.SelectCommand.CommandText =strSql; //映射 da.TableMappings.Add("tbStudentInfo","Student"); da.TableMappings[0].ColumnMappings.Add("StudentID","學生ID"); da.TableMappings[0].ColumnMappings.Add("StudentName","學生姓名"); da.TableMappings[0].ColumnMappings.Add("StudentPass","密碼"); da.TableMappings[0].ColumnMappings.Add("Sex","性別"); da.TableMappings[0].ColumnMappings.Add("BirthDay","生日"); da.TableMappings[0].ColumnMappings.Add("Email","郵件地址"); da.TableMappings[0].ColumnMappings.Add("Score","成績"); 這樣在DataSource中的列名就被映射為后者。 DataSet版本 – Current該行中包含當前值。 – Default根據當前DataRowState,為默認行版本。 – Original該行中包含其原始值。 – Proposed該行中包含建議值。 DataRowVersion 在調用DataRow 對象的BeginEdit 方法之后,如果更改該值,則Current 和Proposed 值變得可用。 在調用DataRow 對象的CancelEdit 方法之后,Proposed 值將被刪除。 在調用DataRow 對象的EndEdit 方法之后,Proposed值變成Current 值。 在調用DataRow 對象的AcceptChanges 方法之后,Original 值變得與Current 值相同。 在調用DataTable 對象的AcceptChanges 方法之后,Original 值變得與Current 值相同。 在調用DataRow 對象的RejectChanges 之后,Proposed 值將被丟棄,版本變成Current。 
 如果對 DataSet、DataTable 或 DataRow 調用 AcceptChanges,則將使 DataRow 的所有 Original 值都將被重寫為該 DataRow 的 Current 值。如果已修改將該行標識為唯一行的字段值,那么當調用 AcceptChanges 后,Original 值將不再匹配數據源中的值。 Fill方法的使用 對于某些情況,如希望從多個數據源填充一個DataSet,再將其寫回另外一個數據存儲,這時要把DataAdapter的屬性AcceptChangesDuringFill設置為false,以便讓結果行表現為新添加的行。 
 [參考代碼] //將指定的行Fill到SqlDataAdapter中 //0表示要Fill的行標 1表示要Fill的行數 da.Fill(ds,0,1,"AuthorAndTitle"); Update方法 在調用Update 之前,必須顯式設置這些命令。如果調用了Update 但不存在用于特定更新的相應命令(例如,不存在用于已刪除行的DeleteCommand),則將引發(fā)異常。 [參考代碼] SqlDataAdapter catDA = new SqlDataAdapter("SELECT * FROM Categories", nwindConn); catDA.UpdateCommand = new SqlCommand("UPDATE Categories SET CategoryName = @CategoryName " +"WHERE CategoryID = @CategoryID" , nwindConn); catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName"); SqlParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int); workParm.SourceColumn = "CategoryID"; //如果下面的不一致,說明數據庫已經更新了 workParm.SourceVersion = DataRowVersion.Original; DataSet catDS = new DataSet(); catDA.Fill(catDS, "Categories"); DataRow cRow = catDS.Tables["Categories"].Rows[0]; cRow["CategoryName"] = "NewName"; catDA.Update(catDS,"Categories"); 
 MSDN上面的解釋: 1. 當調用 Update 方法時,DataAdapter 將分析已作出的更改并執(zhí)行相應的命令(INSERT、UPDATE 或 DELETE)。當 DataAdapter 遇到對 DataRow 的更改時,它將使用 InsertCommand、UpdateCommand 或 DeleteCommand 來處理該更改。這樣,您就可以通過在設計時指定命令語法并在可能時通過使用存儲過程來盡量提高 ADO.NET 應用程序的性能。在調用 Update 之前,必須顯式設置這些命令。如果調用了 Update 但不存在用于特定更新的相應命令(例如,不存在用于已刪除行的 DeleteCommand),則將引發(fā)異常。 2. 請注意,在 UPDATE 語句的 WHERE 子句中指定的參數設置為使用 SourceColumn 的 Original 值。這一點很重要,因為 Current 值可能已被修改,并且可能不匹配數據源中的值。Original 值是曾用來從數據源填充 DataTable 的值。 3. 如果 SelectCommand 返回 OUTER JOIN 的結果,則 DataAdapter 不會為生成的 DataTable 設置 PrimaryKey 值。您必須自己定義 PrimaryKey 以確保正確解析重復行 CommandBuilder 滿足以下條件,就可以使用CommandBuilder自動生成命令 (1) DataTable 映射到單個數據庫表或從單個數據庫表生成 (2) 必須使用了SelectCommand命令了,并有主建 為了生成INSERT、UPDATE 或DELETE語句,CommandBuilder 會自動使用SelectCommand 屬性來檢索所需的元數據集。 
 [參考代碼] SqlDataAdapter catDA = new SqlDataAdapter("SELECT * FROM Categories", nwindConn); catDA.InsertCommand = new SqlCommand("Insert into Categories(CategoryName,Description) values"+" (@CategoryName,@Description)", nwindConn); catDA.InsertCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName"); catDA.InsertCommand.Parameters.Add("@Description", SqlDbType.NText, 16, "Description"); DataSet catDS = new DataSet(); catDA.Fill(catDS, "Categories"); 
 DataRow dr = catDS.Tables["Categories"].NewRow(); dr["CategoryName"] = "Added New Name"; dr["Description"] = "my Description"; catDS.Tables["Categories"].Rows.Add(dr); 
 catDA.Update(catDS,"Categories"); 數據適配器的事件 OnRowUpdated:在數據行更新后執(zhí)行。其最佳用法是檢查單條更新語句的執(zhí)行結果。 
 SqlRowUpdatedEventArgs屬性 屬性 
 [參考代碼] myDataAdapter.RowUpdating += new SqlRowUpdatingEventHandler(MyUpdatingHandler); //省略,OnRowUpdating主要用于在更新前判斷數據源是否已經被其他客戶端更改 public void MyUpdatingHandler(object adapter,SqlRowUpdatingEventArgs e) { switch(e.StatementType) { case StatementType.Update: { SqlConnection myConnection = new SqlConnection( "server=(local);uid=sa;pwd=111;database=Pubs" ); string strSql = "Select * From Authors where au_fname='" +e.Row["au_fname",DataRowVersion.Original]+"'"; SqlCommand com = new SqlCommand(strSql,myConnection); myConnection.Open(); if(com.ExecuteNonQuery()==0) { Response.Write("出錯!有用戶已經修改過數據集!"); e.Status = UpdateStatus.ErrorsOccurred;//報錯 } myConnection.Close(); break; } } } myDataAdapter.RowUpdated += new SqlRowUpdatedEventHandler(MyUpdatedHandler); //省略,OnRowUpdated主要用于更新出錯時,是否忽略錯誤繼續(xù)更新 public void MyUpdatedHandler(object adapter,SqlRowUpdatedEventArgs e) { switch(e.StatementType) { case StatementType.Update: if(e.Status==UpdateStatus.ErrorsOccurred) { e.Status = UpdateStatus.SkipCurrentRow; break; } } } 使用數據適配器最佳實踐 SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Customers;SELECT * FROM Orders;", myConnection); da.TableMappings.Add("Customers1", "Orders"); DataSet ds = new DataSet(); da.Fill(ds, "Customers"); 避免自動增量值沖突 例如一個表,它的主鍵列CustomerID 是自動增量的。兩個新的客戶信息行添加到表中,并接收到自動增量的CustomerID 值1 和2。然后,只有第二個客戶行被傳遞給DataAdapter 的方法Update,新添加的行在數據源接收到一個自動增量的CustomerID 值1,與DataSet 中的值2 不匹配。當DataAdapter 用返回值填充表中第二行時,就會出現約束沖突,因為第一個客戶行已經使用了CustomerID 值1。 要避免這種情況,建議把DataSet 中的列創(chuàng)建為AutoIncrementStep 值等于-1 并且AutoIncrementSeed 值等于0,另外,還要確保數據源生 成的自動增量標識值從1 開始,并且以正階值遞增。 CommandBuilder 的使用 只要DataAdapter的Command屬性為空,CommandBuilder就僅僅創(chuàng)建一個Command,即使你明確地指定Command的屬性值,CommandBuilder也不會重寫,所以如果你想創(chuàng)建一個Command并保留以前的屬性設置,那么就把Command的屬性設置為null。 QA A:對,是數據庫。 
 Q:在智能客戶端中,有多個客戶端對服務器端上的數據進行增、刪、改操作,客戶端可以脫機進行操作,通過計時器與服務器同步數據 在一個客戶端上怎樣把別的客戶端刪除的記錄同步到本地呢? A:建議把客戶端用戶對服務器數據庫中的刪除不做真正刪除,只是做刪除標記。這樣就可以把它同步到本地了。 
 Q: 在更新表時出錯,是否是因為被更新的表沒有設置主鍵? A:對。通常情況如果沒有設置主鍵,會更新失敗。 
 Q: DataSet有多行(n行)需要被更新時,調用DataAdapter的Update方法后,RowUpdated事件,是觸發(fā)一次,還是觸發(fā)多次(n次)? A:觸發(fā)n次,每更新一行都觸發(fā)。 
 Q: DataAdapter中的Command似乎不是很靈活(我是指只能寫死在代碼嗎?),我是想,能不能通過配置文件來?這樣會比較靈活! A:可以把他寫到XML文件中,讀入XML文件,得到對應的Command,也是可以的。 | 
|  | 
來自: 悟靜 > 《.net和asp.net》