為什么開發(背景)
- 最開始使用的是 sqlDbHelper,有微軟的,有自己寫的。
- 后來開始使用比較成熟的框架開發,使用過一段時間的Hibernate,后期主要使用 Entity FrameWork。
- 發現表越多 業務越復雜后,越不好控制項目,所以慢慢的自己根據業務寫了一個小工具,也就是本文說的 LambdaToSql。
- 最開始的功能 主要是准備替代DbHelper的,慢慢的把映射關系加上了,再后來重構了幾次,就慢慢的代替了EF的功能。
- 現在有幾個成熟的項目在使用,軟件也會一直維護下去,現在基本都是核心功能,暫時沒往大而全去做。
ORM介紹
- 鏈式查詢、鏈式更新、鏈式刪除、鏈式插入、復雜模型的查詢、ADO.NET。
- 支持數據庫:現在只支持 MS Sql Server,其他數據庫暫時未做支持處理,里面預留了對其它數據庫支持的接口,但未實現代碼邏輯。
- 數據庫預留接口:Oracle、Mysql、Access。
- 功能: 基本CURD(添加,修改,讀取,刪除)功能,批量修改,DbFirst,表緩存。
- 全部使用Lambda語法,開發簡潔,代碼干凈,后期好維護。
- 有點2:性能高,基本接近於原生ADO,語法簡單,功能強大,持續更新維護。
- 其實LambdaToSql不能算是一個ORM,主要功能其實還應該算是Dapper替代產品,主要是把映射對象通過Lambda形式轉換成sql語句,通過Ado做 CURD操作。
- 缺點1:不支持多表查詢,Join性能還是比較低,但后期還是會支持join查詢。
- 缺點2:暫時不支持外部自定義函數和繼承覆蓋重寫,后期慢慢也會開放出來。
- 如果有想自定義的,可以直接使用源碼改動哦。
性能測試
- 測試環境: 硬盤:三星 SSD 850 EVO; CPU:i7-7700K
- 添加100w條數據 耗時大概250s內
- 查詢100w條數據並生成實體.Tolist(),大概3s
- 100w數據,每頁50條,取中間數據,大概100ms內
- 插入/更新/查詢 單條數據 大概20ms內
- 刪除 單條大概 20ms內
開源地址
- 碼雲gitee: https://gitee.com/wangshuyu/LambdaToSql
- Demo示例:https://gitee.com/wangshuyu/LambdaToSql_Demo
如何安裝
- 源碼方式:可以直接在gitee下載源代碼,在項目中直接使用
- 通過Nuget下載引用: 打開Nuget 搜索:LambdaToSql 就可以了
- Nuget命令方式:
Install-Package LambdaToSql
Config配置,鏈接數據庫
<connectionStrings> <add name="ConnectionString" connectionString="Server=.;Database=LambdaToSql;User ID=sa;Password=abc@123;" providerName="System.Data.SqlClient" /> </connectionStrings>
初始化LambdaToSql 對象
//默認方式 LambdaToSql.SqlClient DB = new LambdaToSql.SqlClient();
//自定義鏈接字符串名稱 var DB = new LambdaToSql.SqlClient(new LambdaToSql.EntityModel.DbContext() { ConnectionStringName = "ConnectionString1", SqlType = LambdaToSql.EntityModel.SqlType.MsSqlServer });
初次使用,如何生成實體類:DbFirst
//生成實體保存路徑 var saveFolder = "d:\\class\\"; //生成全部實體 DB.DbFirst.Create(saveFolder); //生成指定表實體對象 DB.DbFirst.CreateByTable(saveFolder, new List<string>() { "Table_ID", "Table_Guid" });
查詢
/// <summary> /// 查詢 /// </summary> public void Query() { //查詢全部 var list = DB.QueryTable<EntityModel.Table_ID>().ToList(); //Find主鍵查找,支持Guid 和int 自增主鍵 var entity = DB.QueryTable<EntityModel.Table_ID>().Find(200); //In查詢 var arr = new int?[] { 100, 101, 102, 103 }.ToList(); var list1 = DB.QueryTable<EntityModel.Table_ID>(ex => arr.Contains(ex.ID)).ToList(); //Not In 查詢 var list2 = DB.QueryTable<EntityModel.Table_ID>(ex => ex.ID.ExNotIn(arr)).ToList();//有問題 var list2_1 = DB.QueryTable<EntityModel.Table_ID>(ex => arr.NotContains(ex.ID)).ToList();//有問題 // Like 查詢 var list3 = DB.QueryTable<EntityModel.Table_ID>().Where(ex => ex.LoginName.Contains("15")).ToList();//(LoginName like '%15%') var list4 = DB.QueryTable<EntityModel.Table_ID>().Where(ex => ex.LoginName.NotContains("15")).ToList();//(LoginName not like '%15%') //有問題 var list5 = DB.QueryTable<EntityModel.Table_ID>().Where(ex => ex.LoginName.StartsWith("15")).ToList();//(LoginName like '15%') var list6 = DB.QueryTable<EntityModel.Table_ID>().Where(ex => ex.LoginName.EndsWith("15")).ToList();//(LoginName like '%15') //排序 var list7 = DB.QueryTable<EntityModel.Table_ID>().OrderBy(ex => ex.CreateTime).OrderByDescending(ex => ex.LoginName).ToList(); //分組 var list8 = DB.QueryTable<EntityModel.Table_ID>().GroupBy(ex => new { ex.LoginName, ex.UserName }).ToList(); //只取特定字段 var list9 = DB.QueryTable<EntityModel.Table_ID>().Select(ex => new { ex.LoginName, ex.UserName }).ToList(); //top N var list10 = DB.QueryTable<EntityModel.Table_ID>().Take(10).ToList(); //第幾頁 var list11 = DB.QueryTable<EntityModel.Table_ID>().Skip(2).Take(10).ToList(); //取第一條數據 var list12 = DB.QueryTable<EntityModel.Table_ID>().First(); var list13 = DB.QueryTable<EntityModel.Table_ID>().FirstOrDefault(); //分頁 2005,2008使用row_number分頁,2012以上使用offset分頁形式 int total = 0; var list14 = DB.QueryTable<EntityModel.Table_ID>().Skip(15).Take(30).ToPageList(ref total); //分組 select 比原始去重性能要高一些 DB.QueryTable<EntityModel.Table_ID>().GroupBy(ex => new { ex.UserName, ex.LoginName }) .Select(ex => new { ex.UserName, ex.LoginName }) .ToList(); //判斷滿足條件的數據是否存在 var flag = DB.QueryTable<EntityModel.Table_ID>().Any(); //判斷滿足條件的數據是否存在 var flag1 = DB.QueryTable<EntityModel.Table_ID>(ex => ex.ID == 2000).Any(); }
函數處理
/// <summary> /// 函數處理 /// </summary> private void Fun() { //求和 var num1 = DB.QueryTable<EntityModel.Table_ID>().Sum(ex => ex.IsDelete); //最小值 var num2 = DB.QueryTable<EntityModel.Table_ID>().Min(ex => ex.IsDelete); //最大值 var num3 = DB.QueryTable<EntityModel.Table_ID>().Max(ex => ex.IsDelete); //平均值 var num4 = DB.QueryTable<EntityModel.Table_ID>().Avg(ex => ex.IsDelete); //總數 var num5 = DB.QueryTable<EntityModel.Table_ID>().Count(); }
添加
/// <summary> /// 添加數據 /// </summary> public void Inser() { //添加單個實體對象 var entity = new EntityModel.Table_ID() { LoginName = "登錄用戶:", UserName = "用戶名:", PassWord = "密碼-", Gender = "男", IsDelete = 0, Mobile = "15804066511", Remark = "備注", Address = "地址:", CreateTime = DateTime.Now }; var ret = DB.InsertTble(entity).ExecuteNonQuery();//返回主鍵值 //只添加某幾列 var i = DB.InsertTble(entity).InsertColumns(ex => new { ex.LoginName, ex.UserName, ex.Remark }).ExecuteNonQuery(); //忽略某些列 var i1 = DB.InsertTble(entity).IgnoreColumns(ex => new { ex.Mobile, ex.PassWord }).ExecuteNonQuery(); }
修改
- NULL列不做更新處理
- 暫時只支持uniqueidentifier和int自增類型單主鍵
/// <summary> /// 更新數據 /// </summary> public void Update() { //更新單個實體對象 var entity = DB.QueryTable<EntityModel.Table_ID>(ex => ex.ID == 200).FirstOrDefault(); entity.PassWord = "12345"; entity.LoginName = "LambdaToSql"; entity.UserName = "LambdaToSql1"; var i = DB.UpdateTble(entity).ExecuteNonQuery(); //更新特定字段,不指定不更新 var i1 = DB.UpdateTble(entity).UpdateColumns(ex => new { ex.PassWord }).ExecuteNonQuery(); //忽略特定字段,其他字段都更新 var i2 = DB.UpdateTble(entity).IgnoreColumns(ex => new { ex.UserName, ex.PassWord }).ExecuteNonQuery(); //條件更新 不需要取出實體對象 直接數據庫更新 var i3 = DB.UpdateTble(new EntityModel.Table_ID() { PassWord = "123456", LoginName = "12" }).Where(ex => ex.ID == 101).ExecuteNonQuery(true); }
刪除
/// <summary> /// 刪除 /// </summary> public void Delete() { //刪除單個實體,通過主鍵刪除 var entity = DB.QueryTable<EntityModel.Table_ID>(ex => ex.ID == 200).FirstOrDefault(); var i = DB.DeleteTble<EntityModel.Table_ID>(entity).ExecuteNonQuery(); //條件刪除 支持查詢里面的所有條件寫法 var i1 = DB.DeleteTble<EntityModel.Table_ID>(ex => ex.ID == 201).ExecuteNonQuery(); }
事務
事務使用注意:
- 事務只能在同一個SqlClient對象有效;事務只能在同一個SqlClient對象有效;事務只能在同一個SqlClient對象有效;重要的事說三遍
- 跨SqlClient對象請用分布式事務(暫時內置不支持,后續版本會支持分布式事務)
/// <summary> /// 事務 /// </summary> public void Tran() { var sqlClient = new LambdaToSql.SqlClient(); try { sqlClient.BeginTran();//開啟事務 //添加單個實體對象 var entity = new EntityModel.Table_ID() { LoginName = "登錄用戶:", UserName = "用戶名:", PassWord = "密碼-", IsDelete = 0, CreateTime = DateTime.Now }; var entity1 = new EntityModel.Table_ID() { LoginName = "登錄用戶:", UserName = "在破敗中崛起,在寂滅中復蘇。滄海成塵,雷電枯竭,那一縷幽霧又一次臨近大地,世間的枷鎖被打開了,一個全新的世界就此揭開神秘的一角:", PassWord = "密碼-", IsDelete = 0, CreateTime = DateTime.Now }; var entity2 = new EntityModel.Table_ID() { LoginName = "登錄用戶:", UserName = "用戶名:", PassWord = "密碼-", IsDelete = 0, CreateTime = DateTime.Now }; sqlClient.InsertTble(entity).ExecuteNonQuery(); sqlClient.InsertTble(entity1).ExecuteNonQuery();//錯誤 UserName太長=>回滾 sqlClient.InsertTble(entity2).ExecuteNonQuery(); sqlClient.CommitTran();//提交事務 } catch (Exception ex) { sqlClient.RollbackTran();//回滾事務 } }
ADO
/// <summary> /// Ado /// </summary> public void Ado() { var sql = "select top(10) * from table_id"; var sdr = DB.Ado.ExecuteReader(sql); var list = new List<string>(); while (sdr.Read()) { list.Add(sdr[0].ToString()); } sdr.Close(); var Dt = DB.Ado.ExecuteTable(sql); var ls1 = DB.Ado.ExecuteScalar(sql); var ls = DB.Ado.ExecuteScalars(sql); var i = DB.Ado.ExecuteNonQuery("update top (10) table_id set imgurl = 'img1'"); }
后續計划
- 繼續維護代碼和升級新功能
- 把類庫UML圖發布出來
- 把類接口和實現文檔 整理好 發布出來
- 會在開源一個關於WebApi的整體框架結構
- 最近另一個開源項目: https://gitee.com/wangshuyu/CommonLib 這是個通用類庫項目,把平時常用的整理了下,自己也一直在使用此類庫
結尾
- 希望大家多多提bug
- 希望大家多多提意見
- 最后,感謝SqlSugar項目作者開源