很多應用程序都需要與數據庫交互。數據庫是一個數據倉庫,與文本文件和 XML 文件極其相似。可以想見,使用文件來存儲數據并非總是最好的方法,因此大多數應用程序都使用關系數據庫。關系數據庫將數據組織成表、行和記錄。每個表都由行組成,而每行表示一條記錄。行被進一步劃分為列。在同一個表中,每行的列結構都相同。
.NET Framework提供了大量通過ADO.NET與數據庫進行交互的類,而ADO.NET是一個對象庫,讓編寫使用數據庫和LINQ to ADO.NET的應用程序更簡單。
一、ADO.NET
ADO.NET 庫是一個功能豐富的框架,使用它可創建能夠檢索和更新關系數據庫中信息的應用程序。為遵循語言和平臺獨立的理念,ADO.NET是基于數據提供程序的概念設計的。ADO.NET支持的每種數據庫系統(如 SQL Server、Oracle和DB2)都有一個提供程序,它實現了連接到數據庫、執行查詢和更新數據的機制。每個提供程序實現的接口都相同,這讓您能夠編寫獨立于底層數據庫的代碼。
ADO.NET中的一個主要類是DataSet,它表示數據庫的一部分。DataSet不要求始終連接到數據庫,這讓您能夠以離線方式工作。等到需要更新數據庫和 DataSet 時,您才需要將DataSet連接到數據庫,并執行所需的更新。
ps:ADO.NET數據提供程序
ADO.NET數據提供程序封裝了連接到數據庫、執行命令以及檢索結果所需的邏輯。它在底層數據源和應用程序代碼之間提供了一個輕量級層。
當前,.NET Framework自帶了 5個數據提供程序,其中每個都位于一個獨立的命名空間中:
用于SQL Server的數據提供程序(System.Data.SqlClient)
用于OLE DB的數據提供程序(System.Data.OleDb)
用于ODBC的數據提供程序(System.Data.Odbc)
用于Oracle的數據提供程序(System.Data.OracleClient)
EntityClient提供程序(System.Data.EntityClient)
雖然SQL Server和Oracle都支持OLE DB,但是應使用專用于SQL Server或Oracle的提供程序,因為它們針對這些數據庫系統進行了優化。
為此,DataSet必須表示數據庫表以及這些表之間的關系。由數據庫表組成的集合可通過屬性 Tables 訪問,其中每個表都用一個 DataTable 實例表示。屬性 Relations 是一個由DataRelation實例組成的集合,其中每個DataRelation都表示兩個表之間的一種關系。
查詢數據庫時常創建 DataTable,它表示數據庫中的一個表。數據庫表的每列都用一個DataColumn表示,這是通過DataTable的Columns屬性暴露的。DataTable還包含一個Rows屬性,它包含一系列DataRow對象,其中每個DataRow對象都對應于數據庫表中的一行。
如果將DataSet視為數據庫的抽象,便需要使用某種方式在DataSet和底層數據庫之間搭建橋梁。這是使用DataAdapter完成的,它用于在數據源和DataSet之間交換數據,這種交換主要是通過方法Fill和Update完成的。這樣做帶來的好處是,一個DataSet可表示多個數據庫的表。
ps:使用DataReader進行只讀訪問
DataSet和DataAdapter都支持與數據庫的雙向交互,能夠讀取和寫入數據。如果只需要讀取數據(這種情況很常見),那么可以創建DbDataReader或其派生類(如SqlDataReader)的實例。要創建DataReader,最簡單的方式是使用DbCommand的方法ExecuteReader。
DbDataReader 能夠以只讀、只進的方式連續訪問一個表集合,這常被稱為滅火水龍帶游標(fire hose cursor),因為這類似于訪問快速、只進、連續不斷的數據流中的數據,而這種數據流像滅火水龍帶流出的水流:流量大、只進、連續不斷。
因此,它們屬于輕量級對象,最適合用于給控件填充數據,然后斷開數據庫連接。
最后,需要使用DbConnection表示到數據源的連接,并使用DbCommand表示數據庫命令,如SQL語句或存儲過程。創建DbConnection后,便可在眾多DbCommand之間共享它。
每個數據提供程序都提供了DbConnection和DbCommand的子類,并專門針對相應的數據庫系統對這些子類進行了定制。例如,用于 SQL Server 的數據提供程序提供了SqlConnection和SqlCommand類。
ps:必不可少的引用
要使用 ADO.NET,需要確保項目包含到程序集 System.Data 的引用,并包含要使用的數據提供程序對應的命名空間。
如下示例演示了如何執行查詢,其中使用了ADO.NET和用于SQL Server的數據提供程序提供的類:
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString = @"Integrated Security=SSPI;database=AdventureWorksLT2008;server=(local)\SQLEXPRESS";
try
{
using (SqlCommand command = new SqlCommand())
{
command.Connection = connection;
command.CommandText = @"SELECT *
FROM [AdventureWorksLT2008].[SalesLT].[Customer]
WHERE [CompanyName] = 'A ZZ Home'";
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine("{0} {1} {2}",
reader.GetString(2),
reader.GetString(3),
reader.GetString(5));
}
connection.Close();
}
}
catch (SqlException e)
{
Console.WriteLine("An error occurred: {0}", e.Message);
}
}
為確保數據庫命令和數據庫連接占用的資源得以釋放,且連接得以關閉,最好將它們放在一條using語句中。
ps:連接池
大多數數據提供程序都支持連接池。可將連接池視為一組可用的數據庫連接,應用程序需要連接時,提供程序將從連接池中提取下一個可用的連接。應用程序關閉連接時,連接將歸還給連接池,可供下一個需要連接的應用程序使用。
二、LINQ to ADO.NET
LINQ to ADO.NET實際上是 3項獨立的技術,讓您能夠與關系數據庫交互。下圖說明了這些LINQ to ADO.NET技術之間的關系,以及這些技術與其他支持LINQ的數據源和高級編程語言(如C#)之間的關系。
2.1 LINQ to DataSet
LINQ to DataSet建立在現有ADO.NET架構的基礎之上,能夠輕松、快捷地創建查詢,以查詢存儲在 DataSet 中的數據。它并非要取代 ADO.NET 在應用程序中的位置,而是旨在讓您能夠使用LINQ語法編寫查詢,并填補DataSet有限的查詢功能留下的空白。
要使用LINQ to DataSet,必須首先填充DataSet。加載數據后,便可使用LINQ to Objects和LINQ to XML的方法進行查詢。這些查詢可針對DataSet中的一個表,也可針對多個表(通過使用查詢運算符Join和GroupJoin)。
從功能上說,如下程序與上一個示例等價,但是使用的是LINQ to DataSet查詢。該程序清單首先創建了一個 SqlDataAdapter,然后使用數據填充 DataSet 實例。這些代碼對ADO.NET和 LINQ to DataSet來說都是必不可少的,但是通過對 DataTable執行擴展方法AsEnumerable,可對數據執行標準LINQ查詢。
string connectionString = @"Integrated Security=SSPI;database=AdventureWorksLT2008;server=(local)\SQLEXPRESS";
string selectSQL = @"SELECT *FROM [AdventureWorksLT2008].[SalesLT].[Customer]";
SqlDataAdapter adapter = new SqlDataAdapter(selectSQL, connectionString);
DataSet ds = new DataSet();
adapter.Fill(ds);
foreach (var customer in ds.Tables[0].AsEnumerable().Where(row => row.Field<string>("CompanyName") == "A ZZ Home"))
{
Console.WriteLine("{0} {1} {2}",
customer.Field<string>("Title"),
customer.Field<string>("FirstName"),
customer.Field<string>("LastName");
}
就像LINQ to XML添加了專門用于XML的擴展一樣,LINQ to DataSet也添加了專門針對DataSet的擴展。這些擴展使得查詢DataRow對象集合更容易,能夠比較DataRow對象以及直接訪問DataRow的列值。
ps:必不可少的引用
要使用LINQ to DataSet,需確保項目包含指向如下程序集的引用。
(1)System.Core。
(2)System.Data。
(3)System.Data.DataSetExtensions。
(4)System.Data.Common 或 System.Data.SqlClient,這取決于將如何連接到數據庫。
另外,還需包含命名空間System.Linq和System.Data。
2.2 LINQ to SQL
LINQ to SQL讓您能夠直接編寫針對SQL數據庫的查詢,且使用的語法與查詢內存中的集合和其他LINQ數據源時相同。ADO.NET將數據庫映射到由DataSet表示的概念數據模型,而LINQ to SQL將數據庫映射到應用程序代碼中的對象模型。應用程序執行時,LINQ to SQL將把LINQ查詢轉換為SQL查詢,隨后這種查詢被發送給數據庫執行。結果返回后,LINQ to SQL將它們轉換為數據模型對象。
要使用LINQ to SQL,必須首先創建表示數據庫的對象模型。為此,有以下兩種方式。
- 使用對象關系設計器(O/R設計器):它是Visual Studio 2010的一部分,提供了豐富的用戶界面,用于從現有數據庫創建對象模型。O/R設計器只支持SQL Server學習版數據庫以及SQL Server 2000、2005和 2008數據庫。
- 使用命令行工具 SQLMetal:僅當使用的是大型數據庫或 O/R 設計器不支持的數據庫時,才應使用它。
決定如何生成對象模型后,需要決定要生成哪種類型的代碼。使用O/R設計器時,可生成C#源代碼來提供基于屬性的映射。如果使用的是SQLMetal命令行工具,就可生成包含映射元數據的外部XML文件。
ps:創建對象模型
實際上還有第三種創建對象模型的方式,即在代碼編輯器中手動編寫對象模型。不推薦這樣做,因為容易出錯,尤其是在創建表示現有數據庫的對象模型時。
可使用代碼編輯器來修改或精制O/R設計器或命令行工具SQLMetal生成的代碼。
創建對象模型后,便可通過編寫LINQ查詢在應用程序中使用它。如下示例包括定義 DataContext、執行查詢和顯示結果:
DataContext dataContext = new DataContext(@"Integrated Security=SSPI;database=AdventureWorksLT2008;server=(local)\SQLEXPRESS");
Table<Customer> Customers = dataContext.GetTable<Customer>();
IQueryable<Customer> query =
from customer in customers
where customer.CompanyName == "A ZZ Home"
select customer;
foreach(Customer customer in query)
{
Console.WriteLine("{0} {1} {2}",
customer.Title,
customer.FirstName,
customer.LastName);
}
ps:必須包含的引用
要使用LINQ to SQL,務必在項目中包含指向如下程序集的引用。
(1)System.Core。
(2)System.Data.Linq。
另外,還需包含命名空間System.Linq和System.Data.Linq。
首先,必須通過 DataContext 在對象模型和數據庫之間建立連接。然后,創建了一個Table<T>,它充當要查詢的表。可認為DataContext類似于DBConnection,而Table<T>類似于DataTable,雖然實際上并非如此。最后,定義并執行查詢。
只需編寫一個LINQ查詢并執行它,就可選擇數據(也叫投影)。LINQ to SQL還能夠添加、修改和刪除數據庫中的數據。當使用LINQ to SQL執行修改操作時,修改的都只是本地緩存。只有對DataContext實例調用方法SubmitChange后,所做的修改才會發送到數據庫。
要在數據庫中添加記錄,只需創建合適數據模型對象的一個新實例,再調用Table<T>的方法 InsertOnSubmit。