【转】 ADO.NET最佳实践
本文轉自:http://blog.csdn.net/spidertan/archive/2003/12/13/17110.aspx????
??? 概述:
??? 本文在微軟站點資源的基礎上加工整理而成,意在介紹在你的ADO.NET應用程序中執行和完成性能優化、穩定性和功能性方面提供最佳的解決方案;同時也包含在ADO.NET中運用已有的數據對象進行開發的最佳實踐和幫助你怎樣設計ADO.NET應用程序提供建議。
??? 本文包含以下內容:
??? 1..NET框架中的data providers;
??? 2.對照DataSet和DataReader,分別介紹他們的最佳用途;
??? 3.如何使用DataSet、Commands和Connections;
??? 4.結合XML;
??? 5.如果你是ADO程序員,也不妨看看ADO.NET與ADO的區別和聯系;
??? 6.結合一些FAQ,更深一步討論ADO.NET觀點和使用技巧。
??? 介紹:
??? A..NET框架中的data providers
??????? Data providers在應用程序和數據庫之間扮演一個橋梁的角色,它使得你可以從一個數據庫返回查詢結果、執行命令以及對數據集的更新等。
??? B.幾種data provider的介紹
??????? 下面表格中數據表明各種data provider以及最佳適用數據庫對象
| 提供者 | 描述 |
| SQL Server.NET Data Provider | 在.NET框架中使用System.Data.SqlClient命名空間; 建議在中間層應用程序中使用SQL Server7.0或以后版本; 建議在獨立的應用程序中使用MSDE或SQL Server7.0或更高版本; SQL Server6.5或更早版本,必須使用OLE DB.NET Data Provider中的OLE DB Provider For SQL Server。 |
| OLE DB.NET Data Provider | 在.NET框架中使用System.Data.OleDb命名空間; 建議在中間層應用程序中使用SQL Server6.5或以前版本,或者任何在.NET框架SDK中指出的支持OLE DB接口清單的OLE DB Provider,OLE DB接口清單將在后面列出; 建議在獨立的應用程序中使用Access,中間層應用程序不建議使用Access; 不再支持為ODBC的OLE DB Provider,要訪問ODBC,使用ODBC.NET Data Provider。 |
| ODBC.NET Data Provider | 在.NET框架中使用System.Data.Odbc命名空間; 提供對使用ODBC驅動連接的數據庫的訪問; |
| .NET Data Provider For Oracle | 在.NET框架中使用System.Data.OracleClient命名空間; 提供對Oracle數據庫的訪問。 |
| Custom.NET Data Provider | 提供一套接口,讓你可以自定義一個Data Provider; |
| SQLXML Managed Classes | 包含SQLXML Managed Classes的最新版SQLXML3.0,使得你可以訪問SQL Server2000或以后版本的XML功能性擴展,比如執行XML模板文件、執行XPath查詢和使用Updategrams或Diffgrams更新數據等;在SQLXML 3.0中存儲過程和XML模板將會通過SOAP作為一種WEB服務。 |
??????? 表格中提到的OLE DB接口清單,在這里把它列出
| OLE DB 對象 | 接口 |
| OLE DB Services | IdataInitilize |
| DataSource | IDBInitialize |
| Session | ISessionProperties |
| Command | IcommandText |
| MultipleResults | ImultipleResults |
| RowSet | Irowset |
| Row | IRow* |
| Error | IerrorInfo |
??? C.連接SQL Server7.0或更高版本
??????? 使用SQL Server.NET Data Provider連接SQL Server7.0或更高版本是最好的方式,在于它建立與SQL Server的直接連接而中間不需要任何的技術層銜接。如下圖一展示了各種訪問SQL Server7.0或更高版本的技術比較:
圖一(連接訪問SQL Server7.0或更高版本的各種技術比較)
??????? 以下例子演示怎樣創建和打開一個到SQL Server7.0或更高版本數據庫的連接:
‘Visual?Basic
Dim nwindConn As SqlConnection = New SqlConnection("Data Source=localhost;Integrated Security=SSPI;" & _?????????????????????????????????????????????????? "Initial Catalog=northwind")
nwindConn.Open()
‘C#
SqlConnection nwindConn = new SqlConnection("Data Source=localhost; Integrated Security=SSPI;" +
"Initial Catalog=northwind");
nwindConn.Open();
??? D.連接ODBC數據源
??????? ODBC.NET Data Provider,使用System.Data.Odbc命名空間,擁有為SQL Server和OLE DB的.NET Data Porvider一樣的結構,使用ODBC前綴(比如OdbcConnetion)和標準的ODBC連接字符。下面例子演示怎樣創建和打開一個到ODBC數據源的連接:
‘Visual?Basic
Dim nwindConn As OdbcConnection = New OdbcConnection("Driver={SQL Server};Server=localhost;" & _???????????????????????????????????????????????????? "Trusted_Connection=yes;Database=northwind")
nwindConn.Open()
‘C#
OdbcConnection nwindConn = new OdbcConnection("Driver={SQL Server};Server=localhost;" +
"Trusted_Connection=yes;Database=northwind");
nwindConn.Open();
??? E.使用DataReaders、DataSets、DataAdapters和DataViews
??????? ADO.NET使用DataSet和DataReader對象讀取數據并存儲。DataSet就好比是數據庫的直系親屬,擁有數據庫的所有表、順序和數據庫的約束(比如表間關系)。DataReader則從數據庫讀取快速的、只進的的和只讀的數據流。使用DataSet,你將會經常使用DataAdapter(或者CommandBuilder)與你的數據庫打交道,同時,你也許會使用DataView去排序和過濾數據,DataSet還允許你可以創建一個繼承于DataSet的子對象來表現數據中的表、行和列。下面圖二顯示DataSet對象模型:
圖二(DataSet對象模型)
下面將要介紹在什么時候使用DataSet或DataReader最恰當,同時也將說明如何使用DataAdapter(包括CommandBuilder)和DataView最優化對數據的訪問。
??? F.DataSet和DataReader的比較
??????? 在設計你的應用程序時決定究竟使用DataSet還是使用DataReader,主要看在你的應用程序中要實現的功能性級別。
??????? 使用DataSet可以在你的應用程序中做以下事情:
??????? I.在多個離散的結果表之間導航;
??????????? 一個DataSet可以包含多個結果表,這些結果表是不連續的。你可以分開處理這些表,也可以把這些表當作父子關系進行處理。
??????? II.操作多個數據源(比如從XML文件和電子數據表等不只一個數據庫得到的混合數據);
??????? 下面的例子演示從SQL Server2000的Northwind數據庫讀取一個customers表的清單和從Access2000的Northwind數據庫讀取一個orders表的清單,然后使用DataRelation在兩個表之間建立一個對應關系:
‘Visual?Basic
Dim custConn As SqlConnection= New SqlConnection("Data Source=localhost;Integrated Security=SSPI;" & _
"Initial Catalog=northwind;")
Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers", custConn)
Dim orderConn As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" & _?????????????????????????????????????????????????????? "Data Source=c:"Program Files"Microsoft Office"" & _?????????????????????????????????????????????????????? "Office"Samples"northwind.mdb;")
Dim orderDA As OleDbDataAdapter = New OleDbDataAdapter("SELECT * FROM Orders", orderConn)
custConn.Open()
orderConn.Open()
Dim custDS As DataSet = New DataSet()
custDA.Fill(custDS, "Customers")
orderDA.Fill(custDS, "Orders")
custConn.Close()
orderConn.Close()
Dim custOrderRel As DataRelation = custDS.Relations.Add("CustOrders", _???????????????????????????????????? custDS.Tables("Customers").Columns("CustomerID"), _????????????????????????????????????custDS.Tables("Orders").Columns("CustomerID"))
Dim pRow, cRow As DataRow
For Each pRow In custDS.Tables("Customers").Rows
?Console.WriteLine(pRow("CustomerID").ToString())
?For Each cRow In pRow.GetChildRows(custOrderRel)
??? Console.WriteLine(vbTab & cRow("OrderID").ToString())
?Next
Next
‘C#
SqlConnection custConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind;");
SqlDataAdapter custDA = new SqlDataAdapter("SELECT * FROM Customers", custConn);
OleDbConnection orderConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" +??????????????????????????????????????????????? "Data Source=c:""Program Files""Microsoft Office""Office""Samples""northwind.mdb;");
OleDbDataAdapter orderDA = new OleDbDataAdapter("SELECT * FROM Orders", orderConn);
custConn.Open();
orderConn.Open();
DataSet custDS = new DataSet();
custDA.Fill(custDS, "Customers");
orderDA.Fill(custDS, "Orders");
custConn.Close();
orderConn.Close();
DataRelation custOrderRel = custDS.Relations.Add("CustOrders",????????????????????????????? custDS.Tables["Customers"].Columns["CustomerID"],????????????????????????????? custDS.Tables["Orders"].Columns["CustomerID"]);
foreach (DataRow pRow in custDS.Tables["Customers"].Rows)
{
?Console.WriteLine(pRow["CustomerID"]);
?? foreach (DataRow cRow in pRow.GetChildRows(custOrderRel))
??? Console.WriteLine(""t" + cRow["OrderID"]);
}
??????? III.層中交換數據或者使用一個XML WEB服務,與DataReader不一樣的是DataSet可以被傳遞給一個遠程的客戶端;
??????????? 下面的例子演示如何創建一個XML WEB服務,其中使用GetCustomers取數據庫中customers表數據,使用UpdateCustomers更新數據庫中數據:
1.???? ‘Visual?Basic
2.???? <% @ WebService Language = "VB" Class = "Sample" %>
3.???? Imports System
4.???? Imports System.Data
5.???? Imports System.Data.SqlClient
6.???? Imports System.Web.Services
7.???? <WebService(Namespace:="http://microsoft.com/webservices/")> _
8.???? Public Class Sample
9.???? ??Public nwindConn As SqlConnection = New SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind")
10.???<WebMethod( Description := "Returns Northwind Customers", EnableSession := False )> _
11.???Public Function GetCustomers() As DataSet
12.?????Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT CustomerID, CompanyName FROM Customers", nwindConn)
13.?????Dim custDS As DataSet = New DataSet()
14.?????custDA.MissingSchemaAction = MissingSchemaAction.AddWithKey
15.?????custDA.Fill(custDS, "Customers")
16.?????GetCustomers = custDS
17.???End Function
18.???<WebMethod( Description := "Updates Northwind Customers", EnableSession := False )> _
19.???Public Function UpdateCustomers(custDS As DataSet) As DataSet
20.?????Dim custDA As SqlDataAdapter = New SqlDataAdapter()
21.?????custDA.InsertCommand = New SqlCommand("INSERT INTO Customers (CustomerID, CompanyName) " & _????????????????????????????????????????? "Values(@CustomerID, @CompanyName)", nwindConn)
22.?????custDA.InsertCommand.Parameters.Add("@CustomerID", SqlDbType.NChar, 5, "CustomerID")
23.?????custDA.InsertCommand.Parameters.Add("@CompanyName", SqlDbType.NChar, 15, "CompanyName")
24.?????custDA.UpdateCommand = New SqlCommand("UPDATE Customers Set CustomerID = @CustomerID, " & _
25.?"CompanyName = @CompanyName WHERE CustomerID = @OldCustomerID", nwindConn)
26.?????custDA.UpdateCommand.Parameters.Add("@CustomerID", SqlDbType.NChar, 5, "CustomerID")
27.?????custDA.UpdateCommand.Parameters.Add("@CompanyName", SqlDbType.NChar, 15, "CompanyName")
28.?????Dim myParm As SqlParameter = custDA.UpdateCommand.Parameters.Add("@OldCustomerID", SqlDbType.NChar, 5, "CustomerID")
29.?????myParm.SourceVersion = DataRowVersion.Original
30.?????custDA.DeleteCommand = New SqlCommand("DELETE FROM Customers WHERE CustomerID = @CustomerID", nwindConn)
31.?????myParm = custDA.DeleteCommand.Parameters.Add("@CustomerID", SqlDbType.NChar, 5, "CustomerID")
32.?????myParm.SourceVersion = DataRowVersion.Original
33.?????custDA.Update(custDS, "Customers")
34.?????UpdateCustomers = custDS
35.???End Function
36.?End Class
37.??
38.?‘C#
39.?<% @ WebService Language = "C#" Class = "Sample" %>
40.?using System;
41.?using System.Data;
42.?using System.Data.SqlClient;
43.?using System.Web.Services;
44.?[WebService(Namespace="http://microsoft.com/webservices/")]
45.?public class Sample
46.?{
47.???public SqlConnection nwindConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind");
48.???[WebMethod( Description = "Returns Northwind Customers", EnableSession = false )]
49.???public DataSet GetCustomers()
50.???{
51.?????SqlDataAdapter custDA = new SqlDataAdapter("SELECT CustomerID, CompanyName FROM Customers", nwindConn);
52.?????DataSet custDS = new DataSet();
53.?????custDA.MissingSchemaAction = MissingSchemaAction.AddWithKey;
54.?????custDA.Fill(custDS, "Customers");
55.?????return custDS;
56.???}
57.???[WebMethod( Description = "Updates Northwind Customers", EnableSession = false )]
58.???public DataSet UpdateCustomers(DataSet custDS)
59.???{
60.?????SqlDataAdapter custDA = new SqlDataAdapter();
61.?????custDA.InsertCommand = new SqlCommand("INSERT INTO Customers (CustomerID, CompanyName) " +????????????????????????????????????????? "Values(@CustomerID, @CompanyName)", nwindConn);
62.?????custDA.InsertCommand.Parameters.Add("@CustomerID", SqlDbType.NChar, 5, "CustomerID");
63.?????custDA.InsertCommand.Parameters.Add("@CompanyName", SqlDbType.NChar, 15, "CompanyName");
64.?????custDA.UpdateCommand = new SqlCommand("UPDATE Customers Set CustomerID = @CustomerID, " + "CompanyName = @CompanyName WHERE CustomerID = @OldCustomerID", nwindConn);
65.?????custDA.UpdateCommand.Parameters.Add("@CustomerID", SqlDbType.NChar, 5, "CustomerID");
66.?????custDA.UpdateCommand.Parameters.Add("@CompanyName", SqlDbType.NChar, 15, "CompanyName");
67.?????SqlParameter myParm = custDA.UpdateCommand.Parameters.Add("@OldCustomerID", SqlDbType.NChar, 5, "CustomerID");
68.?????myParm.SourceVersion = DataRowVersion.Original;
69.?????custDA.DeleteCommand = new SqlCommand("DELETE FROM Customers WHERE CustomerID = @CustomerID", nwindConn);
70.?????myParm = custDA.DeleteCommand.Parameters.Add("@CustomerID", SqlDbType.NChar, 5, "CustomerID");
71.?????myParm.SourceVersion = DataRowVersion.Original;
72.?????custDA.Update(custDS, "Customers");
73.?????return custDS;
74.???}
}
??????? IV.數據的再使用(比如排序、搜索或過濾數據);
??????? V.執行每行的大容量數據處理,處理DataReader掛起的連接服務已不再需要、影響性能的每一行;
??????? VI.使用諸如XSLT轉換或者XPath查詢等XML操作的多重數據。
??????????? 下面的例子介紹如何使用XmlDataDocument同步DataSet數據和如何使用XSLT樣式文件在HTML文件中包含DataSet數據,首先是XSLT樣式文件:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="CustomerOrders">
?<HTML>
?<STYLE>
?BODY {font-family:verdana;font-size:9pt}
?TD?? {font-size:8pt}
?</STYLE>
??? <BODY>
??? <TABLE BORDER="1">
????? <xsl:apply-templates select="Customers"/>
??? </TABLE>
??? </BODY>
?</HTML>
</xsl:template>
<xsl:template match="Customers">
??? <TR><TD>
????? <xsl:value-of select="ContactName"/>, <xsl:value-of select="Phone"/><BR/>
??? </TD></TR>
????? <xsl:apply-templates select="Orders"/>
</xsl:template>
<xsl:template match="Orders">
?<TABLE BORDER="1">
??? <TR><TD valign="top"><B>Order:</B></TD><TD valign="top"><xsl:value-of select="OrderID"/></TD></TR>
??? <TR><TD valign="top"><B>Date:</B></TD><TD valign="top"><xsl:value-of select="OrderDate"/></TD></TR>
??? <TR><TD valign="top"><B>Ship To:</B></TD>
??????? <TD valign="top"><xsl:value-of select="ShipName"/><BR/>
??????? <xsl:value-of select="ShipAddress"/><BR/>
??????? <xsl:value-of select="ShipCity"/>, <xsl:value-of select="ShipRegion"/>?<xsl:value-of select="ShipPostalCode"/><BR/>
??????? <xsl:value-of select="ShipCountry"/></TD></TR>
?</TABLE>
</xsl:template>
</xsl:stylesheet>
??????????? 接著下面的代碼演示如何填充DataSet的數據和運用XSLT樣式:
‘Visual?Basic
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Xml
Imports System.Xml.Xsl
Public Class Sample
?Public Shared Sub Main()
??? Dim nwindConn As SqlConnection = New SqlConnection("Data Source=localhost;Initial Catalog=northwind;Integrated Security=SSPI")
??? nwindConn.Open()
??? Dim myDataSet As DataSet = New DataSet("CustomerOrders")
??? Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers", nwindConn)
??? custDA.Fill(myDataSet, "Customers")
??? Dim ordersDA As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Orders", nwindConn)
??? ordersDA.Fill(myDataSet, "Orders")
??? nwindConn.Close()
??? myDataSet.Relations.Add("CustOrders",_??????????????????????????? myDataSet.Tables("Customers").Columns("CustomerID"),_??????????????????????????? myDataSet.Tables("Orders").Columns("CustomerID")).Nested = true
??? Dim xmlDoc As XmlDataDocument = New XmlDataDocument(myDataSet)
??? Dim xslTran As XslTransform = New XslTransform
??? xslTran.Load("transform.xsl")
??? Dim writer As XmlTextWriter = New XmlTextWriter("xslt_output.html", System.Text.Encoding.UTF8)
??? xslTran.Transform(xmlDoc, Nothing, writer)
??? writer.Close()
?End Sub
End Class
‘C#
using System;
using System.Data;
using System.Data.SqlClient;
using System.Xml;
using System.Xml.Xsl;
public class Sample
{
?public static void Main()
?{
??? SqlConnection nwindConn = new SqlConnection("Data Source=localhost;Initial Catalog=northwind;Integrated Security=SSPI;");
??? nwindConn.Open();
??? DataSet custDS = new DataSet("CustomerDataSet");
??? SqlDataAdapter custDA = new SqlDataAdapter("SELECT * FROM Customers", nwindConn);
??? custDA.Fill(custDS, "Customers");
??? SqlDataAdapter ordersDA = new SqlDataAdapter("SELECT * FROM Orders", nwindConn);
??? ordersDA.Fill(custDS, "Orders");
??? nwindConn.Close();
??? custDS.Relations.Add("CustOrders",
???????????????????????? custDS.Tables["Customers"].Columns["CustomerID"],
??????????????? ?????????custDS.Tables["Orders"].Columns["CustomerID"]).Nested = true;
??? XmlDataDocument xmlDoc = new XmlDataDocument(custDS);
??? XslTransform xslTran = new XslTransform();
??? xslTran.Load("transform.xsl");
??? XmlTextWriter writer = new XmlTextWriter("xslt_output.html", System.Text.Encoding.UTF8);
??? xslTran.Transform(xmlDoc, null, writer);
??? writer.Close();
?}
}
??????? 使用DataReader可以在你的應用程序中做以下事情:
??????? I.不需要緩存數據;
??????? II.處理太大而不能存儲的數據;
??????? III.需要以只進、只讀和快速方式一次性訪問數據的。
??? G.使用一個自定義的強有力的DataSet類型的好處
??????? 通過創建一個繼承于DataSet的子對象,你可以在運行期間執行類型檢查和聲明。當你有了一個確定的計劃或者為你的DataSet有相關的結構,你就可以創建一個用行和列表述一個對象的DataSet。比如,你表露一個消費者對象的名字屬性來取代表露消費者表的一行中的名字列。有關此節詳細信息,請參考微軟站點上的文章:Working with a Typed DataSet
??? H.在自定義的DataSet中處理無效數據
??????? 通過XSD語言檢查你的DataSet確保你的DataSet適當地處理無效引用。nullValue注釋使你把BBNull替換成別的字符,String.Empty;或者保留無效引用,拋出錯誤提示,提示將取決于你應用程序的上下文,默認情況是引用了無效字符。
??? I.在DataSet中刷新數據
??????? 如果你要從數據庫刷新你的DataSet,使用DataAdapter.Fill,如果你的DataTable擁有主鍵,DataAdapter.Fill將根據主鍵匹配新的行,同時從數據庫取值運用到已存在的行。除非已刷新行在再次刷新前被修改,否者它的RowState將會被設置為UnChanged。注意的是如果DataTable沒有設置主鍵,你的DataSet有可能出現重復的值。如果你想從數據庫刷新一個表并保留任何表中行的更改,那么你就要首先填充一個新表,然后利用preserveChanges等于true來合并那個DataTable到你的DataSet中去。
??? J.在DataSet中搜索數據
??????? 當你在一個DataSet中查詢特殊標準的行時,利用索引查詢將會增加你的查詢性能。當你給一個DataTable設計主鍵時,索引同時也創建了。當你為一個DataTable創建DataView時,索引也同時創建了。以下是使用索引查詢的一些情況:
??? ??? I.如果查詢與DataTable中標識主鍵的列順序相反,使用DataTable.Rows.Find代替DataTable.Select;
??????? II.如果查詢包括無主鍵的列,你可以使用DataView為數據的多重查詢改善性能。當你在DataView中使用排序時,查詢的同時就會創建一個索引。DataView使用Find和FindRows方法查詢DataTable中的數據;
??????? IV.假如你不需要表的排序視圖,你也可以利用DataView為DataTable創建一個索引查詢。注意的是這僅僅在你執行多重查詢時才有優勢,如果你只是執行一個簡單查詢,使用此方法將會降低你的查詢效率。
??? K.DataView的結構
??????? 前面也講過,在給DataTable創建DataView和Sort、RowFilter或者RowStateFilter屬性發生更改的同時潛在的也給DataTable創建了索引。創建DataView對象時,如果Sort、RowFilter和RowStateFilter屬性也同時指定,那么索引將只創建一次;如果創建一個空的DataView,那么索引至少被創建兩次。
??? L.頁面調度
??????? ADO.NET使你可以很清楚地控制從你的數據庫返回什么樣的數據和有多少數據存儲到一個DataSet。以下沒有單一的介紹調度一個查詢結果,但是當你設計你的應用程序時應該考慮到以下情況:
??????? I.避免在使用DataAdapter.Fill時,在startRecord和maxRecords值上溢出。
??????? II.解決這類問題的辦法是使用WHERE語句、ORDER BY語句和TOP斷言。
??????? III.還有一種解決辦法是使用TOP斷言和嵌套的SELECT聲明。比如如下代碼:
???? SELECT TOP 10 * FROM
(SELECT TOP 30 * FROM Customers ORDER BY Id ASC) AS Table1 ORDER BY Id DESC
IV.如果你的日期不是經常改變,你可以使用DataSet的存儲功能改善執行性能,比如你可以存儲相當10頁的數據到你的DataSet,然后當用戶訪問超過在存儲區的FirstPage和LastPage時才查詢數據庫以獲得新的數據。
??? M.有計劃地填充DataSet
??????? 當使用數據填充DataSet時,DataAdapter.Fill方法使用DataSet已有的計劃和SelectCommand返回的數據對DataSet進行填充。如果DataSet中沒有與之對應的表將會失敗,Fill創建一個表,默認情況下,Fill僅僅定義列和列的類型。你可以通過設置DataAdapter的MissingSchemaAction屬性重載默認的Fill方法。舉例,要使Fill方法創建表時總是包含主鍵信息、唯一約束、列屬性、是否允許空值、列的最大長度、只讀列和自動增量列,指定DataAdapter.MissingSchemaAction為MissingSchemaAction.AddWithKey。作為選擇,你也可以在調用DataAdapter.Fill之前調用DataAdpater.FillSchema來保證填充DataSet時計劃到位。調用FillSchema會給數據庫增加額外的負擔來輸出Schema信息,所以最好的建議是指定DataSet的計劃,或者在調用Fill之前設置DataAdapter的MissingSchemaAction。
??? N.使用CommandBuilder
??????? CommandBuilder自動地生成基于DataAdapter的SelectCommand的DataAdapter的InsertCommand、UpdateCommand和DeleteCommand屬性。提供SelectCommand執行一個簡單的SELECT,以下信息介紹使用CommandBuilder的最佳處理。
??????? I.在設計階段不要使用CommandBuilder,否者產生DataAdapter Command屬性的進程將會受到干擾。如果你預先知道你的UPDATE、INSERT和DELETE聲明的內容,你應該清楚地指定。一個最好的設計方案是為你的UPDATE、INSERT和DELETE創建存儲過程,并在DataAdapter的Command屬性中設置和使用它們。
??????? II.CommandBuilder使用SelectCommand決定其他Command屬性的值。如果DataAdapter的SelectCommand本身發生變化,應該使用RefreshSchema去刷新Command的屬性。
??????? III.只要DataAdapter的Command屬性為空,CommandBuilder就僅僅創建一個Command,即使你明確地指定Command的屬性值,CommandBuilder也不會重寫,所以如果你想創建一個Command并保留以前的屬性設置,那么就把Command的屬性設置為null。
??? O.SQL的批聲明和處理
??????? 很多的數據庫都支持在一條命令中使用綜合查詢或批處理或多條子命令。比如SQL Server中使用“;”。在一條命令中使用綜合的多重命令可以有效地減少與數據庫之間交互的次數并在你的應用程序中提高效率。比如在你的應用程序中使用批處理完成所有的刪除(delete)任務等等。
??????? 使用批處理確實提高了效率,但同時也在你的應用程序中管理更新DataSet數據時增加了復雜性。要使得復雜性變簡單化,你就要在你的DataSet中為每個DataTable創建一個DataAdapter。
??? P.使用多個表填充一個DataSet
??????? 如果你是用批處理從多個表返回數據并把這些數據填充到一個DataSet,fill方法將會使用第一個表的表名命名第一個表,以后的表命名將會采用在第一個表的表名基礎上加上一個遞增的數字。舉例,下面的代碼將逐步說明fill方法的工作原理:
??????? ‘Visual Basic
??????? Dim da As SqlDataAdapter = New SqlDataAdapter(“select * from customers;select * from orders;”,myConnection)
??????? Dim ds As DataSet = New DataSet()
??????? da.fill(ds,”customers”)
??????? ‘C#
??????? SqlDataAdapter da = new SqlDataAdapter(“select * from customers;select * from orders;”,myConnection);
??????? DataSet ds = new DataSet();
??????? da.fill(ds,”customers”);
??????? 如上面代碼所示,customers表數據將會存放在一個命名為customers的DataTable中,而orders表數據將會放在一個命名為customers1的DataTable中。當然你也可以在數據填充結束后很容易地修改customers1表屬性(TableName)為orders。然而,在以后的數據填充時,只會影響customers表中數據,而orders表將會忽略并同時創建一個新的命名為customers1的表。要解決這個問題,你就要在customers1和orders之間建立一個DataTableMapping映射。其他表也如此。舉例說明:
??????? ‘Visual Basic
Dim da As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers; SELECT * FROM Orders;", myConnection)
da.TableMappings.Add("Customers1", "Orders")
Dim ds As DataSet = New DataSet()
da.Fill(ds, "Customers")
‘C#
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Customers; SELECT * FROM Orders;", myConnection);
da.TableMappings.Add("Customers1", "Orders");
DataSet ds = new DataSet();
da.Fill(ds, "Customers");
??? Q.使用DataReader
??????? 下面是使用DataReader的一些技巧和一些問題的回答:
??????? I.在使用相關Command訪問任何輸出參數之前必須關閉DataReader;
??????? II.在讀取數據結束后應當關閉DataReader。如果你的Connection僅僅是用來返回DataReader,那么在關閉DataReader之后你也應該立即關閉它。另外一個關閉Connection的方法是傳遞CommandBehavior.CloseConnection給ExecuteReader方法。此方法在你從一個方法中返回DataReader并且對這個DataReader沒有關閉控制權或者關聯的連接時使用時非常有用的。
??????? III.DataReader是為已連接的數據存取設計;
??????? IV.使用GetString、GetInt32等返回特殊數據類型數據;
??????? V.一個連接只允許使用一個DataReader。在ADO中,如果你只創建一個連接并使用兩個recordsets,一個只讀游標和一個只進游標,但實際上,ADO已經為你創建了一個隱式的連接,并在不用的時候隱式地關閉它。ADO.NET中是不行的,你必須為每個DataReader創建一個Connection,這也是為了讓你在使用Connection時給予更多的控制信息。
??????? VI.默認下,DataReader每次讀取時把行中所有的數據裝載到內存中。并允許你隨機存取當前行中數據。如果隨即存取沒有必要(沒有必要把所有數據都裝載到內存),并想提高執行效率,給ExecuteReader方法傳遞CommandBehavior.SequentialAccess,這樣就會改變DataReader的默認動作為僅僅裝載請求的數據到內存。注意的是這種方法要求你順序地存取行中列數據,一旦你略過某一列,以后你將再不會讀取到該列的數據。
??????? VII.如果當你在完成從一個DataReader讀取數據后,仍然還有大量未讀不需要的數據,這就要在調用DataReader的Close命令之前調用Cancel命令。調用DataReader的Close命令會導致把不需要的數據裝載進來并在關閉游標之前清空數據。而調用Cancel命令就會丟棄這部分數據,從而DataReader在關閉之前就不會讀取它們。如果你正在從你的命令返回輸出參數,調用Cancel命令同樣會丟棄它們。如果你需要讀取任何輸出參數,你就不要使用Cancel,而直接使用Close。
??? R.BLOBs對象
??????? 當你使用DataReader讀取二進制數據時,你應該傳遞CommandBehavior.SequentialAccess給ExecuteReader方法調用。因為DataReader的默認情況是每次讀取數據時把每行中所有的數據都存儲到內存,而二進制數據又非常的大,結果就會使大量的內存空間被一個單一的BLOB占用。SequentialAccess使得你的DataReader默認行為為僅讀取需要的數據。然后你就可以使用GetBytes或者GetChars決定一次讀取多少數據。
??????? 記住的是使用SequentialAccess后,你不能次序顛倒地訪問DataReader中不同的字段。就是說,如果你的查詢返回三列,其中第三列是BLOB數據類型,如果你想訪問第三列數據,那么你就必須先訪問第一列,然后是第二列,再然后才是第三列的BLOB數據。這是因為此時返回的數據是有序的,而且一旦你跳過某一列,再回過頭來讀取這一列是不行的。
??? S.使用命令
??????? ADO.NET提供了執行命令的幾種不同方法,同時也提供了優化執行命令的幾種不同參數。下面將要介紹的是選擇最佳執行命令的技巧和改善一個可執行命令的性能。
??????? I.OleDbCommand最佳實踐
??????? .NET 框架中各種數據提供者之間的執行命令標準幾乎是一樣的。但是也有不同,下面是執行OleDbCommand的一些技巧:
??????? ??? ①使用CommandType.Text調用存儲過程,使用CommandType.StoredProcedure生成;
??????????? ②確定設置OleDbParameter的類型、大小(如果要求)和精度(如果是數字或者小數),注意的是,如果你不明確設置OleDbParameter,OleDbCommand將會為你的執行命令重新生成OleDbParameter。
??????? II.SqlCommand最佳實踐
??????????? 使用SqlCommand快速執行存儲過程:如果你要調用一個存儲過程,指定SqlCommand的CommandType為存儲過程的CommandType。這樣在執行命令時,就會提交此命令是調用存儲過程,從而達到快速執行。
??????? III.使用已準備的方法
??????????? Command.Prepare方法優化你的參數化執行命令。Prepare結構為多重調用最優化指定命令。要使用Prepare,你首先得理解你的數據庫是怎樣相應Prepare調用。SQL Server 2000中,Command已經被隱式優化和Prepare不是必須的;在SQL Server7.0或其它數據庫中使用Prepare是有效的。
??????? IV.明確地指定計劃和元數據
??????????? ADO.NET中的很多對象都要推斷元數據信息,只要用戶不指定它,舉例如下:
??????????? ①如果在DataSet中不存在,DataAdapter.Fill方法就會創建表和列信息;
??????????? ②CommandBuilder為獨立表的Select命令生成DataAdpater命令參數;
??????????? ③CommandBuilder.DeriveParameters組裝一個命令對象的參數信息;
??????? 如果什么時候都使用上面講的方法,可能會降低執行性能。推薦在設計階段和廣告段應用程序中使用。可能的情況下,一般都要指定計劃和元數據。這些包括指定DataSet的表和列、指定DataAdapter的Command屬性和指定Command的參數信息。
??????? V.ExecuteScalar和ExecuteNonQuery
??????????? 如果你想只返回一個簡單值,比如Count(*)、Sum(Price)或者Avg(Quantity),你可以使用ExecuteScalar,它幫助你一步到位得到你想要的值,從而避免使用DataReader的兩步計算(ExecuteReader+GetValue);
??????????? 當你不想返回行信息,比如修改數據(INSERT、UPDATE、DELETE)或者僅需要輸出參數或者返回值,使用ExecuteNonQuery,它去掉不必要的處理創建一個空的DataReader。
??????? VI.空值檢查
??????????? 如果在你的表中某列允許空值,你可以使用Where語句進行空值檢查,下面舉例說明:
??????? select * from customers where ((LastName=@LastName) or (LastName IS NULL and @LastName IS NULL))
??????????? 上面語句檢查了列是否為空和參數是否為空。
??????? VII.傳遞null參數值
??????????? 當你在命令中傳遞null參數值給數據庫時,你不能使用null(Nothing在vb中),應該使用DBNull.Value。舉例:
??????? ‘vb
??????? Dim param As SqlParameter = New SqlParameter(“@Name”,SqlDbType.NVarChar,20)
??????? param.Value = DBNull.Value
??????? ‘C#
??????? SqlParameter param = new SqlParameter(“@Name”,SqlDbType.NVarChar,20);
??????? param.Value = DBNull.Value;
??????? VIII.使用事務處理
??????????? ADO.NET中的事務處理模型已經改變,在ADO中,一旦StartTransaction被調用,任何事務下的更新都被認為是事務的一部分。然而,在ADO.NET中,當Connection.BeginTransaction被調用,返回一個命令關聯的事務對象(事務屬性是由命令的事務屬性指定的)。這樣保證讓你在一個Connection中執行多個事務。如果命令的Command.Transaction屬性與開始的事務不一致,命令就不會完成并拋出錯誤。
??? ??? IX.使用Connections
??????????? 高效率的應用程序應該使用最少的時間與數據庫建立連接,比如使用Connection Pooling等。下面將介紹如何使用ADO.NET建立高效率應用的一些數據庫方面的技巧。
??????? ①Connection Pooling
??????????? 在SQL Server、OLE DB和.NET框架結構中的Data Provider中,都提供了隱式的連接池連接支持。你可以在ConnectionString中指定不同的參數值控制連接池的行為。比如下面的例子使OLE DB的連接池無效并自動地進行事務處理:
??????? Provider=SQLOLEDB;OLE DB Services=-4;Data Source=localhost;Integrated Security=SSPI;
??????????? 在SQL Server.NET Data Provider中提供了以下參數設置控制連接池的行為:Connection Lifttime、Connection Reset、Enlist、Max Pool Size、Min Pool Size和Pooling。
??????? ②使用DataAdapter最優化連接
??????????? 使用DataAdpater的Fill和Update方法時會自動地打開相應的連接。如果Fill或者Update打開一個連接,在它操作完成后它會關閉此連接。最好的執行方式是在你需要的時候才建立連接。同時減少多個操作時打開和關閉連接的次數。
??????????? 推薦你在僅僅執行一個Fill或者Update時,允許Fill或者Update方法隱式地打開和關閉連接;如果你要執行多個Fill或者Update,建議你顯式地建立連接、執行Fill或者Update操作然后顯式地關閉連接。
??????????? 額外地,在我們執行事務處理時,在開始事務之前應該顯式地建立連接,并在事務結束后顯式地關閉連接。舉例:
‘Visual Basic
Public Sub RunSqlTransaction(da As SqlDataAdapter, myConnection As SqlConnection, ds As DataSet)
?myConnection.Open()
?Dim myTrans As SqlTransaction = myConnection.BeginTransaction()
?myCommand.Transaction = myTrans
?Try
??? da.Update(ds)
??? myTrans.Commit()
??? Console.WriteLine("Update successful.")
?Catch e As Exception
??? Try
????? myTrans.Rollback()
??? Catch ex As SqlException
????? If Not myTrans.Connection Is Nothing Then
??????? Console.WriteLine("An exception of type " & ex.GetType().ToString() & _
????????????????????????? " was encountered while attempting to roll back the transaction.")
????? End If
??? End Try
??? Console.WriteLine("An exception of type " & e.GetType().ToString() & " was encountered.")
??? Console.WriteLine("Update failed.")
?End Try
?myConnection.Close()
End Sub
‘C#
public void RunSqlTransaction(SqlDataAdapter da, SqlConnection myConnection, DataSet ds)
{
?myConnection.Open();
?SqlTransaction myTrans = myConnection.BeginTransaction();
?myCommand.Transaction = myTrans;
?try
?{
??? da.Update(ds);
??? myCommand.Transaction.Commit();
??? Console.WriteLine("Update successful.");
?}
?catch(Exception e)
?{
??? try
??? {
????? myTrans.Rollback();
??? }
??? catch (SqlException ex)
??? {
????? if (myTrans.Connection != null)
????? {
??????? Console.WriteLine("An exception of type " + ex.GetType() +
????????????????????????? " was encountered while attempting to roll back the transaction.");
????? }
??? }
??? Console.WriteLine(e.ToString());
??? Console.WriteLine("Update failed.");
?}
?myConnection.Close();
}
??????? X.總是關閉Connections和DataReaders
??????????? 在你使用完Connection或者DataReader對象后,你應該明確地關閉它們。系統中的碎片整理程序只是在最后需要的時候才進行整理,而一些很耗資源的連接還得由你自己來釋放。同時如果你不明確地關閉連接,此連接就有可能不返回連接池,除非連接池的Max Pool Size已經達到并且此連接還仍然有效。
??????????? 注意:在你的類的Finalize方法中不要使用Close或者Dispose運用到一個Connection或者一個DataReader或者任何被管理對象上。在一個Finalizer中,僅僅釋放你的類直接擁有的無法管理的資源。如果你的類不擁有任何無法管理的資源,就不要在你的類使用Finalize方法。
??????? XI.在C#中使用Using聲明
??????????? 在C#中,一個非常便利的保證關閉你使用過的Connection和DataReader對象的方法是使用Using聲明。當對象超出了它的使用范圍,Using聲明就會自動地釋放該對象。舉例:
‘C#
string connString = "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;";
using (SqlConnection conn = new SqlConnection(connString))
{
?SqlCommand cmd = conn.CreateCommand();
?cmd.CommandText = "SELECT CustomerId, CompanyName FROM Customers";
?
?conn.Open();
?using (SqlDataReader dr = cmd.ExecuteReader())
?{
??? while (dr.Read())
????? Console.WriteLine("{0}"t{1}", dr.GetString(0), dr.GetString(1));
?}
}
??????????? Using聲明在Visual Basic.Net中不可用。
??????? XII.避免訪問OleDbConnection.State屬性
??????????? 如果你需要經常檢查State屬性,最好在OleDbConnection上監聽StateChange事件。下面的代碼演示當OleDbConnection.State發生變化時使用StateChange向控制臺發送一條消息:
‘Visual?Basic
AddHandler nwindConn.StateChange, New StateChangeEventHandler(AddressOf OnStateChange)
Protected Shared Sub OnStateChange(sender As Object, args As StateChangeEventArgs)
?Console.WriteLine("The current Connection state has changed from {0} to {1}.", _
??????????????????? args.OriginalState, args.CurrentState)
End Sub
‘C#
nwindConn.StateChange?+= new StateChangeEventHandler(OnStateChange);
protected static void OnStateChange(object sender, StateChangeEventArgs args)
{
?Console.WriteLine("The current Connection state has changed from {0} to {1}.",
??????????????????? args.OriginalState, args.CurrentState);
}
??? T.與XML結合
??????? ADO.NET在DataSet中提供對XML的廣泛支持,同時在SQL Server2000或以后版本中的XML功能性擴展也能在ADO.NET中得到充分運用。你可以使用SQLXML訪問在SQL Server2000和以后版本中提供的XML功能性擴展。下面是使用XML和ADO.NET的一些技巧信息。
??? ??? I.DataSet和XML
??????? DataSet和XML的完美整合,可以使你完成以下事情:
??????????? ①從XSD計劃中載入一個DataSet的計劃或相關結構;
??????????? 下面的例子說明一個XSD文件的結構,其中MyDataSet就是我們的DataSet元素,它下面包含一個customers復合類型元素,有了它我們就可以映射創建一個這樣的表:Customers (CustomerID,CompanyName,Phone),同時也定義我們的DataSet的計劃或者結構:
<xs:schema id="SomeID"
???????????? xmlns=""
???????????? xmlns:xs="http://www.w3.org/2001/XMLSchema"
???????????? xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
??? <xs:element name="MyDataSet" msdata:IsDataSet="true">
????? <xs:complexType>
???? ???<xs:choice maxOccurs="unbounded">
????????? <xs:element name="customers" >
??????????? <xs:complexType >
????????????? <xs:sequence>
??????????????? <xs:element name="CustomerID" type="xs:integer"
???????????????????????????? minOccurs="0" />
??????? ????????<xs:element name="CompanyName" type="xs:string"
???????????????????????????? minOccurs="0" />
??????????????? <xs:element name="Phone" type="xs:string" />
????????????? </xs:sequence>
??????????? </xs:complexType>
?????????? </xs:element>
??????? </xs:choice>
????? </xs:complexType>
??? </xs:element>
?</xs:schema>
??????????? ②從XML文件中載入一個DataSet的內容;
??????????? 要從XML文件填充DataSet的內容,請使用DataSet對象的ReadXml方法。下面的例子說明如何從一個XML文件讀取數據到一個DataSet:
‘Visual?Basic
Dim myDS As DataSet = New DataSet
myDS.ReadXml("input.xml", XmlReadMode.ReadSchema)
‘C#
DataSet myDS = new DataSet();
myDS.ReadXml("input.xml", XmlReadMode.ReadSchema);
??????????? ③當沒有提供計劃時從一個XML文件的內容中推斷一個DataSet的計劃;
??????????? 要從一個XML文件載入DataSet的計劃信息,你可以使用DataSet對象的ReadXmlSchema方法。如果沒有提供計劃,你還可以使用InferXmlSchema從XML文件推斷DataSet的計劃,下面的例子介紹如何通過InferXmlSchema從一個XML文件推斷出DataSet的計劃:
‘Visual?Basic
Dim myDS As DataSet = New DataSet
myDS.InferXmlSchema("input_od.xml", New String[] {"urn:schemas-microsoft-com:officedata"})
‘C#
DataSet myDS = new DataSet();
myDS.InferXmlSchema("input_od.xml", new string[] "urn:schemas-microsoft-com:officedata");
??????????? ④象XSD格式計劃一樣寫一個DataSet的計劃;
??????????? 下面的例子展示如何通過ReadXmlSchema從一個XSD文件載入DataSet的計劃:
‘Visual?Basic
Dim myDS As DataSet = New DataSet
myDS.ReadXmlSchema("schema.xsd")
‘C#
DataSet myDS = new DataSet();
myDS.ReadXmlSchema("schema.xsd");
??????????? ⑤象XML格式文件一樣讀寫一個DataSet的內容。
??????????? 利用DiffGrams從DataSet中讀寫內容,下面的例子顯示在提交更改之前更新表中一行數據的結果,其中CustomerID為ALFKI的那一行數據被修改但是還沒有更新:
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
?<CustomerDataSet>
??? <Customers diffgr:id="Customers1" msdata:rowOrder="0" diffgr:hasChanges="modified">
????? <CustomerID>ALFKI</CustomerID>
????? <CompanyName>New Company</CompanyName>
??? </Customers>
??? <Customers diffgr:id="Customers2" msdata:rowOrder="1" diffgram:hasErrors="true">
????? <CustomerID>ANATR</CustomerID>
????? <CompanyName>Ana Trujillo Emparedados y helados</CompanyName>
??? </Customers>
??? <Customers diffgr:id="Customers3" msdata:rowOrder="2">
????? <CustomerID>ANTON</CustomerID>
????? <CompanyName>Antonio Moreno Taquerí-a</CompanyName>
??? </Customers>
??? <Customers diffgr:id="Customers4" msdata:rowOrder="3">
????? <CustomerID>AROUT</CustomerID>
????? <CompanyName>Around the Horn</CompanyName>
??? </Customers>
?</CustomerDataSet>
?<diffgr:before>
??? <Customers diffgr:id="Customers1" msdata:rowOrder="0">
????? <CustomerID>ALFKI</CustomerID>
????? <CompanyName>Alfreds Futterkiste</CompanyName>
??? </Customers>
?</diffgr:before>
?<diffgr:errors>
??? <Customers diffgr:id="Customers2" diffgr:Error="An optimistic concurrency violation has occurred for this row."/>
?</diffgr:errors>
</diffgr:diffgram>
??????? 注意:你可以在你的DataSet中使用XPath查詢和XSLT轉換來同步運用XML的功能性,或者提供一個相關的視圖,或者創建一個XML文檔數據的一個副本。
??? ??? II.計劃接口
??????????? 當你從一個XML文件載入一個DataSet時,你可以從XSD計劃載入DataSet的計劃,或者你可以在載入數據之前預先確定表和列。如果這里沒有XSD計劃或者你又不知道那個表和列是XML文件內容確定的,那么你可以使用基于XML文檔結構推斷計劃。
??????????? 計劃接口是一個很有用的移植工具,但是它應該限制在設計階段的應用程序中,僅僅因為以下幾點:
??????????? ①推斷計劃將會提出額外的處理從而影響應用程序性能的提高;
??????????? ②所有的列將會是一個數據類型:string;
??????????? ③推斷過程具有不確定性。那就是說,它是基于XML文件的,而不是基于有意的計劃。
??????? III.SQL SERVER 的FOR XML查詢
??????????? 如果你想返回如SQL SERVER的FOR XML查詢結果,你可以用SQL Server.NET Data Provider直接使用SqlCommand.ExecuteXmlReader方法創建一個XmlReader。
??????? IV.SQLXML管理類
??????????? 在.NET框架中SQLXML管理類使用Microsoft.Data.SqlXml命名空間。它使得你可以執行Xpath查詢和XML模板文件,如同運用XSLT轉換數據一樣。最新版本是SQLXML3.0。
??? U.更多有用技巧
??????? I.避免自動增量值沖突
??????????? 像大多數數據庫一樣,DataSet讓你在增加新的數據時標識為自動增量的列自動填充增量值。使用自動增量時,應當避免本地DataSet的增量值與數據庫的增量值相沖突。要避免這種情況,推薦在數據庫和DataSet同時使用自動增量時,在你的DataSet中創建AutoIncrementStep為-1和AutoIncrementSeed為0的自動增量列,同時保證你的數據庫中的列從1開始正方向遞增。這樣就保證一個負方向的增量不會與一個正方向的增量相沖突。另外一種方法是使用Guid代替自動增量。在DataSet中產生的Guid永遠不會與數據庫中產生的Guid一樣。如果你的自動增量列只是簡單地用作唯一值,并且不表示任何含義,建議你使用Guids代替自動增量。它們是唯一的并避免使用自動增量產生的額外工作。
??????? II.處理樂觀并發錯誤
??????????? 因為DataSet與數據庫是分離的,所以你應該在你的應用程序中避免當多個客戶更新數據庫數據時發生沖突。這里有幾種處理樂觀并發錯誤的解決方案。一是在你的表中增加一個時間戳列。二是校驗一行中所有列的數據是否與你在SQL聲明中使用Where子句找到的數據靜態匹配。下面的例子說明如何使用where條件處理樂觀并發錯誤:
‘Visual?Basic
?Dim nwindConn As SqlConnection = New SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind")
?Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT CustomerID, CompanyName FROM Customers ORDER BY CustomerID", nwindConn)
?' The Update command checks for optimistic concurrency violations in the WHERE clause.
?custDA.UpdateCommand = New SqlCommand("UPDATE Customers (CustomerID, CompanyName) VALUES(@CustomerID, @CompanyName) " & _
??????????????????????????????????????? "WHERE CustomerID = @oldCustomerID AND CompanyName = @oldCompanyName", nwindConn)
?custDA.UpdateCommand.Parameters.Add("@CustomerID", SqlDbType.NChar, 5, "CustomerID")
?custDA.UpdateCommand.Parameters.Add("@CompanyName", SqlDbType.NVarChar, 30, "CompanyName")
?' Pass the original values to the WHERE clause parameters.
?Dim myParm As SqlParameter
?myParm = custDA.UpdateCommand.Parameters.Add("@oldCustomerID", SqlDbType.NChar, 5, "CustomerID")
?myParm.SourceVersion = DataRowVersion.Original
?myParm = custDA.UpdateCommand.Parameters.Add("@oldCompanyName", SqlDbType.NVarChar, 30, "CompanyName")
?myParm.SourceVersion = DataRowVersion.Original
?' Add the RowUpdated event handler.
?AddHandler custDA.RowUpdated, New SqlRowUpdatedEventHandler(AddressOf OnRowUpdated)
?Dim custDS As DataSet = New DataSet()
?custDA.Fill(custDS, "Customers")
?' Modify the DataSet contents.
?custDA.Update(custDS, "Customers")
?Dim myRow As DataRow
?For Each myRow In custDS.Tables("Customers").Rows
??? If myRow.HasErrors Then Console.WriteLine(myRow(0) & vbCrLf & myRow.RowError)
?Next
Private Shared Sub OnRowUpdated(sender As object, args As SqlRowUpdatedEventArgs)
?If args.RecordsAffected = 0
??? args.Row.RowError = "Optimistic Concurrency Violation Encountered"
??? args.Status = UpdateStatus.SkipCurrentRow
?End If
End Sub
‘C#
?SqlConnection nwindConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind");
?SqlDataAdapter custDA = new SqlDataAdapter("SELECT CustomerID, CompanyName FROM Customers ORDER BY CustomerID", nwindConn);
?// The Update command checks for optimistic concurrency violations in the WHERE clause.
?custDA.UpdateCommand = new SqlCommand("UPDATE Customers (CustomerID, CompanyName) VALUES(@CustomerID, @CompanyName) " +
??????????????????????????????????????? "WHERE CustomerID = @oldCustomerID AND CompanyName = @oldCompanyName", nwindConn);
?custDA.UpdateCommand.Parameters.Add("@CustomerID", SqlDbType.NChar, 5, "CustomerID");
?custDA.UpdateCommand.Parameters.Add("@CompanyName", SqlDbType.NVarChar, 30, "CompanyName");
?// Pass the original values to the WHERE clause parameters.
?SqlParameter myParm;
?myParm = custDA.UpdateCommand.Parameters.Add("@oldCustomerID", SqlDbType.NChar, 5, "CustomerID");
?myParm.SourceVersion = DataRowVersion.Original;
?myParm = custDA.UpdateCommand.Parameters.Add("@oldCompanyName", SqlDbType.NVarChar, 30, "CompanyName");
?myParm.SourceVersion = DataRowVersion.Original;
?// Add the RowUpdated event handler.
?custDA.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);
?DataSet custDS = new DataSet();
?custDA.Fill(custDS, "Customers");
?// Modify the DataSet contents.
?custDA.Update(custDS, "Customers");
?foreach (DataRow myRow in custDS.Tables["Customers"].Rows)
?{
??? if (myRow.HasErrors)
????? Console.WriteLine(myRow[0] + ""n" + myRow.RowError);
?}
protected static void OnRowUpdated(object sender, SqlRowUpdatedEventArgs args)
{
?if (args.RecordsAffected == 0)
?{
??? args.Row.RowError = "Optimistic Concurrency Violation Encountered";
??? args.Status = UpdateStatus.SkipCurrentRow;
?}
}
??????? III.協作設計
??????????? 在你寫期間,你應當鎖定DataSet。
??????? IV.僅當需要的時候才使用COM對象訪問ADO
??????????? ADO.NET設計為大量應用程序最好的解決方案。然而一些應用程序需要只有ADO對象才能提供的功能,比如ADOMD。這種情況下可以使用COM對象訪問ADO,注意的是使用COM對象訪問ADO數據會影響應用程序的執行效率。所以在設計應用程序時,首先應該考慮在使用COM對象訪問ADO數據之前,看看ADO.NET是否就滿足你的設計要求。
??? V.ADO.NET和ADO的比較
??????? I.ADO.NET在ADO設計模型的基礎上演變和發展而來,它并不取代COM程序員的ADO,更多地,它是為.NET程序員訪問相關數據源、XML和應用程序數據設計。ADO.NET支持多樣化的發展要求,包括創建數據庫客戶端和供應用程序、工具、語言、WEB瀏覽器等使用的中間層業務對象。ADO.NET與ADO有許多相似的地方。
??????? II.ADO為COM程序員提供了高效的、強大的與數據庫打交道的各種接口。ADO能得到廣泛的運用是因為它支持任何的自動化控制語言(比如VC、VB和腳本語言等)的調用。ADO基礎上升級而來的ADO.NET提供更好的交互平臺和可升級的數據訪問。在ADO.NET中創建一個新的數據訪問API集能提供較之于ADO接口幾個優越的地方,如下所述:
??????????? ①改進了與XML的結合
??????????? 隨著XML在應用程序中扮演著越來越重要的角色,與XML結合的ADO.NET就應運而生。為了持續和裝載數據以及數據的XML格式,ADO.NET依賴XML在多層之間或客戶機之間遠程傳遞數據。ADO.NET中使用的特殊XML表述形式提供在任何網絡中十分便利地傳輸數據的方法,包括數據安全邊界。同時,ADO.NET使用XML工具執行確認、分級查詢和數據和數據之間的轉換。
??????????? ②綜合.NET框架
??????????? ADO結構如Recordset并不使用常見的設計結構,相反它模擬成一種數據導向。舉例,ADO中的用來導航和得到數據的游標,它的功能性就與其它的比如數組和集合數據結構不同。然而,在ADO.NET中,因為存儲的數據能通過公共的.NET框架結構暴露,包括數組和集合,所以你可以使用一些公共的方法與你的相關數據打交道。
??????????? ③改良對離散業務模型的支持
??????????? ADO使用Recordset提供有限的對離散訪問的支持。ADO.NET介紹一個新的對象DataSet,它作為相關數據的一個公共的、存儲的表現形式,在任何時候都被設計為離散的,它與外部數據并不保持持久的連接,它是包裝、存儲、交換、延續和裝載數據的好方法。也就是說任何對數據的操作都是在本地進行,而不直接與真實的數據庫打交道。
??????????? ④數據訪問行為的控制是清楚的
??????????? ADO中包括在應用程序中并不總是要求和指定的隱含行為會限制應用程序的性能。而在ADO.NET中提供良好的定義和預先的行為、執行和語義要素組件使得你可以在一個高優化的方式下定位到一個普通的情節上。
??????????? ⑤改善設計階段的支持
??????????? ADO源自執行階段隱含的數據信息,而這種信息是基于花費昂貴代價才獲得的元數據。在ADO.NET中的元數據只是在設計階段起一個杠桿作用,從而提供執行階段更好的性能和更好的穩定性。
??????? III.ADO設計
??????????? 為了更好地理解ADO.NET模型和設計思想,回顧一下ADO的概念是有用的。ADO使用一個單一的對象Recordset與所有數據類型打交道。Recordset被用來處理從數據庫返回的只進流數據、翻卷服務器上數據或者翻卷一批存儲結果集。數據上的改變會立即運用到數據庫上或運用到使用樂觀查詢和更新操作的一批數據上。當你創建一個Recordset時你就明確了你所作的任務,Recordset結果行為的改變主要取決于你要求的Recordset參數。因為ADO使用一個單一的能在很多場合使用的Recordset對象,這使得你的應用程序中的對象模型很簡單。然而,也很難寫一個公用的、可預言的和最優化的代碼,那是因為行為、執行和一個單一對象描述的語義要得到改變很大程度上取決于對象是如何創建和對象訪問的是什么數據。
??????? IV.ADO.NET設計
??????????? ADO.NET是考慮到開發者在訪問和使用數據時共同面對的任務和問題而設計。寧可使用一個單一對象執行大量任務,還不如如ADO.NET中指定每個對象的功能性因素去完成對應的每個任務。ADO中的Recordset功能性被分解成ADO.NET中以下的幾個清楚對象:DataReader,提供快速的、只進的和只讀的訪問去查詢結果;DataSet,存儲數據;DataAdapter,在DataSet和數據源之間架起一道橋梁;ExecuteNonQuery,不返回行;ExecuteScalar,返回一個單一值而不是一個行集。下面是一些詳細說明:
??????????? ①只進、只讀數據流
??????????? 應用程序,特別是中間層應用程序,經常要程序化地處理一系列結果,要求在他們讀的時候沒有用戶交互和沒有更新或回滾結果。在ADO中,執行這類數據時使用Recordset的只進游標和只讀鎖。在ADO.NET中,DataReader優化了這種數據的執行性能,它通過提供一個非緩沖、只進和只讀的數據流從數據庫得到數據。
??????????? ②返回單一值
??????????? 在ADO中要得到一個單一值,你需要通過創建一個Recordset—〉讀取結果—〉得到單一值—〉關閉Recordset這樣一個過程。在ADO.NET中你就可以使用Command對象的ExecuteScalar方法不需要額外的操作來獲得單一值。
??????????? ③離散數據訪問
??????????? ADO使用客戶端游標定位離散數據的訪問,而在ADO.NET中DataSet可以很清楚地實現離散數據的訪問。DataSet能從一個多樣的不同的數據源提供一個公有的、完全離散的數據表現形式,是因為DataSet是完全獨立于數據源的。它不管你數據是從數據庫來的,還是從XML文件來的,抑或是從應用程序中得到的。一個簡單的DataSet可以裝載從多個不同數據庫或非數據庫源的數據。然后使用DataRelation在多個表之間建立一個連接,盡管Recordset的MsDataShape提供者可以實現分級結構查詢,但是DataSet提供更高的穩定性處理離散數據。同時DataSet提供以XML文件格式在遠程客戶端和服務器之間傳輸數據。
??????????? ④從數據庫得到數據和更新數據
??????????? ADO.NET提供更好的執行階段性能和可見性。舉例,當使用ADO的Recordset對象進行批更新時,你必須為每個需要改變結果的行使用UPDATE、INSERT或DELETE聲明。ADO產生這些聲明,在執行階段,是需要付出昂貴代價的獲得元數據的。而在ADO.NET中,指定UPDATE、INSERT或DELETE命令就如同自定義業務邏輯(比如一個存儲過程)一樣,你可以使用DataAdapter實現這一切。DataAdapter在DataSet和數據源之間架起一道橋梁。讓你在執行階段就不是如ADO的Recordset一樣需要在數據源中收集元數據信息。從而改善應用程序的執行性能。
??????? V.數據類型
??????????? 在ADO中,所有的結果返回一個Variant數據類型,在ADO.NET中,你可以得到列本身的數據類型。數據類型可以在System.Data.SqlTypes名稱空間定義。
??? W.有關ADO和ADO.NET的詳細介紹,請參考微軟上的資料:ADO.NET for the ADO Programmer
??? 總結:
??? 通過本文,希望與大家共同交流和學習,有不當之處請大家指正,謝謝!
轉載于:https://www.cnblogs.com/feima-lxl/archive/2008/07/12/1241225.html
總結
以上是生活随笔為你收集整理的【转】 ADO.NET最佳实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 总结DevExpress10个使用技巧
- 下一篇: Web前端的优点有哪些?为什么Web前端