NET主流ORM框架分析
接上文我們測試了各個ORM框架的性能,大家可以很直觀的看到各個ORM框架與原生的ADO.NET在境刪改查的性能差異。這裡和大家分享下我對ORM框架的理解及一些使用經驗。
ORM框架工作原理
所有的ORM框架的工作原理都離不開下面這張圖,只是每個框架的實現程度不同但是最終的目的是相同的。
如果是一個ORM框架那麼一定會有上圖中藍色部分的這幾個元素,無論是增刪改查對於ORM一定是以對象為起點,使用對象構造出LINQ表達式,這樣我們在對象的世界中可以描述我們希望對資料庫所進行的操作,LINQ的最終實現其實也是Lambda表達式(必盡LINQ在代碼上會直觀很多),功能較強的ORM中都會記錄有對象類型到資料庫對象的元數據,使用這些元數據可以將複雜的Lambda表達式翻譯成一個通用的中間表達式,這個表達式其實是抽象於各個不同資料庫的具體實現,最後中間表達式再按指定資料庫的具體實現生成最終的SQL語句,交由ADO.NET對象執行到資料庫,如果數據存在返回則會回寫到CLR對象中。
以上概括了所有ORM的實現過程,這裡比較典型的是Dapper,它之所以小巧而高性能的原因正是因為該框架中沒有綠色的部分,可以說是所有ORM框架中最為精簡的(可能還會存在其他類似的框架,這裡僅用Dapper作為典型案例說明),因此沒有多餘的部分自然小而快。然而綠色部分的存在就決定了該ORM框架的功能到底有多強,剛好目前世面上的ORM框架中只有EF是完全實現了綠色框中的各個元素,並且支持最為全面,所以EF必然重而慢(至少EF6相對其他框架是比較差的)。
典型ORM框架實現
Dapper:需要自己手寫SQL語句完成操作,比較簡單直接不過還有DapperExtensions的幫助,可以在插入、修改及刪除時不用寫SQL語句全對象操作,這裡需要指出的是它應該不能算是一個完全的ORM框架,因為日常開發佔比多的查詢還是需要手寫SQL,無法對象類型化。
Nhibernate:這是個比較久遠的框架,該框架需要自己配置元數據功能上會比DapperExtensions強一點,不過對於查詢表達式沒有太好的支持。
SqlSugar,Chloe.ORM:此類框架雖然不能支持LINQ但是可以實現自己一套Lambda表達式用於生成SQL語句,總之其總體思想就是把SQL語法轉換成表達式語法,比較典型的是會用表達式中的變數名用作生成SQL語句中的別名,而且附加功能會比較多,總之比較靈活實用。
Linq2db:此類ORM算是實現的比較完整的ORM流程(見上圖),而且支持眾多的資料庫,總之功能算是比較強大了。
EF功能最強的ORM
EF是目前為止功能最強的ORM,這個相信大家沒有什麼爭議,大家可以參考一下這份文檔 (EF Core and EF6 Feature by Feature Comparison),其中列出了EF6與EFCore的功能特性,相信沒有哪個ORM框架實現了其中大部分的特性,下面隨便列舉幾個特性:
全面支持LINQ查詢,有不少框架也會支持LINQ可是使用時會發現有些寫法是不支持的,但是EF是目前支持最全的,在截止到目前為止EFCore的正式版本中也沒有完全實現EF6對LINQ的支持。
EFCore性能提升:從上一遍博客(Mego(1))中可以看出對於個功能強大的ORM框架而言EFCore已提升相當多了總之已非常接近原生的ADO.NET框架了。
對象繼承:這是個很好的設計,它可以讓你將關係資料庫實現對象繼承化,在數據表中也會存在等價父子表。
資料庫遷移功能,無論你怎麼看待這個功能,但是對於開發而言這個功能太實用了,相信大家在很多DEMO中可以見到使用EF生成資料庫。
對象的集合屬性展開,通常我們在取訂單時也希望一起把明細也取出來,更多的情況是還可以把訂單數據分頁,這個功能在大部ORM都是沒有的需要自己處理,然後在EF中只需要寫一行代碼就能搞定。
多對多關係:這個也是個比較常用但是也在其他ORM比較少見的功能,EF也是目前支持最好的。
EF框架對接了微軟的眾多其他需要數據訪問的框架,例如ASP.NET Identity,ASP.NET WebApi ODATA。
EF與EFCore缺陷
雖然EF或EFCore功能已經很強大了,不過在開發過程中還有很多不足的地方,也有很多缺陷這也是導致有很多人使用之後而放棄的原因之一(當然還有EF6的性能問題)。下面將列一下EF6的不足給大家參考。
數據更新功能不足
在EF中數據提交的功能一直不行,在EF6中所有的數據操作都是單個對象分別提交了,這也就表示在大量數據提交過程中每個對象都要發送一次資料庫,這也是導致為什麼性能底下了,而且更新及刪除數據一定要從資料庫取回對象或附加對象才能操作。這個問題在EFCore中都得到解決而且性能比較好如前文的測試可見。但是EF到目前都不能支持條件更新、條件刪除。不過這個問題可以使用這個框架(Entity Framework Plus)解決,不過這個框架是收費的。
創建時間修改時間問題
我們在業務系統開發時常會遇到創建和更新數據時需要記錄創建時間或修改時間的情況,不過目前無論是EF或EFCore中都只能用觸發器來解決,不過當數據表變多時維護這些觸發器也是個比較麻煩的事情。有人會想到用默認值,這個EF中是支持的,但是EF不能選擇性的插入或更新指定的欄位,這是個兩難的情況。
創建及修改時間只是這類問題的一個例子,這類問題可以概括為我們在插入或更新數據時希望指定欄位是調用相應的數據表達式生成的,而不是應用程序的值,這裡可以舉個實例給大家看,在一個負載均衡的架構中我們需要在創建訂單時生成訂單流水號,這個流水號一定要唯一且連續,在這種多伺服器並發的情況下只有在資料庫生成這個流水號才是比較好的選擇,這樣可以充分保證當提交失敗時新分的流水號可以歸還,同時也保證大家依次生成不會重複且連續,這個時候就很需要ORM可以自動使用單號生成函數來生成值。
EF中的曲線解決,在EF6中插入、更改或刪除操作是支持存儲過程映射的,大家可以參考這個文章(Entity Framework Code First Insert, Update, and Delete Stored Procedures (EF6 onwards))裡面有詳細的說明,這裡我們可以做文章,我們修改存儲過程改為調用我們指定的表達式即可,這樣可以不用觸發器,也能達到目的。這裡有人會問維護存儲也需要比較大代價,這裡我們可以重寫EF的資料庫遷移生成存儲過程的代碼來統一處理。
實體集合屬性多層展開
無論是EF或EFCore中有一個Include函數,用來在返回實體對象時顯示包含它的集合或對象屬性,這個操作被稱為屬性展開,這裡先這麼稱呼。不過可以展開對象屬性(例如訂單 -> 客戶 -> 負責人 -> 公司),也可以展開集合屬性(例如 訂單 -> 明細集合),這時集合下面的屬性就不能展開了例如我希望得到(例如 訂單 -> 明細集合 -> 產品)這是不支持的。
補充EF實際內部代碼是可以支持這種操作但是沒公開,如果有對ASP.NET WebApi OData有所了解的朋友可以知道,如果你的OData服務是建立在EF基礎之上的,在ODATA語法中可以支持多級數據展開的,並且在EF6中得到很好的實現,不過這些的前提是你的資料庫是SQL Server。
對象關係限制
例如一個訂單會有多個明細,每個明細都會對應一個產品,從邏輯上我們就可以認為訂單和產品是多對多關係了,但是在EF中就沒有這麼自由的可以操作了。
在EF中關係的操作有很多限制,這個特別在多對多關係中很嚴重,EF6中會生成一個隱藏的關係實體用來建立關聯,這些都是你不能控制的,而且在關係鏈接中要求主表一定要是主鍵,不過在EFCore中這些問題得到一些解決,不過EFCore目前不能支持多對多關係,至少目前的Flush API中還沒有看到。
時間戳
感覺EF中不少特性都是為SQL Server設計的,EF中強制時間戳必須CLR屬性是位元組數組,不過在MySQL中的時間戳其實就是日期時間。
多資料庫實現差異
EF和EFCore是支持多個資料庫,其原理是EF定義的前文所說的中間表達式,然後再交給各方自行實現後續的操作。這種設計有如下缺點:
資料庫支持綁定了數據訪問組件,例如MySQL.Data.Entity這個組件必須需要在MySQL.Data組件下工作,這時如果你想換個訪問MySQL的組件是不可以的。
資料庫提供程序實現代價太大,如果大家有時間可以去看下EntityFramework.SqlServer或MySQL.Data.Entity里的實現代碼,這些都是著名公司微軟和Oracle維護的代碼,其中的實現代碼非常複雜,對於一般的團隊而言可以算是一個非常大的項目。
資料庫提供程序實現質量,還是以MySQL.Data.Entity為例子,每個提供程序並不像微軟那樣完全實現EF在SQL Server中的各個功能,而且代碼非常複雜問題會很多,即使是Oracle所維護的代碼也是同樣糟糕,下面分享MySQL.Data.Entity的兩個例子。
MySQL.Data.Entity問題一:這個組件在MySQL5.6由於資料庫的主鍵限制在700多個位元組,因此它在自動遷移資料庫操作時會報錯主鍵過長錯誤,等於這個是完全不能用的。
MySQL.Data.Entity問題二:這個組件在MySQL5.7中,由於資料庫的BUG(虛擬列如"SELECT 1"的IS NULL判斷錯誤問題)會導致特定的LINQ表達式生成的SQL腳本是不能得到正確結果的。
MySQL.Data.Entity問題三:對於繼承的SQL生成這一部分是完全沒有實現的,你會發現所生成的腳本都是錯的。
MySQL.Data.Entity問題四:EF的Include操作的具體實現是依賴於CROSS APPLY(SQL Server)語法的,不過MySQL中完全沒有,也很寫出替代語句,因此這個功能在MySQL下等於是不存在的。
以上是因為我參加了EF對接MySQL的改造項目,這是我和我的團隊折騰了幾個月才得出的一些總結,不過好在我們最後通過修改MySQL.Data.Entity源碼解決了這些問題。
總之其他框架不能確定,但是在EF中各個資料庫的支持程度是很不對稱的。
總結
在EntityFramework長達10年的發展歷程中,開始發展很快,但是後面到EntityFramework6.1.3(2015年3月)這個版本時就好像是EntityFramework的結束,之後EntityFramework6就再沒有此實制上的更新了直到今天,應該是微軟已經放棄它轉向EntityFrameworkCore上,不過EntityFrameworkCore的發展也沒有這麼快,至今還沒有超過EntityFramework6,所以直到今天微軟都不敢對外宣布放棄EntityFramework6。
以上是我的個人見解,僅供參考。
原文地址 http://www.cnblogs.com/CarefreeXT/p/8729085.html
※【ASP.NET Core】處理異常
※使用CoreRT將.NET Core發布為Native應用程序
TAG:dotNET跨平台 |