为什么开发(背景)
- 最开始使用的是 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项目作者开源