.NET環境下的ORM技術研究

作者/陳弼暐

[發表日期:2011/4/29]



簡介ORM技術

60年代物件導向程式(Object-oriented programming, OOP)被提出後,物件導向逐漸成為所有程式設計師的共通語言。在這幾十年間,物件導向技術逐漸成熟,舉凡對網路的通訊協定、檔案存取、XML文件等操作都已經物件化,讓IT產業的貢獻者能以更接近自然語言的方式討論與創新各種不同領域的資訊系統。

Object-Relational Mapping (ORM, O/RM, or O/R mapping)一詞就是將關聯式資料庫映射至物件導向的資料抽象化技術。其理念是將資料庫的內容映射為物件,讓程式開發人員可以用操作物件的方式對資料庫進行操作,而不直接使用SQL語法對資料庫進行操作。讓程式設計師不用管底層的資料庫系統是哪種廠牌或哪個版本的資料庫(如:SQL Server、Oracle、DB2、MySQL、Sybase、DBMaker…),僅須用同一套語法撰寫存取資料庫的邏輯。當底層資料庫的實作品變更時,由於程式設計師並不直接對資料庫進行操作,因此程式內容幾乎不用修改,也就是降低了物件導向程式與資料庫之間的耦合關係。

表一列出物件導向觀念與關連式資料庫之間本質上的不同處(object-relational impedance mismatch) (1)。


《表一》物件導向與關聯式資料差異表


由於有上述這些本質上的不同,因此必須有一套映射方法,將關聯資料映射為物件,目前在.NET平台上的ORM框架的主流有Open Source的NHibernate與Microsoft的ADO.NET Entity Framework。兩者皆是透過XML檔案定義映射方式,在執行時,ORM框架會將對物件進行的操作自動轉為SQL語法,並對資料庫進行查詢。由於查詢語法是由框架自動產生,因此效能較差是最主要的缺點。於兩個ORM框架的官方網站中,皆有改進效能的建議作法,Entity Framework請參考 (2);NHibernate請參考 (3)。

ORM與傳統ADO.NET的比較

用ORM技術與ADO.NET技術來開發資料庫存取層程式是兩種相當不同的方式,以下用Microsoft SQL Server 2008 R2、Entity Framework 4、ADO.NET為例,分別以常用的資料操作方式來比較兩種技術上的程式撰寫差異,包含CRUD(Create、Read、Update、Delete)與交易控制。

一、CRUD(Create, Read, Update, Delete)

假設我們所使用的關聯式資料庫系統為Microsoft SQL Server 2008 R2,以下將比較使用ADO.NET的方式與ORM的方式,ORM的方式將會以Entity Framework為例。

  • 初始化


  • ADO.NET


    ORM (Entity Framework)





    ORM框架在處理資料底層連線上有兩種方式:
    1.自動處理底層資料連線與釋放,設計師可以不用去管資料庫是否有連線,ORM框架會在真正需要到資料庫擷取資料的時候再去進行連線。
    2.設計師可以手動進行資料庫連線與釋放,如上面第二種程式撰寫方式所述。

  • Read


  • ADO.NET


    ORM (Entity Framework)


    在此處Entity Framework有提供命名為Entity SQL的查詢語言。這是一種類似SQL語法的查詢方式,不過這裡查詢的對象是物件,而不是資料庫。

    NHibernate也有提供命名為HSQL的查詢語言,同樣是針對物件進行查詢。以此種方式進行查詢會有比較高的自由度。但由於是字串格式的原因,在程式編寫過程中,IDE無法自動告知程式設計師這段Entity SQL或HSQL的語法是否有問題。在非常要求效能的情況底下,建議可以使用LINQ的方式查詢,.NET提供了一個使用已編譯的LINQ查詢方式,當查詢語法非常類似,只有參數不同的情況下適合使用。其運作方式是,第一次使用時會先將LINQ語法編譯過,接著僅透過傳遞不同參數來進行查詢。

  • Create


  • ADO.NET


    ORM (Entity Framework)


    在此處,使用Entity Framework新增資料與在容器中新增物件的方式類似。與ADO.NET不同的地方在於新增完成後資料不會立即更新到資料庫,但程式在查詢時就可以找到這一筆新增的資料,並可以對這筆資料進行操作。直到SaveChanges()方法被叫用時,資料庫內容才會進行更新。

  • Update


  • ADO.NET


    ORM (Entity Framework)


    使用Entity Framework進行資料更新時,首先要定位到要進行更新的資料物件,再對該物件進行資料更新。而ADO.NET則可直接透過SQL對資料庫進行下命令更新動作。

  • Delete


  • ADO.NET


    ORM (Entity Framework)


    與Update相同,Entity Framework一樣得先定位到要刪除的資料物件,再透過實體物件或資料物件的DeleteObject方法進行刪除。同樣的,ADO.NET可直接透過SQL對資料庫進行操作。

    因此,顯而易見的是上述新增、更新、刪除命令若在前端進行耗時工作時,可能會有資料版本一致性的問題,關於此議題我們會在下一個章節說明。

    另外,透過Entity Framework可以經由設定資料物件之間的關聯性。關聯性建立起來後,當單一資料被更新了,關聯的資料表也可以一併更新,以維持資料庫內容的一致性。
    二、交易控制

    當同時要更動分散於各資料表的多筆資料或是對資料庫下達多個命令已完成一項行為時,可能會因為各種關係導致資料更新過程時發生例外狀況,此時做到一半的行為會被迫中止,而導致資料庫的內容不正確。

    資料庫交易的觀念就是定義對資料庫的一項無法切割(Atomic)的工作。當工作失敗時,行為不應該被完成一部分,而是必須被當成是整個行為都失敗。資料庫內不應該存在做到一半的資料殘骸。因此交易的目的是在保持資料庫內容的完整性與一致性。

    ADO.NET進行交易時,是透過Connection.BeginTransaction()方法取得Transaction物件,並透過Transaction物件的Commit()方法進行完成交易。Entity Framework則可透過System.Transaction.TransactionScope物件進行交易控制。以下分別是ADO.NET與Entity Framework進行交易控制的範例。

    ADO.NET


    ORM (Entity Framework)


    上述兩節中,我們分別探討了ADO.NET直接存取資料庫的方式與Entity Framework用操作物件的方式來存取資料庫的方式。如果就以上述的範例來看,撰寫程式的方法上好像程式設計師沒得到太多好處,雖然不必再去寫SQL指令了,但是為了快速操作資料的目的,取而代之的是LINQ與Entity SQL(或HSQL)。

    但是在實務上,當專案範圍中若有使用到多個異質資料庫,程式設計師可能得為不同資料庫撰寫不同語法的查詢時,使用ORM技術來撰寫程式時,程式設計師就不用再煞費苦心的去學每種資料庫的不同SQL指令寫法。

    ORM架構的相關議題

    一、安全性考量

    由於NHibernate與Entity Framework均提供一種類似SQL語法的物件查詢語法,都是讓設計師可以透過組合字串的方式產生查詢語法。因此與原本的ADO.NET相同,也有SQL Injection的安全性問題。當駭客透過網頁或單機應用程式的輸入欄位中輸入了物件查詢語法時,物件查詢語法就有可能會查詢出這個使用者沒有權限取得的資料。因此,微軟於MSDN網站上建議 (4):

    1.不要將使用者輸入的內容結合至物件查詢語法中。
    2.使用LINQ操作物件,可避免SQL Injection的問題。

    二、資料一致性

    由於ORM技術是將資料映射成為物件,設計師操作的是物件,而不是資料本身。因此在多人同時使用的環境中就會產生資料版本不一致的問題。

    於Entity Framework中,預設是不處理資料一致性的問題。不論目前程式手中的物件是否和資料庫實際內容一致都可以對資料庫進行更新。若要進行檢查,Entity Framework的作法是在資料物件中新增一個Status屬性,在每次要異動資料庫內容時,Entity Framework都會自動去檢查資料版本,如果不同,程式就會接收到Exception。在Entity Framework中,不處理一致性問題的模式稱為Optimistic(樂觀) Concurrency。而處理一致性的模式稱為Fixed Concurrency。

    相對於Entity Framework,NHibernate對於資料一致性的做法比較有彈性。基本上分為Optimistic(樂觀) Concurrency與Pessimistic(悲觀) Concurrency。

    使用悲觀模式的時候,設計師可於程式中鎖定資料,被鎖定的資料在被解鎖之前都不允許被存取。樂觀模式則是資料可被讀取,當資料要進行寫入時,NHibernate會對要變更的資料進行檢查,如果資料版本不一致的話,程式就會接收到Exception。因此就兩個ORM產品的比較上,在資料一致性的處理模式上,NHibernate提供了比較有彈性的控制機制。

    三、設計因素

    所有ORM產品都有一個共通缺點:當查詢的資料非常巨大時,由於ORM層會產生巨量的物件,會耗用大量的空間,處理效能也會變差。因此在設計上,針對大量資料的處理,必須考量:

    1.若是大量資料查詢,查詢結果重複使用的程度。由於Entity Framework與NHibernate都有Cache的機制,重複執行的查詢會直接從Cache中讀取,效能上比較優異。如果大量資料查詢,且查詢幾乎不會重複的情況,使用ORM進行查詢時會有效能不彰的情況。

    2.大量資料批次寫入會導致記憶體不足的情況,嚴重的話可能會造成系統當機。因此若有大量資料的需要進行批次寫入時可以將整批資料切割成較小的數批,再分次進行寫入。或是在這種情況下可以繞過ORM架構改用ADO.NET提供的SqlBulkCopy物件,會有較佳的效能 (5)。

    結語

    Object-Relational Mapping技術的初衷是讓程式設計者可以透過操作物件的方式來操作儲存於關聯式資料庫內的資料。由於程式設計者不一定是資料庫專家,且市面上許許多多的資料庫實作品,每一種實作品對於SQL語法的撰寫方式又不盡相同,甚至可能一個應用程式中同時要整合異質性的關聯式資料庫,這對程式設計者來說是相當痛苦的一件事情。因此,可以使用統一的語言來操作資料內容,用設計者最熟悉的物件操作方式來操作資料,對設計者來說無疑是再好不過了。

    再者,以產品的角度來看,客戶的環境可能是各式各樣的關聯式資料庫系統,在設計產品時使用ORM技術,就可以省去不少實作各種不同資料庫存取層所必須花費的工。

    因此,ORM的概念的確是相當好的。但以目前的技術來看,ORM技術包含效能上與功能面都還有許多瓶頸,包含:

    1.大量資料存取時產生的效能問題
    2.不支援Data Cube
    3.複雜的實體物件映射配置檔案

    微軟將其所開發的ORM框架-Entity framework視為重點開發項目,距離包含在.Net Framework 4 一起推出的Entity framework 4至今不到一年, Entity framework 4.1RC版已經隨Visual Studio 2010 SP1一起推出了 (6)。而跨足Java與.NET平台的NHibernate也於前不久推出NHibernate 3.1.0.GA (7)。在兩個ORM產品大廠的推波助瀾下,相信ORM技術將會有璀璨非凡的前景。

    參考資料

    1.AMBLER, SCOTT W., THE OBJECT-RELATIONAL IMPEDANCE MISMATCH, AMBYSOFT, HTTP://WWW.AGILEDATA.ORG/ESSAYS/IMPEDANCEMISMATCH.HTML.

    2.MSDN LIBRARY, PERFORMANCE CONSIDERATIONS (ENTITY FRAMEWORK), MICROSOFT, HTTP://MSDN.MICROSOFT.COM/EN-US/LIBRARY/CC853327.ASPX.

    3.CHAPTER 21. IMPROVING PERFORMANCE. HIBERNATE COMMUNITY DOCUMENTATION, RED HAT, INC., HTTP://DOCS.JBOSS.ORG/HIBERNATE/CORE/3.6/REFERENCE/EN-US/HTML/PERFORMANCE.HTML

    4.MSDN LIBRARY, SECURITY CONSIDERATIONS (ENTITY FRAMEWORK), MICROSOFT, HTTP://MSDN.MICROSOFT.COM/EN-US/LIBRARY/CC716760.ASPX

    5.HILMI ARIC, BATCH INSERT WITH ENTITY FRAMEWORK – LINQ TO ENTITY, HTTP://WWW.HILMIARIC.COM/?P=117

    6.MICROSOFT DOWNLOAD CENTER, ADO.NET ENTITY FRAMEWORK 4.1 RELEASE CANDIDATE, MICROSOFT, HTTP://WWW.MICROSOFT.COM/DOWNLOADS/EN/DETAILS.ASPX?FAMILYID=2DC5DDAC-5A96-48B2-878D-B9F49D87569A

    7.SOURCEFORGE , NHIBERNATE, HTTP://SOURCEFORGE.NET/PROJECTS/NHIBERNATE/FILES/NHIBERNATE/3.1.0.GA/