這一篇文章的內容是依據我在公司裡所分享的主題「ASP.NET.MVC / Web API 另一種存取方式」再加以整理。接觸 Dapper 這個資料存取套件是有一段時間,但一直都沒有機會使用上,因為之後所做的專案多半都是使用 Entity Framework,而在去年要開始做新專案的時候,因為系統環境以及舊有包袱的緣故,於是我就想起了 Dapper,也的確解決了許多原有的問題並且大來很大的效益。
這一篇介紹 Dapper 並不會有特別深入的使用介紹,大概只能算是入門介紹,最主要的目的是要讓大家能夠知道在現有環境無法使用 ORM 的情況,尤其又是開發 ASP.NET MVC / Web API 的專案,除了 Entity Framework 之外能夠有其他的替代方案。
注意,並不是說 ASP.NET MVC / Web API 才能夠使用 Dapper,任何你之前使用 ADO.NET + T-SQL 所開發的專案都可以使用(一定要先這麼說,不然一定很多人會有所誤解,這是寫部落格文章四年多來,接到無數回應、詢問所得到的心得。)
內容大綱
- Model
- 強型別與弱型別
- 為何開發 ASP.NET MVC / Web API 不建議使用 DataSet, DataTable
- 目前開發碰到了哪些問題?
- 使用 Dapper
- 以操作範例來說明
Model
在這邊先說明什麼是「資料模型」:
「資料模型」用於封裝與應用程序的業務邏輯相關的數據以及對數據的處理方法
有關「資料處理」的範圍都可屬於 Model,包含:
- 資料庫的存取操作(讀取、寫入)
- 資料結構的定義
- 資料格式的驗證
Model 還有做哪些事情以及不做那些事情
不處理所有與資料處理無關的事
不直接處理來自瀏覽器的輸入,也不向瀏覽器產生 HTML 輸出。
ASP.NET MVC 架構對可以建置的各種模型物件沒有任何限制。
可用哪些來作為 Model
ORM Solution
NHibernate, SubSonic, LLBLGen Pro … etc
也可以用「非 ORM Solution」,但是會非常累…
Enterprise Library Data Access Application Block
上面內容可以參考之前 twMVC 第一次的研討會簡報裡的 Model 的部分:
https://docs.com/is-twMVC/7324/asp-net-mvc-twmvc-1
強型別與弱型別
在講「強型別(Strongly Typed)」之前,先來瞭解什麼是「弱型別(Weakly Typed)」,大家在專案裡使用 ADO.NET 時最常接觸並且使用的「DataSet」「DataTable」就是弱型別的一種,不過具型別的資料集(Strongly Typed DataSet)就另當別論,就不在這個討論的範圍內。
在 MSDN 上面有這麼一段對於強型別和弱型別的描述
未宣告的變數以及不具資料型別的宣告變數都會指定為 Object 資料型別。 這會加快程式的撰寫,但可能會使程式的執行變慢(這邊所描述的是指弱型別)。
強型別指定
指定所有變數的資料型別稱為「強型別指定」(Strong Typing)。使用強型別指定有以下幾點好處:
讓 IntelliSense 能夠支援變數。 這能讓您在輸入程式碼時看到變數的屬性及其他成員。
可以利用編譯器型別檢查, 這能找出可能因錯誤 (例如溢位) 而在 Run Time 失敗的陳述式。 這也能夠偵測在不支援變數的物件上所進行的方法呼叫。執行程式碼的速度較快。
from「 MSDN - 有效率地使用資料型別」
使用強型別的好處在於你會事先定義好類別、類別的屬性以及所使用的資料型別,在開發時寫 Code 的時候在 Visual Studio 裡當宣告使用你所定義的類別,你可以知道要用那一個屬性以及資料型別,當指定錯誤資料型別的值,Visual Studio 會讓我們知道,要不然在編譯的時候也會知道錯誤,進而減少系統執行時的錯誤。
而使用強型別進行開發所帶來的好處是遠大於使用弱型別。
為何開發 ASP.NET MVC / Web API 不建議使用 DataSet, DataTable
我在之前的文章「ASP.NET MVC 的 Model 使用 ADO.NET」以及在 twMVC#10 研討會理也曾經分享過「ASP.NET MVC Model 的設計與使用」的內容。
文章與分享會的內容都有提到「ASP.NET MVC 的 Model 並不是只能使用 ADO.NET Entity Framework」,所以在專案裡還是可以一樣使用 ADO.NET 以及 DataSet, DataTable,但現實的情況下這麼做是少數,因為會非常的辛苦,尤其在於「定義類別」「資料對映到自定義類別」這些工作上。
另外一開始學習 ASP.NET MVC / Web API 都會接觸到,如果你已經瞭解並領略 ORM 以及強型別在開發上所帶來的好處,就不會想再回到 ADO.NET + DataSet. DataTable, DataRow 的回頭路。
目前開發碰到了哪些問題?
實際開發時當 ASP.NET MVC / Web API 遇到了弱型別最常發生的狀況
建立類別、資料欄位多, 建立類別的屬性就頭痛,更別說資料對應轉換。
資料欄位異動, 當有異動就必須要大費周章的修改,如果異動欄位多的話,接下來的處理就要花更多的時間。
實際開發時使用 ORM (Entity Framework or LINQ to SQL …)遇到的困難
Entity Framework or LINQ to SQL 無法跨資料庫操作
無法將現行熟悉的 T-SQL 給轉換成 LINQ
目前資料庫表格並不適合使用 ORM(本來就不是設計給 ORM 用,無嚴謹的正規化與關聯)
講白一點,很多大公司、大企業在資料庫的管理上並不是很嚴謹,甚至很多開發者在一開始規劃 Table 的時候並不是很正規的去做設計,既始有也會因為後續逐漸增加的專案而在舊有的 DB, Table 裡去做欄位的修改、增加,導致很多 DB 的 Table 都是欄位上百個的怪獸,這樣的 Table 能夠拿到 Entity Framework 裡使用嗎?
既有的專案都是使用 ADO.NET + T-SQL 進行開發,很多的 SQL Command 是相當錯綜複雜的,而且很多都是也跨資料庫的操作處理,這樣的情況是無法改用 Entity Framework 開發。
另外的情況就是,很多專案都會共用一些 Table,但是 Table 的管理是很鬆散的,每個人都可以對 Table 的內容動手腳,可能今天甲開發者因為專案的需要而去修改了某欄位的型別與長度,而乙開發者覺得 Table 的某欄位看起來好像沒有專案在用所以就刪除了,這樣的變動影響了丙開發者的專案,因為他使用 Entity Framework 開發,因為 Table 頻繁地更動,使得丙開發者必須很頻繁地更新 EF 的內容,別人專案裡可能只需要修改幾個受到影響的 T-SQL 語法和程式就好,但是丙開發者卻必須得要對專案進行大規模的修改,搞到最後好像使用物件導向、強型別、ORM 進行開發是種錯誤一樣(這個情況會比較屬於開發規範、開發觀念的管理,但是一個資訊團隊裡的成員視新舊交雜的情況時,這樣的紛爭與困擾會時常出現)。
另外還是有很多人的觀念依然停留在「開發 ASP.NET MVC / Web API 就得要用 Entity Framework」。
使用 Dapper
Dapper-dot-net ( StackExchange )
https://github.com/StackExchange/dapper-dot-net
https://www.nuget.org/packages/Dapper/
雖然 Dapper 在 Nuget 裡的描述說 Dapper 是一個 Micro-ORM,但嚴格來說只有做了 Data 與 Mapping 的單向處理,並沒有雙向的,雖然如此但無損 Dapper 的功能。
另外 Dapper 是由 StackExchange 團隊所開發的,而 StackExchange 就是開發並維護 StackOverflow 系列網站的團隊,不說還真的有很多不知道 StackOverflow 系列網站是使用 ASP.NET MVC 所開發的,而且支撐整個網站的架構大多是使用微軟技術,而且使用的硬體架構相對於相同網頁流量的網站相比,是相當精簡,有興趣的朋友可以前往以下的網頁進行瞭解。
http://stackexchange.com/performance
專案使用 Dapper 對於習慣使用 ADO.NET + T-SQL 的團隊在於仍然可以繼續使用 T-SQL 語法,而且後續的資料處理不必再面對使用 DataSet, DataTable,而且然後是使用 IDbConnection 做處理,而且資料的存取是使用強型別。
以實際範例來做說明
前面說了一堆的廢話,直接使用實際的操作範例來做說明比較清楚,這邊使用 LINQPad 來做示範。
這邊所使用的 LINQPad 是 Premium 版,而 LINQPad 的 Developer 與 Premium 這兩個版本是有提供 Nuget 功能,就像在 Visual Studio 裡專案使用 Nuget 安裝套件。
另外 Dapper 的文件說明可以直接參考 Dapper 在 Github Repository 的 Readme.md 的內容,相當前顯易懂。
下面的案例就是要取得 Customers 的所有資料,而且取得的資料要對映到我們自定義的 Customer 類別,以往的做法是必須要一個欄位去對映屬性,當欄位少的時候還可以接受,一旦欄位多(尤其是很多 Nullable 時)作對映處理就會讓人很容易厭煩。
先用一般常見的做法來做處理,如下圖所示:
當然有人會用 Reflaection 的做法或是我之前寫過用 Automapper 的方式來處理 DataSet 的資料對映轉換,方法雖然可行,但隨之而來的就是執行效能的影響。
接著改用 Dapper 來處理相同的資料內容以及資料對映,首先在 LINQPad 透過 Nuget 加入 Dapper,
接著是使用相同的 SQL Command 內容,取得資料後處理資料對映,使用 Dapper 後的程式遠比傳統做法要來得精簡,
上面是取得全部的 Customer 資料,如果是要取得一筆相符 CustomerID 的資料,則是下面的做法,處理 SQL Command 裡面的參數資料,也是一樣使用類別的方式來處理,也就不會需要另外處理 Parameter,當然也就不需要煩惱 SQL injection 的問題(不過如果你還是在 SQL Command 使用組串的方式,既使是用了 Dapper 還是一樣會有 SQL injection 的風險)。
前面是操作查詢的處理,接著來做 Insert 資料的處理,首先是一筆資料,
也可以一次新增多筆資料
那麼 Update, Delete 等操作就不在繼續做示範說明,做法其實就如同之前 ADO.NET + T-SQL 的方式相同,只是資料都是使用強型別。
以前 ADO.NET + T-SQL 可以怎麼做,使用 Dapper 也是一樣可以,
Multiple Results
Stored Procedure
Performance ?
其實無須太多的擔心,如果你的 SQL Command 寫得不好,就會影響效能,不過那也無關 Dapper ~
Dapper 的核心是 SqlMapper.cs,是對 IDbConnection 建立靜態擴充方法,對於想要瞭解原始碼的朋友,可以直接去查看程式的內容。
https://github.com/StackExchange/dapper-dot-net/blob/master/Dapper%20NET40/SqlMapper.cs
因為企業內部的系統環境與開發限制而無法使用 Entity Framework 的朋友,或者是還抱持著以為開發 ASP.NET MVC / Web API 就必須使用 Entity Framework 的朋友,又或者是有沈重且眾多 SQL Command 包袱、必須時常做跨資料庫處理的開發者朋友,真的就不要再考慮了,請馬上著手開始試著使用 Dapper。
原本我所開發的專案還是使用傳統的 ADO.NET + T-SQL 做法,資料存取的元件使用公司有再經過包裝的元件,因為是開發 ASP.NET Web API 專案,所以專案裡是不會去使用 DataSet 為資料容器,一開始頂多只會在資料存取層這邊會看到 DataSet, DataTable or DataReader,但馬上就會做對映轉換到自定義的型別,但無論是使用自己所寫的 Reflection 方法還是使用 AutoMapper 的轉換,所遇到的問題都是效能的延遲,而且又時常會處理到上百個欄位的資料;所以在開發初期讓我最為苦惱的就是資料層的處理,當什麼方法都嘗試過之後,最後才想起了 Dapper 這個套件,在幾次的來回測試之後就毅然決然地馬上將專案的資料存取處理改換使用 Dapper,轉換的過程相當快也不需要繁瑣的處理,專案裡原本使用傳統做法的方式更換為 Dapper 之後就不再煩惱資料對映所造成的效能問題,而且資料存取處理又重回我最熟悉的強型別、物件導向的方式,套句電影台詞「自從改用 Dapper 之後,我每次考試都得 100 分了」。
延伸閱讀
短小精悍的.NET ORM神器 -- Dapper - 黑暗執行緒
Huan-Lin 學習筆記: 好用的微型 ORM:Dapper
[ASP.NET] Dapper - 輕量級ORM - No.18 - 點部落
StackExchange/dapper-dot-net - Github
NuGet Gallery | Dapper dot net
Dapper - King of Micro ORM (C#.NET) - C-SharpCorner
參考資料
The Will Will Web | 何謂「強型別」(Strong Type)
「強型別」是什麼啊? MSDN Forum > Web應用程式開發 > ASP.NET 與 AJAX(ASP.NET and AJAX)
以上
純粹是在寫興趣的,用寫程式、寫文章來抒解工作壓力