為什么開發(背景)
- 最開始使用的是 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項目作者開源