为什么开发 (背景)
最开始使用的是 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 下载源代码 https://gitee.com/wangshuyu/LambdaToSql , 在项目中直接使用
通过 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 项目作者开源
来源: https://www.cnblogs.com/shuyu/p/9307005.html