数据库-ADONET-向数据库提交更新
?
向數據庫提交更新
ADONET對于提交更新的功能和控制是前所未有的。但是,如何有效的行使這種控制和能力?
很多人在ADONET中使用CommandBuilder來生成更新邏輯,有時代碼段會滴油警告“說應該生成自己的更新邏輯”,但是這些注釋不會解釋為什么和如何做。
對于如何使用ADONET提交更新了解的越多,那么生成自己的更新邏輯和(或)通過存儲過程提交更新就越得心應手。
理解如何使用DataAdapter將DataSet中的掛起更改提交到數據庫,學習如何以及什么適合使用工具,來節省時間,而不影響性能或控制。
你可以輕易的創建“非類型化”和“強類型”的DataSet,用來存儲由DataAdapter返回的數據。
還可以修改DataSet的內容,產生數據更改。
DataAdapter對象公開了一個方法:“Update”,用它可以向數據庫提交“掛起的更改”。
舉一個例子:對于Northwind數據庫的訂單表,可以把訂單數據放到一個DataSet實例中,用戶做了一些修改,使用DataAdapter提供的Update方法進行提交,代碼像這樣:
Dim strConn,strSql as String
strConn=”Provider=SQLOLEDB;Data Source=...;Initial Catalog=Northwind;Trused_Connection=Yes;”
strSql=”select OrderID,ProductID,Quantity,UnitPrice from [Order Details] where OrderID=10503
?order by ProductID”
Dim da as New OleDBDataAdapter(strSql,strConn)
Dim tbl as New DataTable(“訂單明細”)
Da.Fill(tbl)’到這里才算完成了提取數據的工作
?
‘下面是修改訂單代碼
Tbl.Rows(0).Delete()
Tbl.Rows(1)(“Quantity”)=Cshort(tbl.Rows(1)(“Quantity”)*2)
Tbl.Rows.Add(new Object(){10503,1,24,18})
?
‘下面代碼提交更新到數據庫
Try
?????? Da.Update(tbl)
?????? Console.WriteLine(“提交更新 – 成功!”)
Catch ex as Exception
?????? Console.WriteLine(“調用DataAdapter.Update方法拋出異常:” & vbCrLf & ex.Message)
End Try
這段代碼會成功編譯,但是,它不會將訂單的更改成功的提交給數據庫。
你會收到一個異常,提示“更新在傳遞帶有刪除的行的DataRow集合時需要有效的DeleteCommand”。
這種異常使人非常困惑,因為,原來的數據訪問技術,比如ADO等,都包含自動提交改變的功能,ADONET雖然可以使用DataAdapter提交改變,但是DataAdapter不會包含自動提交更新所需要的邏輯(相關的Command對象)
如何向ADONET的DataAdapter添加必要的更新邏輯呢?有三種基本的選擇:
l?編寫自己的代碼
l?讓ADONET生成更新邏輯
l?使用“數據適配器配置向導”,這樣的代碼生成工具。
1????????? 有關歷史
先看一下ADO(ADONET的前身)如何運作這個過程?ADONET不會自動生成更新邏輯;但是,ADO會。
可以通過“查看ADO游標引擎自動提交更新的方式”,了解ADO的運作過程,進而,我們要了解,為什么ADONET要選擇采用不同的方式?為什么要強迫開發者編寫自己的更新邏輯?
ADO游標,支持與“ADONET的DataSet”類似的功能。
可以使用一個客戶端的ADO Recordset,作為脫機數據緩存。另外,Recordset也是ADO向數據庫提交更新的機制。
l?代碼演示:獲取訂單的內容,修改訂單,向數據庫提交掛起(運行環境:經典VB,ADO2.x)
Dim strConn as String ,strSql as String
strConn=”Provider=SQLOLEDB;Data Source=...;Initial Catalog=Northwind;
?????? Trusted_Connection=Yes;”
strSql=”select OrderID,ProductID,Quantity,UnitPrice from [Order Details] where OrderID=10503
?????? ?order by OrderID”
Dim rs as ADODB.Recordset
Set rs=new ADODB.Recordset
Rs.CursorLocation=adUseClient
Rs.Open strSql,strConn,adOpenStatic,adLockBatchOptimistic,adCmdText
?
Rs.Delete
Rs.MoveNext
?
Rs.Fields(“Quantity”)=2* rs.Fields(“Quantity”)
Rs.Update
?
Rs.AddNew
Rs.Fields(“OrderID”)=10503
Rs.Fields(“ProductID”)=1
Rs.Fields(“Quantity”)=24
Rs.Fields(“UnitPrice”)=18
Rs.Update
?
Rs.UpdateBatch
?
Rs.Close
Cn.Close
1.1???????? 使用ADO Recordset提交更新的好處
第一個好處:只需要最少的代碼。代碼沒有更新邏輯,因為,ADO會在運行時自動生成這個邏輯,
這是另一個好處,ADO不需要開發者在程序中提供更新邏輯。可以無需理解并發、鎖定或如何生成SQL Update查詢,而使用“ADO游標引擎”的更新功能。
1.2???????? 用ADO Recordset提交更新的缺點
缺點就是,“緩慢的性能”和“缺乏控制”,當然,這些問題不是大的不能接受,不過,它們是明顯的缺陷。
為了能更好的了解這個問題,首先看一下:“ADO游標引擎”如何向數據庫提交更改?
在調用Recordset對象的UpdateBatch方法時,ADO游標引擎掃描Recordset,找出被修改的行,并且將每一個被修改的行中的【更改】轉化為對數據庫中相應行進行修改的SQL查詢。
這就跟,開發者自己生成Update、Insert、Delete方面的SQL語句,是類似的。(不過這里是ADO游標引擎完成的)
要觀察這一點,你可以用SQL事件查看器,監視SQL對數據的調用。如果看到“一個對【帶參數的批量查詢SQL Server sp_executesql存儲過程】的調用”,多半就是ADO游標引擎產生的提交查詢了。這個存儲過程調用的等價代碼如下:
?
DELETE FROM [Order Details] WHERE OrderID=10503 AND ProductID=14
UPDATE [Order Details] SET Quantity=40
??? WHERE OrderID=10503 AND ProductID=65 AND Quantity=20
INSERT INTO [Order Details] (OrderId,ProductId,Quantity,UnitPrice)
??? VALUES(10503,1,24,18)
?
?
?
?
當然了,這是重新檢查了代碼中最初的查詢和對Recordset所做的更改之后,才寫出來的。
如果知道了數據的來源,那么將Recordset中的更改解釋為SQL查詢是相當簡單的。
下一問題是,ADO游標引擎是如何發現這些信息的呢?
當ADO游標提取查詢的結果時,它會向數據請求額外的元數據。
為了構建如前所示的UPDATE查詢,游標引擎需要知道基表和結果表中每一列的列名,以及查詢引用的表的主鍵信息。
“用ADO Field對象的Properties集合查詢元數據”的代碼如下:
| With rs.Fields(“Quantity”) ??? Debug.Print “BaseTableName = “ & .Properties(“BaseTableName”) ??? Debug.Print “BaseColumnName= “ & .Properties(“BaseColumnName”) ??? Debug.print “KeyColumn = “ & .Properties(“KeyColumn”) End With |
?
?
?
?
?
?
由此看出,ADO游標引擎的更新功能的第一大主要缺點就是,性能。
ADO游標引擎,發出的查詢(從數據庫搜集表、列和主鍵信息等),造成了顯著的性能損失。
而且,(非常不幸)ADO沒有能力在代碼中提供元數據,因此,必須在每次打開Recordset的時候,向數據庫查詢這些信息。
ADO光標引擎是一種“黑箱”技術。它,不讓你定義自己的更新邏輯。這是第二大缺點。
它對更新邏輯只有很少甚至沒有控制(不能選擇通過存儲過程調用類提交更新)
如果不喜歡ADO游標引擎產生的更新邏輯,只能是自己來編寫代碼。
2????????? 用ADONET Command對象提交更新
ADO游標引擎是構建了帶參查詢,用來提交更新。
ADONET也可以構建具有同樣功能的參數化查詢。
ADONET的Command對象不會像ADO那樣動態。
上面的過程,我們要構建:一個Command用于更新,一個Command用于插入,一個Command用于刪除,它們的參數化查詢語句如下:
| UPDATE [Order Details] ??? SET OrderID=?,ProductID=?,Quantity=?,UnitPrice=? ??? WHERE OrderID=? AND ProductID=? AND Quantity=? AND UnitPrice=? INSERT INTO [Order Details] (OrderID,ProductID,Quantity,UnitPrice) ??? VALUES(?,?,?,?) DELETE FROM [Order Details] ??? WHERE OrderID=? AND ProductID=? AND Quantity=? AND UnitPrice=? |
?
?
?
?
?
?
?
?
注意UPDATE和INSERT查詢會為原始查詢中的每一列都向數據庫提交新值。這些查詢在其WHERE語句中引用原始查詢中的每一列。這種方式有缺點也有優點。
下面的代碼演示構建3個帶參Command對象,它們使用名為cn的OleDBConnection對象。
??? Private Function create_update_command() As OleDbCommand
??????? Dim strsql As String
??????? strsql = "update [Order Details] set OrderID=?,ProductID=?, Quantity=?,UnitPrice=? where OrderID=? and ProductID=? AND Quantity=? AND UnitPrice=?"
??????? Dim cmd As New OleDbCommand(strsql, cn)
??????? Dim pc As OleDbParameterCollection = cmd.Parameters
??????? With pc
??????????? .Add("OrderID_new", OleDbType.Integer)
??????????? .Add("ProductID_new", OleDbType.Integer)
??????????? .Add("Quantity_new", OleDbType.SmallInt)
??????????? .Add("UnitPrice_new", OleDbType.Currency)
?
??????????? .Add("OrderID_orig", OleDbType.Integer)
??????????? .Add("ProductID_orig", OleDbType.Integer)
?????? ?????.Add("Quantity_orig", OleDbType.SmallInt)
??????????? .Add("UnitPrice_orig", OleDbType.Currency)
?
??????? End With
??????? Return cmd
??? End Function
?
??? Private Function create_Insert_command() As OleDbCommand
??????? Dim strsql As String
??????? strsql = "insert into [Order Details] (OrderID,ProductID,Quantity,UnitPrice VALUES(?,?,?,?)"
??????? Dim cmd As New OleDbCommand(strsql, cn)
??????? Dim pc As OleDbParameterCollection = cmd.Parameters
??????? With pc
??????????? .Add("OrderID", OleDbType.Integer)
??????????? .Add("ProductID", OleDbType.Integer)
??????????? .Add("Quantity", OleDbType.SmallInt)
??????????? .Add("UnitPrice", OleDbType.Currency)
??????? End With
??????? Return cmd
??? End Function
?
??? Private Function create_Delete_command() As OleDbCommand
??????? Dim strsql As String
??????? strsql = "delete from [Order Details] where OrderID=? AND ProductID=? AND Quantity=? AND UnitPrice=?"
??????? Dim cmd As New OleDbCommand(strsql, cn)
??????? Dim pc As OleDbParameterCollection = cmd.Parameters
??????? With pc
??????????? .Add("OrderID", OleDbType.Integer)
??????????? .Add("ProductID", OleDbType.Integer)
??????????? .Add("Quantity", OleDbType.SmallInt)
??????????? .Add("UnitPrice", OleDbType.Currency)
??????? End With
??????? Return cmd
??? End Function
使用我們的參數化Command對象,來進行提交更新,是非常直觀的。
需要,在DataTable中檢查修改的行,并確定存儲在第一行中的修改類型(是更新、插入或者刪除),然后用行的內容構成適當的命令參數。
在調用Command對象的ExecuteNonQuery方法,執行查詢之后,可以用方法的返回值,確定更新是否成功,如果成功,就可以調用DataRow對象的AcceptChange方法;否則,可以設置DataRow對象的RowError屬性,來表明更改嘗試失敗。
‘提交過程
??????? Dim cmdUpdate As OleDbCommand = create_update_command()
??????? Dim cmdInsert As OleDbCommand = create_Insert_command()
??????? Dim cmdDelete As OleDbCommand = create_Delete_command()
??????? Dim row As DataRow
??????? Dim intRowsAffected As Integer
??????? Dim dvrs As DataViewRowState
??????? dvrs = DataViewRowState.ModifiedCurrent Or DataViewRowState.Deleted Or DataViewRowState.Added
??????? '下面取被改變的行,并遍歷,進行對應操作
??????? For Each row In tbl.Select("", "", dvrs)
??????????? Select Case row.RowState
??????????????? Case DataRowState.Modified
'submitUpdate方法用來更新一行數據
??????????????????? intRowsAffected = submitUpdate(row, cmdUpdate)
????????????? ??Case DataRowState.Deleted
??????????????????? intRowsAffected = submitDelete(row, cmdDelete)
??????????????? Case DataRowState.Added
??????????????????? intRowsAffected = submitInsert(row, cmdInsert)
??????????? End Select
??????????? If intRowsAffected = 1 Then
??????????????? row.AcceptChanges()
??????????????? MsgBox("提交成功!")
??????????? Else
??????????????? row.RowError = "Update attempt failed"
??????????? End If
??????? Next
使用DataTable對象的Select方法,用來在修改的行(通過第三個參數取得)中循環遍歷。
之所以,不“使用For或For Each循環檢查DataRow中的全體Rows集合”是因為,在成功提交“掛起刪除”或“調用DataRow的AcceptChanges方法”時,這項DataRow就從其父集合中刪除了,而由Select方法返回的DataRow對象數組,實質上包含“被修改的行的指針”,如果從DataTable的Rows集合刪除項,代碼仍然會成功執行。
下面是應用代碼:
?
‘插入行,并提交的演示
??????? tbl.Rows.Add(New Object() {10248, 70, 100, 10.5})
??????? SubmitChangesByHand()
‘修改行并提交的演示
??????? tbl.PrimaryKey = New DataColumn() {tbl.Columns("OrderID"), tbl.Columns("ProductID")}
??????? Dim row As DataRow = tbl.Rows.Find(New Object() {10248, 70})
??????? row("UnitPrice") = row("UnitPrice") * 2
??????? SubmitChangesByHand()
‘刪除行并提交的演示
??????? tbl.PrimaryKey = New DataColumn() {tbl.Columns("OrderID"), tbl.Columns("ProductID")}
??????? Dim row As DataRow = tbl.Rows.Find(New Object() {10248, 70})
??????? row.Delete()
??????? SubmitChangesByHand()
?
前面的參數化Command對象是專門針對初識查詢的。
不過SubmitChangesByHand中的代碼是具有一般性的。
實際上,上面只是手動構建了DataAdapter對象所提供的更新功能。
3????????? 使用ADONET DataAdapter對象提交更新
DataAdapter對象,可以將查詢的結果存儲到DataTable中;還可以,向數據庫提交DataSet掛起的更改。
在生成DataAdapter,用于向數據庫提交更改的更新邏輯時,有三種選擇:
n?用代碼手工配置DataAdapter對象
n?在運行時,使用CommandBuilder對象。
n?在設計時,使用“數據適配器配置向導”。
4????????? 手工配置DataAdapter對象
DataAdapter對象公開有,4個包含Command的屬性。分別是:
n?SelectCommand屬性
用來填充數據
n?UpdateCommand屬性
用來提交數據修改
n?InsertCommand屬性
用來提交插入
n?DeleteCommand屬性
用來提交刪除
這種體系結構,代表了與ADO對象模型技術相比,主要的更改。這里沒有“黑箱”技術,因為DataAdapter中的Command對象都得開發者提供,所以開發者可以控制如何提交掛起的更改。
DataAdapter對象的Update方法是非常靈活的。提供給它的參數可以是:
u?一個DataSet
u?一個DataSet和一個表名
u?一個DataTable
u?一個DataRow數組對象
不管如何調用Update方法(指使用不同的重載版本),DataAdapter都將嘗試通過正確的Command提交掛起更改。
前面代碼中的SubmitChangesByHand過程中的所有工作,都可以通過調用DataAdapter.Update方法一次完成。
4.1???????? 綁定參數-簡介
SubmitChangesByHand過程并不復雜,它把繁瑣的工作交給了下面的三個子函數。
這些函數根據此行被修改數據的類型給出相應查詢的參數值。
下面,我們將使用DataAdapter對象,以同樣的參數化查詢方式,提交掛起的更改。
當向“DataAdapter對象的Command對象添加Parameter對象”時,這里要使用專門為DataAdapter更新而設計的ADONET Parameter對象的特殊屬性:SourceColumn和SourceVersion。
上述屬性,基本上就是將Parameter綁定到DataTable中的DataColumn上。
DataAdapter在執行查詢前,使用這些屬性,“確定如何填充Parameter對象的Value屬性”,這與我們在上面代碼中,用代碼來完成的功能是類似的。
n?代碼演示:下面創建一個參數化Command對象,設置該對象的SourceColumn和SourceVersion屬性。SourceVersion屬性的默認值是DataRowVersion.Current,所以如果需要將Parameter對象綁定到行的原始值,需要設置這個屬性。
??? Private Function CreateDataAdapterUpdateCommand() As OleDbCommand
??????? Dim strSql As String = "update [Order Details] set OrderID=?,ProductID=?,Quantity=?,UnitPrice=? where OrderID=? AND ProductID=? AND Quantity=? AND UnitPrice=?"
??????? Dim cmd As New OleDbCommand(strSql, conn)
??????? Dim pc As OleDbParameterCollection = cmd.Parameters
??????? pc.Add("OrderID_New", OleDbType.Integer, 0, "OrderID")
??????? pc.Add("ProductID_New", OleDbType.Integer, 0, "ProductID")
??????? pc.Add("Quantity_New", OleDbType.SmallInt, 0, "Quantity")
??????? pc.Add("UnitPrice", OleDbType.Currency, 0, "UnitPrice")
?
??????? Dim p As OleDbParameter
??????? p = pc.Add("Order_Orig", OleDbType.Integer, 0, "OrderID")
??????? p.SourceVersion = DataRowVersion.Original
??????? p = pc.Add("Product_Orig", OleDbType.Integer, 0, "ProductID")
??????? p.SourceVersion = DataRowVersion.Original
????? ??p = pc.Add("Quantity_Orig", OleDbType.SmallInt, 0, "Quantity")
??????? p.SourceVersion = DataRowVersion.Original
??????? p = pc.Add("UnitPrice_Orig", OleDbType.Currency, 0, "UnitPrice")
??????? p.SourceVersion = DataRowVersion.Original
??????? Return cmd
??? End Function
??? Private Function CreateDataAdapterInsertCommand() As OleDbCommand
??????? Dim str As String = "insert into [Order Details] (OrderID,ProductID,Quantity,UnitPrice) VALUES(?,?,?,?)"
??????? Dim cmd As New OleDbCommand(str, conn)
??????? Dim pc As OleDbParameterCollection = cmd.Parameters
??????? pc.Add("OrderID", OleDbType.Integer, 0, "OrderID")
??????? pc.Add("ProductID", OleDbType.Integer, 0, "ProductID")
??????? pc.Add("Quantity", OleDbType.SmallInt, 0, "Quantity")
??????? pc.Add("UnitPrice", OleDbType.Currency, 0, "UnitPrice")
??????? Return cmd
??? End Function
??? Private Function CreateDataAdapterDeleteCommand() As OleDbCommand
??????? Dim strSql As String
??????? strSql = "delete from [Order Details] where OrderID=? AND ProductID=? AND Quantity=? AND UnitPrice=?"
??????? Dim cmd As New OleDbCommand(strSql, conn)
??????? Dim pc As OleDbParameterCollection = cmd.Parameters
??????? Dim p As OleDbParameter
??????? p = pc.Add("OrderID", OleDbType.Integer, 0, "OrderID")
??????? p.SourceVersion = DataRowVersion.Original
??????? p = pc.Add("ProductID", OleDbType.Integer, 0, "ProductID")
??????? p.SourceVersion = DataRowVersion.Original
??????? p = pc.Add("Quantity", OleDbType.SmallInt, 0, "Quantity")
??????? p.SourceVersion = DataRowVersion.Original
??????? p = pc.Add("UnitPrice", OleDbType.Currency, 0, "UnitPrice")
??????? p.SourceVersion = DataRowVersion.Original
??????? Return cmd
??? End Function
提交的過程就很簡單了
??????? da.InsertCommand = CreateDataAdapterInsertCommand()
??????? da.UpdateCommand = CreateDataAdapterUpdateCommand()
??????? da.DeleteCommand = CreateDataAdapterDeleteCommand()
??????? da.Update(ds.Tables("Order Details"))
4.2???????? 用存儲過程提交更新
使用ADO,從數據中提交數據的過程中,無法通過Recordset對象的UpdataBatch方法,用存儲過程提交更新。
但是,在ADONET對象架構中,“構建自己的Command,然后用DataAdapter來提交掛起的更新”這個過程中可以用存儲過程來提交。
首先,需要定義存儲過程,比如在Sql Server的Northwind數據庫中定義一個給Orderdetails表修改、插入、刪除行的存儲過程。(用SQL查詢分析器或調用名為CreateSprocs的過程)
USE Northwind
GO
CREATE PROCEDURE spUpdateDetails
(@OrderID_New int, @ProductID_New int,
@Quantity_New smallint,@UnitPrice_New money,
@OrderID_Orig int,@ProductID_Orig int,
@Quantity_Orig smallint,@UnitPrice_Orig money)
AS
UPDATE [Order Details]
SET OrderID=@OrderID_New,ProductID=@ProductID_New,
Quantity=@Quantity_New,UnitPrice=@UnitPrice_New
WHERE OrderID=@OrderID_Orig AND ProductID=@ProductID_Orig
?AND Quantity=@Quantity_Orig AND UnitPrice=@UnitPrice_Orig
?
GO
CREATE PROCEDURE spInsertDetail(@OrderID int,@ProductID int,@Quantity smallint,
@UnitPrice money)
AS
INSERT INTO [Order Details]
???? (OrderID,ProductID,Quantity,UnitPrice)
???? VALUES ( @OrderID,@ProductID,@Quantity,@UnitPrice)
GO
CREATE PROCEDURE spDeleteDetail
??????????? (@OrderID int,@ProductID int,@Quantity smallint,@UnitPrice money)
AS
DELETE FROM [Order Details]
???? WHERE OrderID=@OrderID AND ProductID=@ProductID AND Quantity=@Quantity AND
?????????????????? UnitPrice = @UnitPrice
將輸入的存儲過程提交給SqlServer,下面編寫Command對象以便在調用DataAdapter對象的Update方法時,自動調用這些存儲過程。
Private Sub SubmitChangesViaStoredProcedures()
Da.UpdateCommand=CreateUpdateViaSPCommand()
Da.InsertCommand=CreateInsertViaSPCommand()
Da.DeleteCommand=CreateDeleteViaSPCommand()
Da.Update(tbl)
End Sub
Private Function CreateUpdateViaSPCommand() As OleDbCommand
Dim cmd As New OleDbCommand(“spUpdateDetail”,cn)
cmd.CommandType=CommandType.StoredProcedure
Dim pc as OleDbParameterCollection=cmd.Parameters
Pc.Add(“OrderID_New”,OledbType.Integer,0,”OrderID”)
Pc.Add(“ProductID_New”,OleDbType.Integer,0,”ProductID”)
Pc.Add(“Quantity_New”,OleDbType.SmallInt,0,”Quantity”)
Pc.Add(“UnitPrice_New”,OleDbType.Currency,0,”UnitPrice”)
Dim p As OleDbParameter
P=pc.Add(“OrderID_Orig”,OleDbType.Integer,0,”OrderID”)
p.SourceVersion=DataRowVersion.Original
p=pc.Add(“ProductID_Orig”,OleDbType.Integer,0,”ProductID”)
p.SourceVersion=DataRowVersion.Original
p=pc.Add(‘Quantity_Orig”,OleDbType.SmallInt,0,”Quantity”)
p.SourceVersion=DataRowVersion.Original
p=pc.Add(“UnitPrice_Orig",OleDbType.Currency,0,”UnitPrice”)
p.SourceVersion=DataRowVersion.Original
Return cmd
End Function
Private Function CreateInsertViaSPCommand() AS OleDbCommand
Dim cmd as New OleDbCommand(“spInsertDetail”,cn)
cmd.CommandType=CommandType.StoredProcedure
Dim pc as OleDbParameterCollection=cmd.Parameters
Pc.Add(“OrderID”,OleDbType.Integer,0,”OrderID”)
Pc.Add(“ProductID”,OleDbType.Integer,0,”ProductID”)
Pc.Add(“Quantity”,OleDbType.SmallInt,0,”Quantity”)
Pc.Add(“UnitPrice”,OleDbType.Currency,0,”UnitPrice”)
Return cmd
End Function
Private Function CreateDeleteViaSPCommand() As OleDbCommand
Dim cmd as New OleDbCommand(“spDeleteDetail”,cn)
cmd.CommandType=CommandType.StoreProcedure
Dim pc As OleDbParameterCollection=cmd.Parameters
Pc.Add(“OrderID”,OleDbType.Integer,0,”OrderID”)
Pc.Add(“ProductID”,OleDbType.Integer,0,”ProductUD”)
Pc.Add(“Quantity”,OleDbType.SmallInt,0,”Quantity”)
Pc.Add(“UnitPrice”,OleDbType.Currency,0,”UnitPrice”)
Return cmd
End Function
下面展示,用代碼創建存儲過程:
Private Sub CreateSprocs()
Dim cmd As OleDbCommand=cn.CreateCommand
Dim strSql as String
strSql=”create procedure spUpdateDetail ” & vbCrLf &_
“ (@OrderID_New int, @ProductID_New int, “ & vbCrLf & _
“@Quantity_New SmallInt, “ & vbCrLf &_
“@UnitPrice_New money, “ & vbCrLf &_
“@OrderID_Orig int, “ & vbCrLf &_
“@ProductID_Orig int, “ & vbCrLf &_
“@Quantity_Orig smallint, “ & vbCrLf &_
“@UnitPrice_Orig money) “ & vbCrLf &_
“AS “ & vbCrLf &_
“UPDATE [Order Details] “ & vbCrLf &_
“ SET OrderID=@OrderID_New, “ & vbCrLf &_
“ ProductID=@ProductID_New, “ & vbCrLf &_
“ Quantity=@Quantity_New, “ & vbCrLf &_
“ UnitPrice=@UnitPrice_New “ & vbCrLf &_
“ WHERE OrderID=@OrderID_Orig AND “ & vbCrLf &_
“ ProductID=@ProductID_Orig AND “ & vbCrLf &_
“ Quantity=@Quantity_Orig AND “ & vbCrLf &_
“ UnitPrice=@UnitPrice_Orig“
cmd.CommandText=strSql
cmd.ExecuteNonQuery()
?
strSql=”CREATE PROCEDURE spInsertDetail “ & vbCrLf & _
????????????? “ (@OrderID int,@ProductID int, @Quantity smallint,@UnitPrice money) “ & vbCrLf & _
????????????? “AS “ & vbCrLf & _
????????????? “ INSERT INTO [Order Details] “ & vbCrLf & _
????????????? “ (OrderID,ProductID,Quantity,UnitPrice) “ &vbCrLf & _
????????????? “ VALUES (@OrderID,@ProductID,@Quantity,@UnitPrice) ”
cmd.CommandText=strSql
cmd.ExecuteNonQuery()
strSql=”CREATE PROCEDURE spDeleteDetail “ & vbCrLf &_
????????????? “ (@OrderID int,@ProductID int, “ & vbCrLf &_
????????????? “ @Quantity smallint,@UnitPrice money) “ & vbCrLf &_
????????????? “ AS “ & vbCrLf &_
????????????? “ DELETE FROM [Order Details] “& vbCrLf &_
“ WHERE OrderID=@OrderID AND “ & vbCrLf &_
“ ProductID=@ProductID AND “ & vbCrLf &_
“ Quantity=@Quantity AND UnitPrice=@UnitPrice”
??????????? cmd.CommandText=strSql
??????????? cmd.ExecuteNonQuery()
End Sub
4.3???????? 提供自己的更新邏輯
比較:“在代碼中——提供自己的更新邏輯的【優點】和【缺點】“
優點:
?????? 最大的兩個優點是:控制和性能。
?????? ADONET的DataAdapter對象為開發者提供了比任何的Microsoft數據訪問技術,更多的對更新邏輯的控制。從此不再局限于直接向表提交更新,開發者可以最終以RAD方式利用存儲過程。
?????? 此外,因為不再依賴數據訪問技術確定數據來源,所以可以認為所有的結果集都是可以更新的。
相對的,通過ADO游標引擎訪問數據的時候,如果游標引擎不能收集到向數據庫返回提交更改所需的元數據,那么就沒辦法在程序中提供這種信息。而使用ADONET技術,可以利用存儲過程調用、對臨時表的查詢或聯合的多查詢的結果填充DataSet-或任何其他您認為合適的方式填充它——并且仍然可以向數據庫提交更改。
在代碼中提交更新邏輯可以改進應用程序的性能。使用ADO游標引擎提交更新的代碼段包含的代碼行數更少,但它需要ADO游標引擎查詢數據以得到源表名、源列名和源表的主鍵信息。查詢數據庫系統表以獲得元數據,再用這些元數據產生更新邏輯要比只是從本地代碼中加載它要花費更多時間。
缺點
提供自己的更新邏輯的缺點,與ADO游標引擎方法的優點一樣。
首先,提供自己的更新邏輯,需要大量代碼。編寫這些代碼是很花時間的,并且相當繁瑣。
另外,很多人不習慣編寫自己的更新邏輯。比如:需要在查詢中分隔表名?應該使用什么樣的參數類型標記?哪一列應該出現在UpdateCommand和DeleteCommand的CommandText的WHERE語句中?對于包含日期、時間值的參數,正確的OleDbType設置是什么?
5????????? 使用CommandBuilder對象生成更新邏輯
ADONET對象模型不只讓你定義自己的更新邏輯,而且還用CommandBuilder對象提供了與ADO游標引擎類似的動態更新邏輯產生機制。
如果,實例化一個CommandBuilder對象,并將它與,一個DataAdapter對象相關聯,那么CommandBuilder將嘗試根據在DataAdapter對象的SelectCommand中包含的查詢,來生成更新邏輯。
為了演示CommandBuilder如何工作?下面用它類為查詢訂單明細表的示例代碼產生更新邏輯。
Imports System.Data
Imports System.Data.OleDb
Public Class Form1
??? Dim da As OleDbDataAdapter
??? Dim cb As OleDbCommandBuilder
??? Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
??????? Dim str_sql As String = "select OrderID,ProductID,Quantity,UnitPrice from [Order Details] where OrderID=10503 order by ProductID"
??????? da = New OleDbDataAdapter(str_sql, conn)
??????? cb = New OleDbCommandBuilder(da)
?
??????? Label1.Text = cb.GetInsertCommand.CommandText
??????? Label2.Text = cb.GetDeleteCommand.CommandText
??????? Label3.Text = cb.GetUpdateCommand.CommandText
??? End Sub
End Class
運行的結果就是,能夠顯示由CommandBuilder對象產生的,插入、刪除、更新語句。
生成的CommandText語句,看起來與前面構建的提交查詢很類似。
5.1???????? CommandBuilder如何生成更新邏輯
CommandBuilder用于生成Update、Insert、Delete查詢的邏輯。
生成過程并不復雜,與ADO游標引擎類似。
CommandBuilder會查詢數據庫,以確定查詢的結果的基表、列名以及關鍵信息。
CommandBuilder自動生成更新邏輯,需要滿足以下條件:
u?查詢只返回一個表中的數據
u?這個表有一個主鍵
u?主鍵包括在查詢的結果中。
主鍵,確保CommandBuilder所生成的基于更新查詢,最多只能更新一行。
CommandBuilder對象使用DataAdapter對象的SelectCommand屬性,獲取更新邏輯所需要的元數據。Command對象的ExecuteReader方法可以連同查詢結果一起請求這種類型的元數據。
??????? Dim str_sql As String = "select OrderID,ProductID,Quantity,UnitPrice from [Order Details] where OrderID=10503 order by ProductID"
??????? Dim cmd As New OleDbCommand(str_sql, conn)
??????? conn.Open()
??????? Dim rdr As OleDbDataReader
??????? rdr = cmd.ExecuteReader(CommandBehavior.SchemaOnly Or CommandBehavior.KeyInfo)
??????? Dim tbl As DataTable = rdr.GetSchemaTable
??????? rdr.Close()
??????? conn.Close()
?
??????? Dim r As DataRow
??????? Dim c As DataColumn
??????? For Each r In tbl.Rows
??????????? For Each c In tbl.Columns
??????????????? ListBox1.Items.Add(c.ColumnName & " - " & r(c).ToString)
??????????? Next
??????????? ListBox1.Items.Add("")
??????? Next
運行上面的代碼,你會看到,CommandBuilder為了生成更新邏輯,需要取得的所有數據。
列名、基表和列的基列名、列是否是基表的主鍵的一部分、列是否包含一個長的數據類型(大文本或二進制)、浮點列的范圍和精度,等等。
5.2???????? 使用CommandBuilder的優點和缺點
將“由CommandBuilder生成的代碼”與“自己產生更新邏輯的代碼”進行比較,發現“使用CommandBuilder對象”有兩個主要的優點。
u?使用CommandBuilder對象需要的代碼更少。
u?在生成更新邏輯時,不需要對SQL的Update、Insert和Delete查詢語法有深入的了解。
如果“在生成自己的更新邏輯”時遇到問題,CommandBuilder也可以提供幫助。——先利用CommandBuilder成功的生成更新邏輯,通過檢查其Command對象的CommandText屬性的值或其所構建的Parameter對象的不同屬性,重新設計自己的更新邏輯。
在所有需要支持更新但是,設計時不知道查詢結構的應用程序中,CommandBuilder都是很有用的。
與ADO游標引擎一樣,CommandBuilder在運行時,自動生成更新邏輯。
因此,CommandBuilder也具有與ADO游標引擎的同樣的問題和局限性。
CommandBuilder不能提供最好的運行時性能。解決辦法是,可以在代碼中提供自己的更新邏輯,這比CommandBuilder請求并處理生成類似更新邏輯所需要的時間少的多。
CommandBuilder不提供對“它自己產生的語句”進行控制。
不能指定想要使用的開放式并發的類型。
CommandBuilder也不能使用存儲過程提交更新。
6????????? 使用【數據適配器配置向導】生成更新邏輯
【數據適配器配置向導】可以在,設計時快速、高效地,產生更新邏輯。
6.1???????? 檢查DataAdapter的結構
舉例說明:加入新的DataAdpater組件之后,進入屬性窗口,深入其DeleteCommand屬性,現在CommandText屬性,單擊右邊的按鈕,可以出現相應的【查詢生成器】。
6.2???????? 構建更新邏輯的選擇
【向導】的【生成SQL語句】窗口,有一個【高級選項】按鈕,單擊它顯示【高級SQL生成選項】對話框。
其中的選項有三個:
u?生成Insert、Update和Delete語句
不生成此三種語句(只用到提取數據功能),能夠節省一些設計和運行的時間
u?使用開放式并發
在默認情況下,向導將所有非BLOB列添加到提交掛起更新和刪除的查詢的WHERE語句,如果取消選擇【使用開放式并發】,向導在查詢的WHERE語句中只包括主鍵列。
u?刷新數據庫
一些數據庫,如SQL Server,支持可以返回幾行數據的成批查詢。如果使用【向導】構建與此種數據庫通訊的DataAdapter,那么【刷新數據集】是啟用并選中的。選中此項,向導會產生,在提交改變后立即提取修改的行的內容的查詢,這樣新的服務器產生的值,如時間戳和自動遞增值,將會在調用DataAdapter.Update之后出現在DataSet中。
6.3???????? 使用存儲過程提交更新
【向導】還可以幫助構建用存儲過程向Sql Server數據庫提交更新的DataAdapter對象。
在向導的【選擇查詢類型】頁面,有【使用現有存儲過程】選項。選中后,下一步可以為每一個Command對象選擇存儲過程。
注意:向導沒有提供設置Parameter對象的SourceVersion屬性的選項。因此,此屬性默認值為Current。
企業版的Visual Studio.NET提供SQL查詢,在【向導】中會為DataAdapter對象的各個Command產生新的存儲過程。(在【向導】的【選中查詢類型】頁面選【創建新存儲過程】)
6.4???????? 使用向導的優缺點
向導比CommandBuilder提供更多選項,還能生成開發者不愿編寫的繁瑣代碼。
盡管“向導產生更新邏輯,需要數據庫的架構信息”與“CommandBuilder”一樣,但它只在設計時請求一次信息,然后將新邏輯保存在代碼中。因此,應用程序避免了使用CommandBuilder所帶來的性能損失。
向導只提供了,OLEDB和SQL客戶端.NET數據提供程序。
7????????? 關于更新的其他注意事項
你需要了解更多的知識:比如,如何處理并發,以便不會意外覆蓋另一位用戶的更改?如何處理并發檢查中的空值?在提交更新時,DataAdapter的TableMappings集合起什么作用?
7.1???????? 開放式并發選項
“構建使用開放式并發向數據庫提交更新的多用戶數據庫程序”時,開放式并發檢查,很重要。
假設,兩個用戶請求同一行數據,然后都嘗試更新同一行數據,接下來會發生什么?(這取決于開發者如何構造更新查詢)
在SQL更新查詢中有四種基本的開放式并發選項。
7.1.1?? 只包含主鍵列
在SQL的Update和Delete查詢中,可以只包括【主鍵列】,這會創建出“后來居上”的更新方案。
上述,兩個更新嘗試都會成功。并且,最后一個更新會覆蓋前一個更改。
方案的簡單過程如下:
?????????? 用戶A提取一行
?????????? 用戶B提取一行
?????????? 用戶B修改此行并提交更改
?????????? 用戶A修改此行,并提交更改,覆蓋了B的更改。
用戶A甚至意識不到數據庫這一行的內容,在最初查詢和用戶提交更改這段時間,已經發生改變。
CommandBuilder對象不提供這種開放式并發選擇
【數據適配器配置向導】則提供。在【高級選項】中,取消選擇【使用開放式并發】
7.1.2?? 在WHERE語句中包括所有列
CommandBuilder和向導的默認操作,都是在WHERE語句中包括:所有列。
使用這種邏輯,即可防止覆蓋在代碼獲取行期間(或者代碼嘗試向數據庫提交對這一行所做的掛起改變)內,另一個用戶所做的更改。
舉個例子:假設用戶A和用戶B獲取同一行客戶數據。
用戶B對ContactName列做了改變并提交了更改。應用程序在基于查詢的更新中的WHERE語句中包括所有列,所以Update查詢形式如下:
UPDATE Customers
SET CustomerID=’ABCDE’,CompanyName=’Original Company Name’,
?????????? ContactName=’New Contact’,Phone=’800-555-1212’
WHERE CustomerID=’ABCDE’ AND
??? CompanyName=’Original Comany Name’ AND
??? ContactName=’Original Contact’ AND
??? Phone=’800-555-1212’
與此同時,用戶A修改了同一行客戶數據,改變了CompanyName列的值。
因為用戶A在用戶B提交之前取得的數據,因此更新語句如下:
UPDATE Customers
SET CustomerID=’ABCDE’,CompanyName=’New Company Name’,
?????????? ContactName=’Original Contact’,Phone=’800-555-1212’
WHERE CustomerID=’ABCDE’ AND
??? CompanyName=’Original Comany Name’ AND
??? ContactName=’Original Contact’ AND
??? Phone=’800-555-1212’
因為,A提交時數據庫的ContactName值已經改變,表中沒有能夠滿足WHERE語句的數據,因此,數據庫不會修改任何行。DataAdapter查詢數據(確定被修改行數),發現沒有成功修改行,就相應標記DataRow。
?
這是CommandBuilder對象使用的并發選擇。
適配器向導默認會啟用這種并發選擇。
注意:一般的,數據庫不會讓你執行對兩個BLOB值的比較操作。
?????????? 因為在BLOB列中可以存儲許多兆字節的數據,對它們的比較即使可行,也非常低效。
?????????? 代碼生成工具(CommandBuilder和適配器向導)不會在基于查詢的更新語句的?WHERE部分包括BLOB列。
7.1.3?? 包括主鍵和時間戳列
使用【時間戳】列,可以簡化查詢更新的WHERE語句。
SQL SERVER時間戳列,并不真正包含日期和時間信息。它只包含數據庫中惟一的二進制數據。
定義SQL SERVER表上的時間戳列,在任何行的內容發生改變之時,SQL SERVER都會修改此行的時間戳值。
向Customers表中添加時間戳列,并修改查詢如下:
UPDATE Customers
SET CustomerID=’ABCDE’,CompanyName=’Original Company Name’,
?????????? ContactName=’New Contact’,Phone=’800-555-1212’
WHERE CustomerID=’ABCDE’ AND TimestampColumn=0x00000000000CC
因為服務器在【每次更新一行時都會為時間戳產生新值】,因此,在更新語句的WHERE子句中使用【主鍵和時間戳列】的組合,可以保證不會覆蓋另一位用戶所做的更改。
大多數數據庫系統都支持【類似時間戳】的數據類型。它們,有的使用惟一的二進制值,有的使用日期/時間值。可以通過【檢查數據庫系統的文檔】來確定后端的數據類型,并了解如何迫使數據庫在每次修改一行的內容時更新這個值。
目前,CommandBuilder和適配器配置向導,都【不支持】生成使用這種開放式并發策略的更新邏輯。
注意:SQL Server中,rowversion和時間戳數據類型是同義的。Sql Server文檔建議你用rowversion關鍵詞代替時間戳。
使用【主鍵+時間戳】可以得到簡單的更新邏輯,并且每次嘗試更新時數據庫需要檢查的列更少。
7.1.4?? 包括主鍵和被修改的列
默認情況下,ADO游標引擎在查詢語句中的WHERE部分只包括【主鍵列和修改列的原始值】;在Update語句的SET部分也只包括修改的列。
在使用ADO游標引擎進行此種更新時多用戶例子如下:假設用戶A用戶B同時取得同一行客戶數據,他們修改了不同行的數據——用戶A改變了CompanyName列,用戶B改變了ContactName列。用戶B首先提交了對ContactName列的掛起更改,用戶B的Update語句如下:
UPDATE Customers
SET???? ContactName=’New Contact’
WHERE CustomerID=’ABCDE’ AND ContactName=’Original Contact’
然后用戶A用下面的Update語句提交更新:
UPDATE Customers
SET CompanyName=’New Company Name’
WHERE CustomerID=’ABCDE’ AND
?CompanyName=’Original Company Name’
數據庫執行效果:
公司名=‘新公司名’;聯系人姓名=‘新聯系人’
?
兩個更新都成功了,并且由用戶A的更新沒有覆蓋用戶B的更新。
ADONET DataAdapter的結構,不會依靠這種更新策略,因為,它需要根據被修改的列來改變查詢的結構,DataAdapter提供了以行為基礎的更新語句參數值,但是它不會修改參數查詢的實際結構。
理論上,可以編寫代碼更改相應Command對象結構的代碼,并在處理DataAdapter對象的RowUpdating事件時使用這段代碼。【這種更新策略有其好處,但是代價過高,超過優點】
7.2???????? 使用空值
Northwind數據庫中的Customers表包含了一個Region列,可以接受15個字符的字符串,也可以接受空值(Null)。
一般開發者都會嘗試用下面的查詢取得空值的行:
Select CustomerID,CompanyName,ContactName,Phone from Customers where Region=NULL
如果在ADONET中使用此種查詢,或者在SQL查詢分析器中運行它,則將返回零行。
在“數據庫世界”中,空值是一種特殊情況,特別是當在查詢中比較空值的時候,根據ANSI標準,不能使用等號運算符“=”來比較空值,必須使用IS NULL。
上面的語句需要寫成:
Select CustomerID,CompanyName,ContactName,Phone from Customers where Region IS NULL
空值與用DataAdapter向數據庫提交更新有什么關系呢???
在前面,用于提交Order Details表中被修改行的Command Text是這樣:
UPDATE [Order Details]
???? SET OrderID=?,ProductID=?,Quantity=?,UnitPrice=?
??????????? WHERE OrderID=? AND ProductID=? AND Quantity=? AND UnitPrice=?
如果查詢中有引用列接受空值,會怎樣?假設,有一行的Quantity列是空值,現在希望改為20。那么,實際的查詢語句如下
UPDATE [Order Details]
???? SET OrderID=12345, ProductID=1, Quantity=20, UnitPrice=18
??????????? WHERE OrderID=12345 AND ProductID=1 AND Quantity=Null AND UnitPrice=18
這時,由于WHERE語句中的Quantity=Null,查詢會修改零行。(因為,數據庫所需行的Quantity列為空,但是Null=Null值為False,因此,數據庫不會修改行)
怎么讓WHERE語句能夠適應【并發檢查中的空值】?如果是特定的列接受空值,可以這樣修改:
將ColumnName=?
修改為
(ColumnName=?OR ((ColumnName IS NULL) AND (? IS NULL))
這就使得“當列和參數是相等的非空值”或者“它們都是空值”時,語句的值為True。
如果使用【適配器配置向導】構建更新邏輯,假設ContactName和Phone列可接受空值,你會發現向導產生的查詢是帶有相應的空值檢查的。
總結:
???? 【數據適配器配置向導】在“產生更新邏輯”方面做的很好。即使你要生成自己的邏輯,看一下向導產生的代碼對你的工作也有雙重檢查的好處。
7.3???????? 在事務中提交更新
將更新封裝到一個事務中,可以使更新作為整體提交。
但是,DataAdapter未公開Transaction屬性。
實際上,DataAdapter并不提交更新,它只是將工作上交給UpdateCommand、InsertCommand和DeleteCommand屬性對應的Command對象。Command對象公開了Transaction屬性。
用DataAdapter提交帶事務的更新,必須設置對應Command對象的Transaction屬性。
‘設置時
‘創建新事務
Dim txn As OleDbTransaction=cn.BeginTransaction()
‘設置DataAdapter的Command的Transaction屬性
Da.UpdateCommand.Transaction=txn
Da.InsertCommand.Transaction=txn
Da.DeleteCommand.Transaction=txn
‘使用時
try
‘提交更改
Da.Update(tbl)
‘接受更新并關閉連接
Txn.Commit()
Cn.Close()
Catch e as exception
?
End try
使用CommandBuilder對象生成更新邏輯,由于CommandBuilder在實例化的時候,并沒有實際地產生更新語句,再以事務方式提交就比較困難。
在調用DataAdapter.Update的時候,CommandBuilder對象將使用DA的SelectCommand取得數據庫的元數據,如果SelectCommand沒有跟事務對象關聯,CommandBuilder會引發異常。
解決辦法就是給SelectCommand也關聯事務對象:
Da.SelectCommand.Transaction=txn
不過,這樣又使得CommandBuilder提取數據庫架構信息也在事務中進行了,一般來說,我們會希望事務【盡可能少的】接觸數據庫中的數據。怎么辦?
更恰當的選擇就是,迫使CommandBuilder在開始事務之前就生成更新邏輯,比如,可以先通過調用CommandBuilder的GetUpdateCommand方法,取得Update語句邏輯。
7.4???????? 使用TableMapping集合
DataAdapter的TableMapping集合可以影響其Fill方法填充DataSet的過程。
如下:
Dim strConn,strSql As String
strConn=””
strSql=”select OrderID,ProductID,Quantity,UnitPrice from [Order Details] where OrderID=10503”
Dim da as New OleDbDataAdapter(strSql,strConn)
Dim da as new DataSet()
‘注意這里
Da.TableMappings.Add(“Table”,”Order Details”)
Da.Fill(ds) ’填充的表名直接被指定為Table
在提交更新時,TableMappings集合有類似上面的效果。
如果在DataAdapter對象的Update方法中只提供一個DataSet對象,那么DataAdapter就會依靠其TableMappings集合來確定究竟要檢查DataSet中的哪一個DataTable。
‘前面略
...
Dim da as new OleDbDataAdapter(strSql,strConn)
Da.TableMappings.Add(“Table”,”OrderDetails”)
‘定義更新邏輯
Dim ds as new DataSet()
Da.Fill(ds)
‘此處修改一些數據,略
Da.Update(ds)
如果沒有填充DataAdapter對象的TableMappings集合,那么你就必須使用【可接受DataSet和一個表名稱】的Update方法,或者使用可接受數據表DataTable對象的Update方法。
‘略
Dim da as new OleDbDataAdapter(strSql,strConn)
‘定義更新邏輯
Dim ds as new DataSet()
Da.Fill(ds,”Order Details”)
‘修改內容,略
Da.Update(ds,”Order Details”)
‘第二種
...
Dim da As New OleDbDataAdapter(strSql,strConn)
‘定義更新邏輯
Dim tbl as new DataTable()
Da.Fill(tbl)
‘修改內容,略
Da.Update(tbl)
有一條【基本原則】:“你應該使用相同的邏輯,來控制DataAdapter.Fill和DataAdapter.Update中,所引用的DataTable(數據表)”
7.5???????? 最佳更新方式
ADO.NET為開發者提供了許多提交更改的選擇,
可以在運行時使用CommandBuilder來生成更新邏輯
可以在代碼中提供自己的更新邏輯,通過Insert、Update、Delete查詢或存儲過程調用提交更改。
可以在設計時使用【數據適配器配置向導】
哪一種選擇更適合你呢?
答案,取決于,應用程序的參數。
你或許可以,通過配置DataAdapter對象,為通過存儲過程調用提交更新,得到最佳性能。不過,如果必須使用Access這樣不支持存儲過程的數據庫,這種方案就不適用了。這時,你最好使用Insert、Update、Delete查詢。
一般而言,建議在可能的情況下,通過存儲過程來提交更新。
但是,如果使用多種后端數據庫,則應該使用基于查詢的更新。
不管選擇哪一種方式,都要生成自己的更新邏輯,注意【避免在運行時生成更新邏輯】。
還有,要記住的一點是,除非絕對必要,否則不要在應用程序里使用CommandBuilder對象。
?
轉載于:https://www.cnblogs.com/lizunicon/archive/2008/12/18/1357282.html
總結
以上是生活随笔為你收集整理的数据库-ADONET-向数据库提交更新的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: COM原理与应用之COM的实现
- 下一篇: linux cmake编译源码,linu