前言
.NET Framework自2002年發布以來,已經歷了十來個年頭。相應的,.NET平台上的數據訪問技術也在不斷發展,從最基礎的ADO.NET,到SqlHelper簡單幫助類,到DAAB(Data Access Application Block),再到LINQ,最終演變為現在微軟主推的Entity Framework,一路走來可算不易。同時,一些優秀的開源項目也不斷涌現,如從Java平台移植過來的NHibernate;源自.NET平台的Castle ActiveRecord;博客園網友原創的NBear;還有我曾經在一個項目中用過的非主流技術Grovekit等等。下圖對.NET平台的一些主流數據訪問技術進行了一個整體展示:
圖1 .NET平台數據訪問技術匯總圖
本文接下來將對ADO.NET、SqlHelper、DAAB、NHibernate、LINQ和Entity Framework幾種技術在技術架構和使用方法上作簡要介紹,並使用這些技術實現對同一個數據對象的增刪改查功能,最后對這些技術進行對比分析。希望本文對你了解.NET平台的數據訪問技術有所幫助。
ADO.NET
ADO.NET是.NET平台數據訪問的基礎,接下來要討論的每一種技術都是建立在他的基礎上的。雖然我不提倡凡是都直接用ADO.NET,因為那樣會讓你的數據層代碼量激增;但是作為.NET程序員,理解ADO.NET技術的主要內容是絕對必要的。
ADO.NET是由ASP時代的ADO(ActiveX Data Objects)演變而來,用於應用程序與數據源進行交互。ADO.NET的技術架構如下圖所示:
圖2 ADO.NET技術架構圖
如上圖所示,ADO.NET技術由兩部分組成,一部分是.NET Framework數據提供程序,它主要包含Connection對象(用於與數據源進行連接)、Command對象(用於執行數據CRUD命令)、DataReader(以只進的方式一次讀取一條數據庫記錄)對象和DataAdapter對象(通過SelectCommand、InsertCommand、UpdateCommand和DeleteCommand命令實現數據源與DataSet的適配)的實現。.NET Framework默認實現了SQL Server(System.Data.SqlClient)、Oracle(System.Data.OracleClient)、OLEDB(System.Data.OleDb)和ODBC(System.Data.Odbc)數據源提供程序。也就是說,對上述這幾種數據源可以直接進行訪問,不需要實現數據提供程序接口;而訪問其他的數據源,如MySQL、DB2、其他國產數據庫等,均需實現數據提供程序接口,當然,這項工作一般是由數據庫廠商來完成,作為程序員,我們只需要通過某種途徑獲得該實現庫並引用就行了。
另一部分是DataSet對象,它獨立於任何數據源,即可以通過DataAdapter對象將數據庫中的數據適配成DataSet對象,也可以將一個XML數據文件序列化為DataSet對象。DataSet對象與前面提到的DataReader對象有很大的不同,DataReader對象是只進且只讀的,並且不駐留內存,因此讀取數據的過程中需要始終保持數據庫連接;DataSet對象以DataTable對象集合的形式駐留內存,因此數據一旦加載,便可斷開數據源連接,這一點很關鍵,因為數據連接在程序的運行時往往是很寶貴的。
ADO.NET的使用大致可以分為配置數據庫連接、連接數據庫、執行數據操作命令和關閉連接這幾個步驟:
一、配置數據庫連接
無論是B/S應用程序配置文件Web.config,還是其他應用程序(包括)配置文件App.config,均提供了數據源連接字符串配置節。我們只需在配置文件中配置數據庫連接信息,就能在程序中方便的訪問數據庫連接字符串信息,從而輕松支持程序運行時更改數據庫連接信息。
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <connectionStrings> 4 <add name="AdoNet" connectionString="Data Source=localhost;User Id=sa;Password=11111111;Initial Catalog=Apollo;MultipleActiveResultSets=True;" providerName="System.Data.SqlClient"/> 5 </connectionStrings> 6 </configuration>
當然,該步驟並不是必須的,在我曾經作為外援參加的某國內還算知名軟件公司的一個項目中,就將數據庫配置信息硬編碼到DAL類庫代碼中的。
二、連接數據庫
ADO.NET提供了Connection對象來進行數據庫連接對象的創建,並建立數據庫連接。需要注意的是,盡可能晚的打開連接是通常需要遵循的行為准則。
1 protected readonly string CONNECTION_STRING = ConfigurationManager.ConnectionStrings["AdoNet"].ConnectionString; 2 … 3 using (var connection = new SqlConnection(CONNECTION_STRING)) 4 { 5 connection.Open(); 6 … 7 }
三、執行數據操作命令
ADO.NET提供了Command對象來執行數據操作命令。Command對象的ExecuteNonQuery()方法用於執行命令並返回受影響的行數,該方法常用於更新、刪除數據;ExecuteScalar()方法用於執行命令並返回查詢所影響的結果集中的第一行第一列,該方法常用於添加數據並返回自動生成的自增長標識列值;ExecuteReader()用於執行命令並返回IDataReader類型的數據,該方法常用於以只讀步進的方式進行數據查詢。需要注意的是,ExecuteReader()方法讀取數據全程需要保持數據庫連接,如果想以斷開數據庫連接的方式一次性將數據查詢並放入內存中隨時使用,請使用DataAdapter對象將數據源適配為DataSet對象。
1 using (var command = connection.CreateCommand()) 2 { 3 command.CommandText = SQL_INSERT; 4 command.Parameters.Add(new SqlParameter("@ID", user.ID)); 5 command.Parameters.Add(new SqlParameter("@Name", user.Name)); 6 7 return command.ExecuteNonQuery() > 0; 8 }
四、關閉連接
當數據操作命令執行完成后,請千萬記得關閉數據庫連接,盡早關閉數據庫連接通常也是我們需要遵守的行為准則。Connection對象提供了Close()方法供顯示調用關閉連接;同時,Connection對象也實現了IDisposiable接口,使用using語句建立數據連接的情況下,關閉連接的問題就不用再費心了,而由CLR將代勞。
SqlHelper
我是在研讀PetShop源碼時第一次接觸到了SqlHelper的。嚴格地說,SqlHelper算不上一種數據訪問技術,而僅僅是將ADO.NET技術進行了簡單的封裝,讓它使用起來更簡便些。但是不要因此而小看SqlHelper,它可以有效的提高你的編碼效率,顯著減少數據訪問層代碼量,並使之更易維護。SqlHelper僅適用於SQL Server數據庫,微軟提供了OdbcHelper、OleDbHelper和XmlHelper等封裝類,實現對其他數據源的快速訪問。
SqlHelper是一個靜態封閉類,它提供了ExecuteNonQuery()、ExecuteScalar()、ExecuteReader()和ExecuteDataSet()靜態方法,使得對數據的訪問不再像使用ADO.NET那樣需要一二三四步了,而是一步到位:
var result = SqlHelper.ExecuteNonQuery(CONNECTION_STRING, CommandType.Text, sql, parameters);
DAAB
談DAAB之前,需要先談談Enterprise Library(微軟企業庫)。Enterprise Library是微軟Patterns & Practices團隊為企業級應用開發人員提供的一個組件庫,最新的版本為2010年4月發布的Enterprise Library 5.0,該版本最高支持VS2010開發環境 + .NET Framework 4.0框架。
Enterprise Library由一系列應用程序塊所組成,包括數據訪問(Data Access Application Block)、異常處理(Exception Handling Application Block)、日志(Logging Application Block)、數據驗證(Validation Application Block)、依賴注入容器(Unity Application Block)、緩存(Caching Application Block)、密碼應用(Cryptography Application Block)和安全(Security Application Block)。可以看出,Enterprise Library所包含的內容基本涵蓋了企業應用開發所需涉及的技術的方方面面。
作為企業訪問不可或缺的一部分,DAAB(Data Access Application Block)在Enterprise Library中的地位非常重要,同時也是最常用的一個應用程序塊。它的技術架構如下圖所示:
圖3 DAAB技術架構圖
Enterprise Library提供了一系列的工具,以幫助開發人員更方便的使用各程序塊。DAAB的使用也非常簡單,這里就不介紹相關工具的使用了,而是直接編碼。
一、配置連接
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <configSections> 4 <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" /> 5 </configSections> 6 <dataConfiguration defaultDatabase="AdoNet" /> 7 <connectionStrings> 8 <add name="AdoNet" connectionString="Data Source=localhost;User Id=sa;Password=11111111;Initial Catalog=Apollo;MultipleActiveResultSets=True;" providerName="System.Data.SqlClient"/> 9 </connectionStrings> 10 </configuration>
二、創建數據庫對象
var database = DatabaseFactory.CreateDatabase();
三、數據持久化
使用Database對象的實例方法,進行數據持久化操作:
database.ExecuteNonQuery(CommandType.Text, SQL_INSERT);
NHibernate
NHibernate是由Java平台的Hibernate遷移到.NET平台的一個優秀的開源項目。雖然NHibernate在.NET平台上的地位遠沒有其老大哥Hibernate那么顯赫,但是該項目自創建以來一直十分活躍,版本更新頻繁,目前已更新至3.3.3版本。不少的商業軟件選擇NHibernate,說明其在.NET平台上還是有市場的。我曾經在一個項目中選擇了Spring.NET + NHibernate的組合來實現依賴注入、面向方面編程和數據持久化,完全能應付企業級軟件的開發需求。
NHibernate采用非侵入式的方式進行對象-關系映射,從而成就了其輕量級ORM技術的美名,這一點相信成為很多架構師鍾愛他的重要理由。NHibernate技術架構如下圖所示:
圖4 NHibernate技術架構圖
NHibernate的使用大致可以分為配置信息、編寫映射文件和持久化數據幾個步驟:
一、配置信息
NHibernate需要對配置文件作以下配置方可使用:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <configSections> 4 <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" /> 5 </configSections> 6 7 <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2"> 8 <session-factory> 9 <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property> 10 <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property> 11 <property name="connection.connection_string">Data Source=localhost;User Id=sa;Password=11111111;Initial Catalog=Apollo;MultipleActiveResultSets=True;</property> 12 <mapping assembly="Apollo.Blog.EF.Chapter1.Domain" /> 13 </session-factory> 14 </hibernate-configuration> 15 </configuration>
二、映射文件
1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Apollo.Blog.EF.Chapter1.Domain" namespace="Apollo.Blog.EF.Chapter1.Domain"> 3 <class name="User" table="`User`" lazy="false"> 4 <id name="ID"> 5 <generator class="guid" /> 6 </id> 7 <property name="Name" type="string" not-null="true" /> 8 </class> 9 </hibernate-mapping>
三、數據持久化
1 var sessionFactory = new Configuration().Configure().BuildSessionFactory(); 2 using (var session = sessionFactory.OpenSession()) 3 { 4 session.Save(user); 5 session.Flush(); 6 }
LINQ
LINQ(Language Integrated Query,語言集成查詢)是一組用於C#和VB.NET語言的擴展,它允許編寫C#或者VB.NET代碼以查詢數據庫相同的方式操作內存數據。LINQ提供提供了豐富的類似SQL的查詢語法,功能強大且容易上手。
LINQ技術通過提供程序擴展,可以實現對任何數據源的訪問。常用的提供程序有LINQ to SQL、LINQ to XML、LINQ to Objects、LINQ to Entities、LINQ to Datasets、LINQ to ADO.NET。LINQ技術架構如下圖所示:
圖5 LINQ技術架構圖
LINQ的使用大致包括連接配置、關系映射、上下文環境定義和數據持久化四步:
一、連接配置
LINQ的數據庫連接配置與ADO.NET一樣:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <connectionStrings> 4 <add name="AdoNet" connectionString="Data Source=localhost;User Id=sa;Password=11111111;Initial Catalog=Apollo;MultipleActiveResultSets=True;" providerName="System.Data.SqlClient"/> 5 </connectionStrings> 6 </configuration>
二、侵入式關系映射
1 [Table(Name = "User")] 2 public class LinqUser : User 3 { 4 [Column(IsPrimaryKey = true)] 5 public override Guid ID { get; set; } 6 7 [Column] 8 public override string Name { get; set; } 9 }
三、上下文環境
1 internal class LinqContext : DataContext 2 { 3 public LinqContext(string connection) 4 : base(connection) 5 { 6 } 7 8 public LinqContext(IDbConnection connection) 9 : base(connection) 10 { 11 } 12 13 public Table<LinqUser> Users 14 { 15 get { return this.GetTable<LinqUser>(); } 16 } 17 }
四、數據持久化
1 using (var db = new LinqContext(CONNECTION_STRING)) 2 { 3 db.Users.InsertOnSubmit(user); 4 db.SubmitChanges(); 5 }
Entity Framework
Entity Framework的全稱為ADO.NET Entity Framework,是在ADO.NET上層實現的ORM封裝,其技術架構如下圖所示:
圖6 Entity Framework技術架構圖
Entity Framework的使用與LINQ一樣,分為連接配置、關系映射、上下文環境定義和數據持久化四步:
一、連接配置
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <connectionStrings> 4 <add name="EntityFramework" providerName="System.Data.EntityClient" 5 connectionString="provider=System.Data.SqlClient; 6 provider connection string=" 7 Data Source=localhost; 8 User Id=sa; 9 Password=11111111; 10 Initial Catalog=Apollo; 11 Integrated Security=False; 12 MultipleActiveResultSets=True;"; 13 metadata=..\..\..\Apollo.Blog.EF.Chapter1.Domain\Edmx\Chapter1.ssdl| 14 ..\..\..\Apollo.Blog.EF.Chapter1.Domain\Edmx\Chapter1.csdl| 15 ..\..\..\Apollo.Blog.EF.Chapter1.Domain\Edmx\Chapter1.msl"/> 16 </connectionStrings> 17 </configuration>
二、關系映射
Entity Framework是通過定義概念模型(CSDL)、物理模型(SSDL)及兩者的映射關系(MSL),實現對象與關系映射的。
SSDL映射文件內容如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <Schema Namespace="Chapter1.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2005" 3 xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl" 4 xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"> 5 <EntityContainer Name="Chapter1StoreContainer"> 6 <EntitySet Name="User" EntityType="Chapter1.Store.User" store:Type="Tables" Schema="dbo" /> 7 </EntityContainer> 8 9 <EntityType Name="User"> 10 <Key> 11 <PropertyRef Name="ID" /> 12 </Key> 13 <Property Name="ID" Type="uniqueidentifier" Nullable="false" /> 14 <Property Name="Name" Type="nvarchar" Nullable="false" MaxLength="40" /> 15 </EntityType> 16 </Schema>
CSDL映射文件內容如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <Schema Namespace="Chapter1" Alias="Self" 3 xmlns="http://schemas.microsoft.com/ado/2008/09/edm" 4 xmlns:cg="http://schemas.microsoft.com/ado/2006/04/codegeneration" 5 xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" 6 xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation"> 7 <EntityContainer Name="Chapter1" annotation:LazyLoadingEnabled="true"> 8 <EntitySet Name="User" EntityType="Chapter1.User" /> 9 </EntityContainer> 10 11 <EntityType Name="User"> 12 <Key> 13 <PropertyRef Name="ID" /> 14 </Key> 15 <Property Name="ID" Type="Guid" Nullable="false" /> 16 <Property Name="Name" Type="String" Nullable="false" /> 17 </EntityType> 18 </Schema>
MSL映射文件內容如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2008/09/mapping/cs"> 3 <Alias Key="Cdm" Value="Chapter1" /> 4 <Alias Key="Storage" Value="Chapter1.Store" /> 5 <EntityContainerMapping CdmEntityContainer="Chapter1" StorageEntityContainer="Chapter1StoreContainer"> 6 <EntitySetMapping Name="User"> 7 <EntityTypeMapping TypeName="Cdm.User"> 8 <MappingFragment StoreEntitySet="User"> 9 <ScalarProperty Name="ID" ColumnName="ID" /> 10 <ScalarProperty Name="Name" ColumnName="Name" /> 11 </MappingFragment> 12 </EntityTypeMapping> 13 </EntitySetMapping> 14 </EntityContainerMapping> 15 </Mapping>
三、上下文環境
Entity Framework提供了ObjectContext上下文環境,以對數據對象進行持久化操作。通過繼承它,可以方便的實現自定義數據對象的訪問。
1 internal class EntityContext : ObjectContext 2 { 3 public EntityContext() 4 : this("name=EntityFramework") 5 { 6 } 7 8 public EntityContext(string connectionString) 9 : base(connectionString, "Chapter1") 10 { 11 this.Users = CreateObjectSet<User>(); 12 } 13 14 public ObjectSet<User> Users { get; set; } 15 }
四、數據持久化
1 using (var db = new EntityContext()) 2 { 3 db.Users.AddObject(user); 4 db.SaveChanges(); 5 }
總結
本文就.NET平台常用的幾種數據訪問技術的技術框架及使用方法進行了概要闡述,並針對每種技術提供一個數據對象的CRUD功能實現。考慮到上述各種技術的形態差異較大,在此就不對各技術間的異同作對比了,后續將對Entity Framework與其它常用的ORM技術做詳細對比論述。下一篇文章將介紹如何使用Entity Framework快速開發數據訪問應用程序。