扯淡
這是一款輕量、高效的.NET C#數據庫訪問框架(ORM)。查詢接口借鑒 Linq(但不支持 Linq)。借助 lambda 表達式,可以完全用面向對象的方式就能輕松執行多表連接查詢、分組查詢、聚合函數查詢、插入數據、刪除和更新滿足條件的數據等操作。
在上篇文章中, Chloe.ORM 初次對外拋頭露面,雖然是在重復造輪子,但還是得到部分園友的支持與認可,LZ非常感謝!上文主要展示 Chloe 的查詢方式以及支持的 lambda 寫法,本文主題是多表連接查詢、插入數據、更新數據、刪除數據以及事務支持。
導航
Chloe.ORM
事前准備
實體:
public enum Gender { Man = 1, Woman } [Table("Users")] public class User { [Column(IsPrimaryKey = true)] [AutoIncrement] public int Id { get; set; } public string Name { get; set; } public Gender? Gender { get; set; } public int? Age { get; set; } public int? CityId { get; set; } public DateTime? OpTime { get; set; } }
首先,創建一個 DbContext:
IDbContext context = new MsSqlContext(DbHelper.ConnectionString);
再創建一個 IQuery<T>:
IQuery<User> q = context.Query<User>();
多表連接查詢
在上篇文章中,介紹了 Chloe 的查詢功能。Chloe 執行連接查詢時,建立連接(IQuery<T>接口調用 InnerJoin、LeftJoin、RightJoin 和 FullJoin)返回的是 IJoiningQuery 的泛型對象,如果看過框架源碼的同學可能會知道,IJoiningQuery 的泛型參數最多有5個,而且有5個泛型參數的那個類(IJoiningQuery<T1,T2,T3,T4,T5>)就只有一個 Select 方法,也就是說不能繼續連接其它表,最多有5個表建立連接,真的是這樣嗎?如果你這樣認為你就錯了!我們來看看怎么建立超過5個表的連接。
偽代碼:
//假設已經有5個表建立了連接的對象為 jq_q1_q5 IJoiningQuery<T1, T2, T3, T4, T5> jq_q1_q5 = null; //jq_q1_q5 調用 Select 方法,返回一個包含 T1-T5 的 IQuery<T> 對象 view_q1_q5 var view_q1_q5 = jq_q1_q5.Select((t1, t2, t3, t4, t5) => new { T1 = t1, T2 = t2, T3 = t3, T4 = t4, T5 = t5 }); //假設第6個表的 IQuery<T6> 對象為 q6 IQuery<T6> q6 = null; //這時,view_q1_q5 與 q6 建立連接,返回 IJoiningQuery 對象 jq var jq = view_q1_q5.InnerJoin(q6, (t1_t5, t6) => t1_t5.T5.XX == t6.XXX); //然后我們調用 jq 的 Select 方法,返回一個包含 T1-T6 的 IQuery<T> 對象 view。 //view 又是一個 IQuery<T> 對象,泛型參數為包含 T1-T6 所有信息的匿名對象(這時候還沒有發起 sql 查詢哦),拿到它,我們就可以為所欲為了。 var view = jq.Select((t1_t5, t6) => new { T1 = t1_t5.T1, T2 = t1_t5.T2, T3 = t1_t5.T3, T4 = t1_t5.T4, T5 = t1_t5.T5, T6 = t6 }); //可以直接查出數據庫中 T1-T6 的所有信息 view.ToList(); //也可以選取 T1-T6 中我們想要的字段 view.Select(a => new { a.T1.xx, a.T2.xx, a.T3.xx /*...*/}).ToList();
這樣,周而復始,就可以實現無限表連接了,相對沒有 Linq 那么直觀,但這是我能想到最好的方式了。如果哪位同學有更好的設計,望分享,不勝感激!
Chloe 的另外亮點是聚合函數查詢和分組查詢,這兩個查詢有別於 Linq,更趨於標准 sql 思想,配合連接查詢使用,可以應對一些稍微復雜的查詢,可以翻看上篇文章使用進階部分,有簡單介紹,連接地址:http://www.cnblogs.com/so9527/p/5636216.html#more_usage。
目前市面上很多 ORM 都只是支持單表查詢,能靈活操作連接查詢和聚合函數查詢的,除 EF 和 linq to sql 外,為數不多,這也是我造輪子的原因之一。
插入數據
方式1
以 lambda 的形式插入:
IDbContext context = new MsSqlContext(DbHelper.ConnectionString); //返回主鍵 Id int id = (int)context.Insert<User>(() => new User() { Name = "lu", Age = 18, Gender = Gender.Man, CityId = 1, OpTime = DateTime.Now }); /* * INSERT INTO [Users]([Name],[Age],[Gender],[CityId],[OpTime]) VALUES(N'lu',18,1,1,GETDATE());SELECT @@IDENTITY */
方式2
IDbContext context = new MsSqlContext(DbHelper.ConnectionString); User user = new User(); user.Name = "lu"; user.Age = 18; user.Gender = Gender.Man; user.CityId = 1; user.OpTime = new DateTime(1992, 1, 16); //會自動將自增 Id 設置到 user 的 Id 屬性上 user = context.Insert(user); /* * String @P_0 = "lu"; Gender @P_1 = Man; Int32 @P_2 = 18; Int32 @P_3 = 1; DateTime @P_4 = "1992/1/16 0:00:00"; INSERT INTO [Users]([Name],[Gender],[Age],[CityId],[OpTime]) VALUES(@P_0,@P_1,@P_2,@P_3,@P_4);SELECT @@IDENTITY */
第一種方式比較接近 sql 的寫法,可以有選擇的插入。第二種方式則會將所有映射的字段都插入。對於插入數據需求,這兩種方式我想應該可以滿足了。
更新數據
方式1
以 lambda 的形式更新:
MsSqlContext context = new MsSqlContext(DbHelper.ConnectionString); context.Update<User>(a => new User() { Name = a.Name, Age = a.Age + 100, Gender = Gender.Man, OpTime = DateTime.Now }, a => a.Id == 1); /* * UPDATE [Users] SET [Name]=[Users].[Name],[Age]=([Users].[Age] + 100),[Gender]=1,[OpTime]=GETDATE() WHERE [Users].[Id] = 1 */ //批量更新 //給所有女性朋友年輕 10 歲 context.Update<User>(a => new User() { Age = a.Age - 10, OpTime = DateTime.Now }, a => a.Gender == Gender.Woman); /* * UPDATE [Users] SET [Age]=([Users].[Age] - 10),[OpTime]=GETDATE() WHERE [Users].[Gender] = 2 */
方式2
MsSqlContext context = new MsSqlContext(DbHelper.ConnectionString); User user = new User(); user.Id = 1; user.Name = "lu"; user.Age = 28; user.Gender = Gender.Man; user.OpTime = DateTime.Now; context.Update(user); //會更新所有映射的字段 /* * String @P_0 = "lu"; Gender @P_1 = Man; Int32 @P_2 = 28; Nullable<Int32> @P_3 = NULL; DateTime @P_4 = "2016/7/8 11:28:27"; Int32 @P_5 = 1; UPDATE [Users] SET [Name]=@P_0,[Gender]=@P_1,[Age]=@P_2,[CityId]=@P_3,[OpTime]=@P_4 WHERE [Users].[Id] = @P_5 */ /* * 支持只更新屬性值已變的屬性 */ context.TrackEntity(user);//在上下文中跟蹤實體 user.Name = user.Name + "1"; context.Update(user);//這時只會更新被修改的字段 /* * String @P_0 = "lu1"; Int32 @P_1 = 1; UPDATE [Users] SET [Name]=@P_0 WHERE [Users].[Id] = @P_1 */
像第一種方式可以批量更新數據方法,我覺得在實際開發中很有必要,且不可或缺。
之前,同事用 EF 開發一個功能,需要批量更新,他的做法是將所有滿足條件的數據給查出來,然后挨個賦值,最后統一 SaveChanges(),開發時由於數據量少,沒發現問題。部署上線了以后,用戶隨便點擊一下保存,這個方法執行都要好久,瞬間懵了,不多說為什么,你懂的!雖然有人基於 EF 做了一個支持批量更新的 EF 擴展類庫,經過各方面考慮,最終不敢輕易冒風險引入項目中。那就拼接一句批量 Update 的sql唄,原生態執行。Oh!想到拼 sql,心中一萬個草泥馬路過!!!都 .net4.5+ 時代,.net Core 都出了,還執着手寫sql?這費時費力的活咱90后程序員可不干!雖然我 sql 寫得很溜,但不到沒轍的時候我是不會去拼 sql。項目開發階段,我是秉着怎么快怎么做原則,有能10分鍾解決問題的方式,我是不會選擇10分鍾+1秒的其他方法!多一秒也不行!至於優化,那是后事。
自己動手,能根據需求定制理想的功能,這也是我造輪子的原因之一。
刪除數據
方式1
以 lambda 的形式刪除:
MsSqlContext context = new MsSqlContext(DbHelper.ConnectionString); context.Delete<User>(a => a.Id == 1); /* * DELETE [Users] WHERE [Users].[Id] = 1 */ //批量刪除 //刪除所有不男不女的用戶 context.Delete<User>(a => a.Gender == null); /* * DELETE [Users] WHERE [Users].[Gender] IS NULL */
方式2
MsSqlContext context = new MsSqlContext(DbHelper.ConnectionString); User user = new User(); user.Id = 1; context.Delete(user); /* * Int32 @P_0 = 1; DELETE [Users] WHERE [Users].[Id] = @P_0 */
刪除數據就這么簡單。
事務支持
Chloe 的事務操作方法在 DbContext.CurrentSession 屬性里:
MsSqlContext context = new MsSqlContext(DbHelper.ConnectionString); IDbSession dbSession = context.CurrentSession; try { dbSession.BeginTransaction(); //to do somethings here... dbSession.CommitTransaction(); } catch { dbSession.RollbackTransaction(); }
事務是每個 ORM 必備的功能。太簡單,本來不想寫進來的,想想,寫吧,湊個字數也好。
結語
Chloe.ORM 主打便捷、高效,無論是 IDbContext 還是 IQuery<T> 接口,設計極其簡單,基本一看就懂,一用就會。並且不依賴任何配置,上手簡單,傳個數據庫連接字符串就能跑,我就喜歡這樣傻瓜式開發。話說有一點我很納悶,C# 是一門高度簡化的語言,很多人說微軟把程序員培養得越來越傻,試問,傻瓜式開發不好嗎?這是開發語言的進步啊,我們為什么要拒絕!那些人是不是都喜歡折騰?說句不好聽的,如果一門語言真把一個程序員變“傻”,我想,那位程序員肯定不是好程序員!扯遠了,嘿嘿。
秉着開源共享精神,促進國內開源事業發展,Chloe.ORM 完全開源,遵循 Apache2.0 協議。
GitHub:https://github.com/shuxinqin/Chloe
開源中國:http://www.oschina.net/p/chloe-orm
ps:小弟不才,文化、技術功底有限,必有不足之處,望各路大神多多指點,非常感謝。也請有異議的同學,點反對的同時,麻煩也留個建議再離開,行嗎?謝謝。