话说C#程序员人手一个ORM
话说C#程序员人手一个ORM,确实没有必要再写ORM了,不过我的ORM并不是新的,是从DBHelper演化过来的,算是DBHelper魔改版。
目前流行的ORM有EF、Dapper、SqlSugar、FreeSql、Chloe等,有经验的程序员总是在这些ORM基础上或者在DBHelper基础上再封装一套,再加上自己写ORM的,可谓人手一个ORM。可能是因为在框架当中,ORM入门相对简单吧,不过做好很难。
本项目来源:2014年我在上家公司做ERP、CRM之类的BS管理系统项目;当时公司用的就是OracleHelper、SqlServerHelper之类的,2015年开始尝试修改,当时支持了事务,不过仍是静态的帮助类;2016年我把OracleHelper、SqlServerHelper、MySqlHelper、SQLiteHelper合成了一个DBHelper,不过仍是静态类。2020年,终于发现静态DBHelper多线程并发,事务这块有BUG,所以改造成了非静态的。后来又加入SqlString类,重构以支持更多数据库,方便扩展支持更多数据库种类。最近简单支持了一下Lambda表达式。几经修改,原来DBHelper的那些方法几乎没怎么变。10年不变的API才是好API。
主要是自己用的,如果别人要用的话,建议从gitee或github上拉源码下来,源码在手好控制,而且相对简单,有经验的程序员不难看懂,可以自己调试修改扩展,如果lambda hold不住,有问题,就原生SQL。
跟主流ORM相比还是比较欠缺的,它只是一个DBHelper。
该ORM介绍及使用示例如下:
DBHelper
简介
一款轻量级ORM,查询使用原生SQL,查询结果映射到实体类,增删改支持实体类,支持Oracle、MSSQL、MySQL、SQLite等多种数据库,有配套Model生成器,方便自己扩展以支持更多数据库
特点
- 支持Oracle、MSSQL、MySQL、SQLite四种数据库
- 方便扩展以支持更多关系数据库
- 有配套的Model生成器
- insert、update、delete操作无需写SQL
- 查询使用原生SQL
- 查询结果通过映射转成实体类或实体类集合
- 支持参数化查询,通过SqlString类提供非常方便的参数化查询
- 支持连接多个数据源
- 单表查询、单表分页查询、简单的联表分页查询支持Lambda表达式
- 支持原生SQL和Lambda表达式混写
优点
- 代码实现比较简单,有经验的程序员容易掌控代码,自己修改和扩展
- 查询使用原生SQL
缺点
- 联表查询对Lambda表达式的支持比较弱
- 复杂查询不支持Lambda表达式
建议
- 单表查询可以使用Lambda表达式
- 联表查询以及复杂查询建议使用原生SQL或原生SQL和Lambda表达式混写
作者邮箱
651029594@qq.com
示例
定义数据库对象
public class DBHelper
{
#region 变量
private static ISessionHelper _sessionHelper = new SessionHelper(ConfigurationManager.ConnectionStrings["DefaultConnection"].ToString(), DBType.MySQL);
#endregion
#region 获取 ISession
/// <summary>
/// 获取 ISession
/// </summary>
public static ISession GetSession()
{
return _sessionHelper.GetSession();
}
#endregion
#region 获取 ISession (异步)
/// <summary>
/// 获取 ISession (异步)
/// </summary>
public static async Task<ISession> GetSessionAsync()
{
return await _sessionHelper.GetSessionAsync();
}
#endregion
}
使用Model生成器生成实体类
- 实体类放在Models文件夹中
- 扩展实体类放在ExtModels文件夹中
- 实体类和扩展实体类使用partial修饰,实际上是一个类,放在不同的文件中
- 如果需要添加自定义属性,请修改ExtModels,不要修改Models
实体类示例
/// <summary>
/// 订单表
/// </summary>
[Serializable]
[DBTable("bs_order")]
public partial class BsOrder
{
/// <summary>
/// 主键
/// </summary>
[DBKey]
[DBField]
public string Id { get; set; }
/// <summary>
/// 订单时间
/// </summary>
[DBField("order_time")]
public DateTime OrderTime { get; set; }
/// <summary>
/// 订单金额
/// </summary>
[DBField]
public decimal? Amount { get; set; }
/// <summary>
/// 下单用户
/// </summary>
[DBField("order_userid")]
public long OrderUserid { get; set; }
/// <summary>
/// 订单状态(0草稿 1已下单 2已付款 3已发货 4完成)
/// </summary>
[DBField]
public int Status { get; set; }
/// <summary>
/// 备注
/// </summary>
[DBField]
public string Remark { get; set; }
/// <summary>
/// 创建者ID
/// </summary>
[DBField("create_userid")]
public string CreateUserid { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[DBField("create_time")]
public DateTime CreateTime { get; set; }
/// <summary>
/// 更新者ID
/// </summary>
[DBField("update_userid")]
public string UpdateUserid { get; set; }
/// <summary>
/// 更新时间
/// </summary>
[DBField("update_time")]
public DateTime? UpdateTime { get; set; }
}
修改扩展实体类
- 修改扩展实体类,添加自定义属性
- 下面的扩展实体类中,查询时OrderUserRealName会被自动填充,查询SQL:select t.*, u.real_name as OrderUserRealName from ......
- DetailList不会被自动填充,需要手动查询
扩展实体类示例
/// <summary>
/// 订单表
/// </summary>
public partial class BsOrder
{
/// <summary>
/// 订单明细集合
/// </summary>
public List<BsOrderDetail> DetailList { get; set; }
/// <summary>
/// 下单用户姓名
/// </summary>
public string OrderUserRealName { get; set; }
/// <summary>
/// 下单用户名
/// </summary>
public string OrderUserName { get; set; }
}
添加
public void Insert(SysUser info)
{
using (var session = DBHelper.GetSession())
{
session.Insert(info);
}
}
批量添加
public void Insert(List<SysUser> list)
{
using (var session = DBHelper.GetSession())
{
session.Insert(list);
}
}
修改
public void Update(SysUser info)
{
using (var session = DBHelper.GetSession())
{
session.Update(info);
}
}
批量修改
public void Update(List<SysUser> list)
{
using (var session = DBHelper.GetSession())
{
session.Update(list);
}
}
删除
public void Delete(string id)
{
using (var session = DBHelper.GetSession())
{
session.DeleteById<SysUser>(id);
}
}
条件删除
using (var session = DBHelper.GetSession())
{
session.DeleteByCondition<SysUser>(string.Format("id>=12"));
}
查询单个记录
public SysUser Get(string id)
{
using (var session = DBHelper.GetSession())
{
return session.FindById<SysUser>(id);
}
}
using (var session = DBHelper.GetSession())
{
return session.FindBySql<SysUser>("select * from sys_user");
}
简单查询
using (var session = DBHelper.GetSession())
{
string sql = "select * from CARINFO_MERGE";
List<CarinfoMerge> result = session.FindListBySql<CarinfoMerge>(sql);
}
条件查询
public List<BsOrder> GetList(int? status, string remark, DateTime? startTime, DateTime? endTime)
{
using (var session = DBHelper.GetSession())
{
SqlString sql = session.CreateSqlString(@"
select t.*, u.real_name as OrderUserRealName
from bs_order t
left join sys_user u on t.order_userid=u.id
where 1=1");
sql.AppendIf(status.HasValue, " and t.status=@status", status);
sql.AppendIf(!string.IsNullOrWhiteSpace(remark), " and t.remark like concat('%',@remark,'%')", remark);
sql.AppendIf(startTime.HasValue, " and t.order_time>=STR_TO_DATE(@startTime, '%Y-%m-%d %H:%i:%s') ", startTime.Value.ToString("yyyy-MM-dd HH:mm:ss"));
sql.AppendIf(endTime.HasValue, " and t.order_time<=STR_TO_DATE(@endTime, '%Y-%m-%d %H:%i:%s') ", endTime.Value.ToString("yyyy-MM-dd HH:mm:ss"));
sql.Append(" order by t.order_time desc, t.id asc ");
List<BsOrder> list = session.FindListBySql<BsOrder>(sql.SQL, sql.Params);
return list;
}
}
分页查询
public List<BsOrder> GetListPage(ref PageModel pageModel, int? status, string remark, DateTime? startTime, DateTime? endTime)
{
using (var session = DBHelper.GetSession())
{
SqlString sql = session.CreateSqlString(@"
select t.*, u.real_name as OrderUserRealName
from bs_order t
left join sys_user u on t.order_userid=u.id
where 1=1");
sql.AppendIf(status.HasValue, " and t.status=@status", status);
sql.AppendIf(!string.IsNullOrWhiteSpace(remark), " and t.remark like concat('%',@remark,'%')", remark);
sql.AppendIf(startTime.HasValue, " and t.order_time>=STR_TO_DATE(@startTime, '%Y-%m-%d %H:%i:%s') ", startTime.Value.ToString("yyyy-MM-dd HH:mm:ss"));
sql.AppendIf(endTime.HasValue, " and t.order_time<=STR_TO_DATE(@endTime, '%Y-%m-%d %H:%i:%s') ", endTime.Value.ToString("yyyy-MM-dd HH:mm:ss"));
string orderby = " order by t.order_time desc, t.id asc ";
pageModel = session.FindPageBySql<BsOrder>(sql.SQL, orderby, pageModel.PageSize, pageModel.CurrentPage, sql.Params);
return pageModel.GetResult<BsOrder>();
}
}
事务
public string Insert(BsOrder order, List<BsOrderDetail> detailList)
{
using (var session = DBHelper.GetSession())
{
try
{
session.BeginTransaction();
order.Id = Guid.NewGuid().ToString("N");
order.CreateTime = DateTime.Now;
decimal amount = 0;
foreach (BsOrderDetail detail in detailList)
{
detail.Id = Guid.NewGuid().ToString("N");
detail.OrderId = order.Id;
detail.CreateTime = DateTime.Now;
amount += detail.Price * detail.Quantity;
session.Insert(detail);
}
order.Amount = amount;
session.Insert(order);
session.CommitTransaction();
return order.Id;
}
catch (Exception ex)
{
session.RollbackTransaction();
Console.WriteLine(ex.Message + "\r\n" + ex.StackTrace);
throw ex;
}
}
}
异步查询
public async Task<List<BsOrder>> GetListPageAsync(PageModel pageModel, int? status, string remark, DateTime? startTime, DateTime? endTime)
{
using (var session = await DBHelper.GetSessionAsync())
{
SqlString sql = session.CreateSqlString(@"
select t.*, u.real_name as OrderUserRealName
from bs_order t
left join sys_user u on t.order_userid=u.id
where 1=1");
sql.AppendIf(status.HasValue, " and t.status=@status", status);
sql.AppendIf(!string.IsNullOrWhiteSpace(remark), " and t.remark like concat('%',@remark,'%')", remark);
sql.AppendIf(startTime.HasValue, " and t.order_time>=STR_TO_DATE(@startTime, '%Y-%m-%d %H:%i:%s') ", startTime.Value.ToString("yyyy-MM-dd HH:mm:ss"));
sql.AppendIf(endTime.HasValue, " and t.order_time<=STR_TO_DATE(@endTime, '%Y-%m-%d %H:%i:%s') ", endTime.Value.ToString("yyyy-MM-dd HH:mm:ss"));
string orderby = " order by t.order_time desc, t.id asc ";
pageModel = await session.FindPageBySqlAsync<BsOrder>(sql.SQL, orderby, pageModel.PageSize, pageModel.CurrentPage, sql.Params);
return pageModel.GetResult<BsOrder>();
}
}
条件查询(使用 ForContains、ForStartsWith、ForEndsWith、ForDateTime、ForList 等辅助方法)
public List<BsOrder> GetListExt(int? status, string remark, DateTime? startTime, DateTime? endTime, string ids)
{
using (var session = DBHelper.GetSession())
{
SqlString sql = session.CreateSqlString(@"
select t.*, u.real_name as OrderUserRealName
from bs_order t
left join sys_user u on t.order_userid=u.id
where 1=1");
sql.AppendIf(status.HasValue, " and t.status=@status", status);
sql.AppendIf(!string.IsNullOrWhiteSpace(remark), " and t.remark like @remark", sql.ForContains(remark));
sql.AppendIf(startTime.HasValue, " and t.order_time >= @startTime ", sql.ForDateTime(startTime.Value));
sql.AppendIf(endTime.HasValue, " and t.order_time <= @endTime ", sql.ForDateTime(endTime.Value));
sql.Append(" and t.id in @ids ", sql.ForList(ids.Split(',').ToList()));
sql.Append(" order by t.order_time desc, t.id asc ");
List<BsOrder> list = session.FindListBySql<BsOrder>(sql.SQL, sql.Params);
return list;
}
}
使用Lambda表达式单表查询
单表分页查询使用ToPageList替换ToList即可
public void TestQueryByLambda6()
{
using (var session = DBHelper.GetSession())
{
SqlString<BsOrder> sql = session.CreateSqlString<BsOrder>();
string remark = "测试";
List<BsOrder> list = sql.Query()
.WhereIf(!string.IsNullOrWhiteSpace(remark),
t => t.Remark.Contains(remark)
&& t.CreateTime < DateTime.Now
&& t.CreateUserid == "10")
.OrderByDescending(t => t.OrderTime).OrderBy(t => t.Id)
.ToList();
foreach (BsOrder item in list)
{
Console.WriteLine(ModelToStringUtil.ToString(item));
}
}
}
使用Lambda表达式联表分页查询(简单的联表查询,复杂情况请使用原生SQL或原生SQL和Lambda表达式混写)
public void TestQueryByLambda7()
{
using (var session = DBHelper.GetSession())
{
SqlString<BsOrder> sql = session.CreateSqlString<BsOrder>();
int total;
List<string> idsNotIn = new List<string>() { "100007", "100008", "100009" };
List<BsOrder> list = sql.Query()
.Select<SysUser>(u => u.UserName, t => t.OrderUserName)
.Select<SysUser>(u => u.RealName, t => t.OrderUserRealName)
.LeftJoin<SysUser>((t, u) => t.OrderUserid == u.Id)
.LeftJoin<BsOrderDetail>((t, d) => t.Id == d.OrderId)
.Where<SysUser, BsOrderDetail>((t, u, d) => t.Remark.Contains("订单") && u.CreateUserid == "1" && d.GoodsName != null)
.WhereIf<BsOrder>(true, t => t.Remark.Contains("测试"))
.WhereIf<BsOrder>(true, t => !idsNotIn.Contains(t.Id))
.WhereIf<SysUser>(true, u => u.CreateUserid == "1")
.OrderByDescending(t => t.OrderTime).OrderBy(t => t.Id)
.ToPageList(1, 20, out total);
foreach (BsOrder item in list)
{
Console.WriteLine(ModelToStringUtil.ToString(item));
}
}
}
原生SQL和Lambda表达式混写
public void TestQueryByLambda9()
{
using (var session = DBHelper.GetSession())
{
SqlString<BsOrder> sql = session.CreateSqlString<BsOrder>(@"
select t.*, u.real_name as OrderUserRealName
from bs_order t
left join sys_user u on t.order_userid=u.id
where 1=1");
List<BsOrder> list = sql.Where(t => t.Status == int.Parse("0")
&& t.Status == new BsOrder().Status
&& t.Remark.Contains("订单")
&& t.Remark != null
&& t.OrderTime >= new DateTime(2010, 1, 1)
&& t.OrderTime <= DateTime.Now.AddDays(1))
.WhereIf<SysUser>(true, u => u.CreateTime < DateTime.Now)
.OrderByDescending(t => t.OrderTime).OrderBy(t => t.Id)
.ToList();
foreach (BsOrder item in list)
{
Console.WriteLine(ModelToStringUtil.ToString(item));
}
}
}
源码地址:
https://gitee.com/s0611163/DBHelper
https://github.com/0611163/DBHelper
话说C#程序员人手一个ORM的更多相关文章
- G彩娱乐网一个程序员到一个销售高手的心路历程
0.引言 我大学本科读的是理工科,后来毕业以后,我逐渐走上了程 序员的道路.每天面对电脑一行一行的敲代码,这被我们程序员们戏称为"搬砖头",因为我们所做的事跟民工搬砖头砌墙本质上是 ...
- 面试一个 3 年 Java 程序员,一个问题都不会!
大家周末愉快,当你看到这篇文章的时候,事情已经过去几天了. 刚从洽谈室走出来,心情很复杂! 栈长面试过很多人,不乏知识渊博.技能顶尖的选手,但从未遇到过工作了三年,却一个问题都答不上来.. 这场史无前 ...
- 后端程序员实现一个IP归属地的小程序
在日常开发中,后端主要提供数据以及处理业务逻辑,前端主要提供页面布局以及数据展示.后端程序员对于页面布局接触比较少,但是小程序有完善的文档说明.页面布局也相对简单,实现起来相对简单一些.而且小程序相对 ...
- 向.Net/Unity 程序员推荐一个十分因吹斯听的网站:sharplab.io
0x00 前言 & 过程 & 结论 今天发现了一个十分有趣的网站: https://sharplab.io/ 网站的页面并不复杂,功能大体上能够在题图中得到概括.发现它的过程也很偶然, ...
- 为什么我的子线程更新了 UI 没报错?借此,纠正一些Android 程序员的一个知识误区
开门见山: 这个误区是:子线程不能更新 UI ,其应该分类讨论,而不是绝对的. 半小时前,我的 XRecyclerView 群里面,一位群友私聊我,问题是: 为什么我的子线程更新了 UI 没报错? 我 ...
- 一个Java程序员该有的良好品质
一.前言 多年来,在IT领域,从一个普通的程序员到一个技术主管,再到一个技术经理,再到一个技术主管,他们践踏了许多坑,劳累了许多课程,还背着许多罐子.在提高他们的技术和管理能力的同时,他们一直在考虑如 ...
- 成为python程序员,对疫情过后的毕业生来说,真是一个不错的方向吗?
Python最近几年,一直被炒得很火,这其中有商业因素,但更重要的是即将到来的人工智能时代,而python就恰好是最适合的编程语言. 所以无论是在职的人,还是在校的学生,都想着跟上这一趋势,但,在今年 ...
- 程序员编程利器:20款最好的免费的IDEs和编辑器
程序员编程利器:20款最好的免费的IDEs和编辑器 还没转眼明年可就大年三十了,忙的可真是晕头转了个向,看着亲朋好友们那让人欣羡的小肚腩,不禁感慨,岁月是一把猪饲料,绿了芭蕉,肥了那杨柳小蛮腰,可怜我 ...
- 「编程羽录」上线,程序员必备的这些技能你能get到嘛?
大家好,我是小羽. 好久不见,给大家带来个好消息,小羽的全新专题「编程羽录」系列正式上新,主要是介绍一些关于面试题和经验总结的文章. 会为大家提供一些技术栈之外,程序员还需要的其他方面硬核知识,做到全 ...
随机推荐
- 网站高可用架构之BASE原理
BASE理论是eBay架构师提出的. BASE定理来源:是CAP中一致性和可用性的权衡结果,它来自大规模互联网分布式系统的总结,是基于CAP定理逐步演化而来的. BASE定理的核心思想:即使无法做到强 ...
- Spring Boot应用程序启动器
官网地址:https://docs.spring.io/spring-boot/docs/2.1.12.RELEASE/reference/html/using-boot-build-systems. ...
- JAVA获取访问者IP地址
pom <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomc ...
- 【LeetCode】1154. Day of the Year 解题报告(C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 计算1月1号之间天数 日期 题目地址:https:// ...
- 【LeetCode】359. Logger Rate Limiter 解题报告(C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典 日期 题目地址:https://leetcode ...
- 【LeetCode】809. Expressive Words 解题报告(Python)
[LeetCode]809. Expressive Words 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http ...
- 3998 - Prime k-tuple
{p1,..., pk : p1 < p2 <...< pk} is called a prime k -tuple of distance s if p1, p2,..., pk ...
- Tomcat 组成与工作原理
开源的 Java Web 应用服务器,实现了 Java EE(Java Platform Enterprise Edition)的部分技术规范,比如 Java Servlet.Java Server ...
- uniapp蓝牙传输中文乱码问题
问题描述:app接收到蓝牙传出过来的二进制数据,1.app进行arrbuff转成16进制字符串 // ArrayBuffer转16进度字符串示例 function ab2hex(buffer) { c ...
- 基于Spring MVC + Spring + MyBatis的【物流系统 - 公司信息管理】
资源下载:https://download.csdn.net/download/weixin_44893902/45601768 练习点设计:模糊查询.删除.新增 一.语言和环境 实现语言:JAVA语 ...