GitHub:git |
早前分享過,當時沒有把代碼上傳到Github,只是經過郵件的形式分享給了部分須要的朋友,最近終於有時間簡單整理一下直接上傳到 Github。sql
目前上傳的最新版本有一些新功能特性,還有一些細節調整有興趣的本身看一下代碼。數據庫
代碼的核心實現簡單粗暴,我奉行夠用就好,解決問題就好的思路,不會在最初的版本中就考慮上千萬上億數據balabala之類的問題,可是若是我在工做中遇到了這樣的場景,我會去升級它並解決這樣的問題。app
這個組件是我前兩年寫的,可能和如今流行的 dapper 有一些相似,當時我並不知道有 dapper,若是知道的話可能我就直接使用 dapper了。我寫 sheng.ADO.NET.Plus 並非閒的無聊要造個輪子玩,而是我在本身的項目開發中,切實遇到了一些問題須要解決:使用EF帶來的不便和直接使用ADO.NET帶來的不便,我須要一個介於二者之間的,高度自由的組件。框架
=====函數
目前咱們所接觸到的許多項目開發,大多數都應用了 ORM 技術來實現與數據庫的交互,ORM 雖然有諸多好處,可是在實際工做中,特別是在大型項目開發中,容易發現 ORM 存在一些缺點,在複雜場景下,反而容易大大增長開發的複雜度及犧牲靈活度。使用 ORM 不寫 SQL 而使數據庫交互變得簡單易行,是否可以達到預期效果,要畫一個問號。工具
主要問題可能存在於如下幾點:性能
1.大幅度犧牲性能。學習
2.雖然隱藏了數據層面的設計,但並無從根本上下降數據訪問複雜度,只是將複雜緯度從一個點(SQL,存儲過程)轉移到另外一個點(代碼),以EF爲例,最終生成的代碼性能與C#書寫有很大關係,且難以經過成熟的數據庫技術反查性能瓶頸。
3.對於複雜查詢,ORM 力不從心,雖然從技術角度說實現確定都能實現,可是代價是不值的。
有朋友認爲 ORM 可使不懂數據庫的開發人員也能在開發中輕鬆實現與數據庫的交互,可是,在大型項目中,讓不懂數據庫的開發人員作這塊工做,Are you kidding me?
在我本身的項目開發經驗中,ORM 還存在如下問題:
1.對於大型項目的開發,表示數據的實體類和數據庫層面的持久化設計並不是一一對應的關係,使用ORM根據數據庫表生成一一對應的實體類模型,並不能徹底適用,這是促使我實現本身的加強組件的重要緣由之一;
2.在實體類中,須要進行其它編碼工做,如額外的屬性定義,附加額外的Attribute,部分功能實現和業務操做等,而使用ORM來生成實體類,生成時會覆蓋現有實體類而致使項目自身的編碼工做丟失;
直接使用 ADO.NET 又在不少時候過於繁瑣,特別是取值賦值的過程很是冗餘又麻煩,那能不能設計一種機制,既能擁有 ORM 所帶來的一些便利,又不失 ADO.NET 的高性能和自由度呢?
基於這樣的目標,我設計實現了升訊威ADO.NET加強組件。
升訊威ADO.NET加強組件有如下幾個特色:
1.支持全部數據庫原生操做(基於微軟企業庫的數據模塊,並在最新版中集成了日誌模塊)
2.解除與數據庫表模型一一對應的關係,由開發人員靈活指定映射關係。
3.支持直接使用SQL語句並根據查詢結果在內存中動態映射。
4.支持調用存儲過程並根據查詢結果動態映射。
5.支持自動化的事務處理,可自動回滾。
6.支持一對多的映射關係,即一個實體類能夠映射到多張表。
7.支持自動填充/補全數據實體類中的數據。
8.支持DataSet、DataTable、DataRow多種粒種的內存動態映射。
9.支持簡單SQL構造器,支持自動生成簡單的無模型映射的SQL語句。
10.高性能,高靈活性,高可維護性。
下面直接從代碼示例中看使用效果:
如今假定有 User 表,包括四個字段:Id,Name,Age,ExtraInfo。
咱們定義一個簡單的 User 類。(亦可以使用其它工具自動生成)。
public class User { public Guid Id { get; set; } public string Name { get; set; } public int Age { get; set; } public string ExtraInfo { get; set; } }
初始化升訊威ADO.NET加強組件核心類 DatabaseWrapper,
private DatabaseWrapper _dataBase = DatabaseUnity.Database;
public void AddUser(User user) { _dataBase.Insert(user); }
升訊威ADO.NET加強組的 Insert 方法原型是:
public bool Insert(object obj)
Insert 方法會自動解析傳入的對象實例,分析對象的類型名稱(User)及其所包括的屬性(Property),自動實現對User表及各字段的動態映射,將數據插入到表中。
public List<User> GetUserList() { return _dataBase.Select<User>(); }
此處原理同上文同樣,Select 方法自動解析對象類型,獲得表,字段信息,實現數據的查詢與填充。
public void UpdateUser(User user) { _dataBase.Update(user); }
有些ORM框架,使用跟蹤對象實例的變化的方式,基於特定對象提交數據,可是這種方式的開銷很是大,升訊威ADO.NET加強組沒有采用這種方式,而是直接根據提交的對象實例,更新數據庫表。
public void RemoveUser(User user) { _dataBase.Remove(user); }
須要注意的是,使用上文中的簡單方式進行修改及刪除操做,必須在實體類中指定主鍵字段:
public class User { [Key] public Guid Id { get; set; } ...... }
至此咱們實現了基本的數據庫操做的自動化。
是否是很熟悉,和Entity Framework很相似是否是?
上文中的簡單增刪改查操做,是根據對象實例獲得對象類型從而獲得類型名稱和屬性(Property)集合及他們的名稱,那麼若是實體類型的名稱與數據庫表名稱並不同怎麼辦呢?若是數據實體的屬性(Property)與數據庫表字段並不一一對應怎麼辦呢?
在大型項目中,這種狀況是常常存在的,對於複雜的數據庫表設計,到了業務層,可能會有不一樣的解釋方法,例如我有一張用戶表,包含了產品不一樣維度的信息:基本信息、擴展信息等。到了業務實現層面,我但願展開爲兩個不一樣的實體對象進行操做,基本信息對象和擴展信息對象。他們所使用的字段可能不太相同,卻又包括了某些共通的字段,如Id,姓名。
如上文所說,升訊威ADO.NET加強組件沒有強制的實體類與數據庫表的映射關係要求,數據庫表中的字段多少與實體類中的屬性多少,或者說表中有的,實體類中沒有,都沒有關係,實體類中有的,經過Attribute標記是否映射便可。
咱們定義兩個不一樣的實體類:
[Table("User")] public class User_BaseInfo { [Key] public Guid Id { get; set; } public string Name { get; set; } public int Age { get; set; } }
[Table("User")] public class User_ExtraInfo { [Key] public Guid Id { get; set; } public string ExtraInfo { get; set; } }
只需在類型定義前加上 TableAttribute ,對 User_BaseInfo 或 User_ExtraInfo 類的對像實例進行操做,直接使用上文中的增刪改查方法便可。至此咱們已經開始解除了實體類與數據庫表結果的強關聯。
此處嚴格來說,並不是通常ORM中針對 數據庫表字段 的映射,而是針對 結果集字段 的映射。好比說經過複雜SQL,存儲過程獲得的結果集,根本不是數據庫中的表。
在某些場景中,實體類中須要額外定義一些屬性,用於存儲特定信息或實現特定功能,這些數據並不須要進行持久化存儲。或是實體類中的屬性名稱與數據庫表字段名稱存在不徹底相同的狀況,如將一張表映射到多個數據實體後,爲了區別描述,以及基於複雜查詢(SQL,存儲過程)獲得的結果集中的字段名。
[Table("User")] public class User_ExtraInfo { [Key] public Guid Id { get; set; } [Column("ExtraInfo")] public string Infomation { get; set; } [NotMapped] public int Count { get; set; } }
只需在屬性定義前加上 ColumnAttribute 或 NotMapped ,使用上文中的增刪改查方法便可實現相應的操做。
此功能用於將二維的數據庫表(或結果集)進一步強類型化。
在使用通常ORM框架時,對於複雜的數據庫表結構,經常能夠見到很是多的字段定義,但在咱們的實際業務中,這些字段可能都有不一樣的邏輯歸屬,此外,在開發中,咱們可能在數據傳遞,操做的過程當中,但願只傳遞或公開一部分數據,而不是整個對象進行傳遞。
public class User { [Key] public Guid Id { get; set; } public string Name { get; set; } public int Age { get; set; } [Partial] public ExtraInfo ExtraInfo { get; set; } } public class ExtraInfo { public string ExtraInfo { get; set; } }
只需在對象上加上 PartialAttribute ,表示屬性的對象是 當前數據集 的一部分字段所表示的子對象。
PartialAttribute 還提供了 FieldRelationship 用來進一步指定映射關係。
這樣咱們實現了實體類對數據表(數據集)的多對一映射,那如何實現多對多的映射呢?實際上很是簡單,使用SQL,視圖,存儲過程進行多表查詢,結合使用 PartialAttribute 便可。
除了上文中提到的基本 Select<T>() 方法外,升訊威ADO.NET加強組件提供了額外的幾個進階方式進行數據查詢。
a) 基本查詢
public List<T> Select<T>() where T : class,new()
上文已展現。
b) 附加查詢條件
public List<T> Select<T>(Dictionary<string,object> attachedWhere) where T : class,new()
經過 attachedWhere 額外的指定查詢條件。Dictionary<string,object> 中的 string 和 object 分別指定字段和字段值。
爲何不使用 lamda?在一些場景中不夠靈活。
c)經過 SQL 語句進行查詢
既然是ADO.NET加強組件,直接使用SQL來操做固然是重頭戲。
public List<T> Select<T>(string sql) where T : class
直接編寫 SQL 語句進行數據查詢,Select 方法可根據返回的結果集和指定的對象類型進行自動映射,返回強類型對象集合。
能夠傳遞任意可以返回結果集的SQL語句,返回的結果集自動與泛型T匹配,泛型T也不必定就是數據庫中的表所映射的對象。
d) 參數化 SQL 語句查詢
public List<T> Select<T>(string sql, List<CommandParameter> parameterList) where T : class
進行參數化的 SQL 語句查詢,例如:
List<CommandParameter> parameterList = new List<CommandParameter>(); parameterList.Add(new CommandParameter("@extraInfo", "ABC")); List<User> userList = _dataBase.Select<User>("SELECT * FROM [User] WHERE ExtraInfo = @extraInfo");
當咱們使用存儲過程或其它方式獲得一個 DataSet 時,升訊威ADO.NET加強組件支持對其進行動態映射,根據 DataSet 數據集獲得強類型的對象實例或對象實例的集合。
RelationalMappingUnity 類提供瞭如下方法:
public static List<T> Select<T>(DataSet ds) where T : class
將 DataSet 視爲一個完整數據源,從中查找指定對象類型所映射的表名進行實例化。
public static List<T> Select<T>(DataTable dt) where T : class
使用 DataTable 做爲惟一數據集,對指定的對象類型進行實例化。
public static T Select<T>(DataRow dr) where T : class
public static object Select(DataRow dr, Type type) public static object Select(DataRow dr, Type type, Dictionary<string, string> fieldPair)
上面三個方法提供了更細粒度的操做可能,直接從 DataRow 獲得一個強類型的對象實例。
不少時候咱們須要根據某個已知條件查詢獲得對象實例,如咱們獲得 User 的 Id,但願查詢數據庫表獲得 User 對象,在升訊威ADO.NET加強組件中,咱們使用 Fill 方法既可。
public bool Fill<T>(object obj) where T : class,new()
public User GetUser(Guid id) { User user = new User(); user.Id = id; if (_dataBase.Fill<User>(user)) return user; else
return null; }
Fill 方法返回一個 bool 值,表示是否成功查詢並填充了數據。
Fill 方法也有一個高階重載,能夠額外指定查詢條件:
public bool Fill<T>(object obj, Dictionary<string, object> attachedWhere) where T : class,new()
有時,咱們但願直接經過 SQL 語句實現對數據庫表的簡單操做,升訊威ADO.NET加強組件提供了一個 SQL 語句構造器,幫助生成 SQL 語句,能夠減輕開發人員編寫 SQL 語句的工做量和出錯的可能性,提升軟件工程的質量。
public void AddUser(User user) { SqlStructureBuild sqlStructureBuild = new SqlStructureBuild(); sqlStructureBuild.Type = SqlExpressionType.Insert; sqlStructureBuild.Table = "User"; sqlStructureBuild.AddParameter("Id", user.Id); sqlStructureBuild.AddParameter("Name", user.Name); sqlStructureBuild.AddParameter("Age", user.Age); SqlExpression sqlExpression = sqlStructureBuild.GetSqlExpression(); _dataBase.ExcuteSqlExpression(sqlExpression); }
ExcuteSqlExpression 方法在執行 SQL 構造器生成的 SqlExpression 對象時,使用的是參數化,強類型的方法進行的。
對於連續的數據庫操做,升訊威ADO.NET加強組件自動封裝爲一個事務進行執行,若是執行失敗,將自動回滾。
a) 連續寫入操做
很是簡單,直接使用 Insert 方法插入一個對象集合既可,方法原型以下:
public void InsertList(List<object> objList)
鏈接的寫入操做時,並不要求傳入的參數是一樣類型的,也就是說能夠傳入多個不一樣相似的實體對象,如同時傳入User和Order,升訊威ADO.NET加強組件也會將其封裝爲事務執行,要麼所有寫入成功,要麼回滾。
b) 複雜複合操做
對於相對複雜的數據庫事務操做,可以使用 SQL 語句構造器,分別構造 SqlExpression 對象,將其按執行順序放入集合中,經過 ExcuteSqlExpression 執行便可。
public void ExcuteSqlExpression(List<SqlExpression> sqlExpressionList)
這種方式執行的多個 SqlExpression 對象,亦封裝爲事務進行執行。
升訊威ADO.NET加強組件支持對數據庫進行原生操做,在此基礎之上,結合上述功能,實現簡單高效高靈活性的數據庫操做。
public int ExecuteNonQuery(string commandText) public int ExecuteNonQuery(string commandText, List<CommandParameter> parameterList) public int ExecuteNonQuery(CommandType commandType, string commandText, List<CommandParameter> parameterList) public object ExecuteScalar(string commandText) public object ExecuteScalar(string commandText, List<CommandParameter> parameterList) public object ExecuteScalar(CommandType commandType, string commandText, List<CommandParameter> parameterList) public DataSet ExecuteDataSet(string commandText) public DataSet ExecuteDataSet(string commandText, string tableName) public DataSet ExecuteDataSet(CommandType commandType, string commandText, string tableName) public DataSet ExecuteDataSet(string commandText, List<CommandParameter> parameterList, string tableName) public DataSet ExecuteDataSet(CommandType commandType, string commandText,List<CommandParameter> parameterList, string tableName)
綜上所述,升訊威ADO.NET加強組件強調的並不是實體類與數據庫表結構的強關聯,而是經過與內存數據集的動態映射,將數據庫操做時大量的重複勞動自動化,對於複雜數據庫操做,繼續使用原生 SQL,存儲過程,自定義函數,視圖等。
這種方式結合了 ORM 自動化的優勢,又充分利用了數據庫原生操做的強大功能,使數據層的開發輕鬆,高效,高質量。將簡單的,重複的體力勞動,交由程序自動化處理,複雜業務場景由人工處理,並將數據映射,取/賦值等重複勞動,自動化處理。
以上設計實現不免存在考慮不周的狀況,但願和你們多多交流。
歡迎加我QQ交流探討,共同窗習:279060597,另外我在南京,有南京的朋友嗎?