【原创】基于.NET的轻量级高性能 ORM - TZM.XFramework 之让代码更优雅
【前言】
大家好,我是TANZAME。出乎意料的,我们在立冬的前一天又见面了,天气慢慢转凉,朋友们注意添衣保暖,愉快撸码。距离 TZM.XFramework 的首秀已数月有余,期间收到不少朋友的鼓励、建议和反馈,在此致以深深的感谢。
不少围观的朋友经常问题我,.NET 体系下优秀的 O/RM 官方的有EF,第三方的有linq2db (国外)、StackExchange/Dapper (国外)、NHibernate (国外)、PetaPoco (国外)、Freesql (国内)等等,What's your problem?Ok,咱们就用一分钟的时间聊聊如何用 ORM 让代码变得更优雅更加清爽。
【正文】
相信朋友们都遇到过这样的场景:要插入/删除/修改的数据来自外键表,怎么办?先查出来再进行接下的操作吗,别这样老铁,至少两次以上的数据库访问,从性能角度来说并不是最优的做法。手撸纯 SQL吗,看起来还行至少不会那么令人不舒服。如果有 ORM 能帮我们撸这种 SQL,岂不更痛快?来看看我们的 ORM 是怎么操作的:
1. 多表关联更新
// 更新本表值等于别表的字段值
var query =
from a in context.GetTable<Model.Client>()
join b in context.GetTable<Model.CloudServer>() on a.CloudServerId equals b.CloudServerId
join c in context.GetTable<Model.ClientAccount>() on a.ClientId equals c.ClientId
where c.AccountId == "1"
select a;
context.Update<Model.Client, Model.CloudServer, Model.ClientAccount>((a, b, c) => new
{
CloudServerId = b.CloudServerId,
Qty = c.Qty > 0 ? c.Qty : 1,
}, query);
context.SubmitChanges(); -- 产生的SQL
--UPDATE t0 SET
--t0.[CloudServerId] = t1.[CloudServerId],
--t0.[Qty] = (CASE WHEN t2.[Qty] > @p1 THEN t2.[Qty] ELSE @p2 END)
--FROM [Bas_Client] AS [t0]
--INNER JOIN [Sys_CloudServer] t1 ON t0.[CloudServerId] = t1.[CloudServerId]
--INNER JOIN [Bas_ClientAccount] t2 ON t0.[ClientId] = t2.[ClientId]
--WHERE t2.[AccountId] = @p3
仔细观察查询语义和对应的 SQL 不难发现,from a in context.GetTable 这一句正是对应了 UPDAE *** FROM TABLE 这一句,接下来就是关联到外键表也即是 INNER JOIN TABLE,最后是我们熟悉的 WHERE 语句。有一个特别的地方就是 Oracle 它没有 UPDATE FROM 这种语法,只能用 MERGE INTO 来代替。来看看源码是怎么实现的:
// 预先解析表别名,将查询语义中出现的如a,b,c这些变量表达成 t0,t1,t2的形式
TableAliasCache aliases = this.PrepareAlias<T>(uQueryInfo.SelectInfo, token);
ExpressionVisitorBase visitor = null;
// 解析UPDATE的字段
visitor = new UpdateExpressionVisitor(this, aliases, uQueryInfo.Expression);
visitor.Write(builder);
// FROM 片段
builder.AppendNewLine();
builder.Append("FROM ");
builder.AppendMember(typeRuntime.TableName, !typeRuntime.IsTemporary);
builder.AppendAs("t0"); var cmd2 = new MappingCommand(this, aliases, token) { HasMany = uQueryInfo.SelectInfo.HasMany };
// 解析外键表
visitor = new JoinExpressionVisitor(this, aliases, uQueryInfo.SelectInfo.Joins);
visitor.Write(cmd2.JoinFragment);
// 解析WHERE条件
visitor = new WhereExpressionVisitor(this, aliases, uQueryInfo.SelectInfo.WhereExpression);
visitor.Write(cmd2.WhereFragment);
cmd2.AddNavMembers(visitor.NavMembers);
// 最后合并所有的片断形成最终SQL语句
builder.Append(cmd2.CommandText);
2. 多表关联插入
// 多表关联批量新增
var query =
from a in context.GetTable<Model.Client>()
join b in context.GetTable<Model.CloudServer>() on a.CloudServerId equals b.CloudServerId
where a.ClientId <= 5 && b.CloudServerId != 0
select new Model.Client
{
ClientId = DbFunction.RowNumber<int>(x => a.ClientId) + (maxClientId + 2),
ClientCode = "ABC2",
ClientName = "啊啵呲2",
CloudServerId = b.CloudServerId,
State = 2,
ActiveDate = DateTime.Now
};
context.Insert(query); -- 产生的SQL
--INSERT INTO [Bas_Client]([ClientId],[ClientCode],[ClientName],[CloudServerId],[State],[ActiveDate])
--SELECT
--ROW_NUMBER() Over(Order By t0.[ClientId]) + @p17 + @p18 AS [ClientId],
--@p19 AS [ClientCode],
--@p20 AS [ClientName],
--t1.[CloudServerId] AS [CloudServerId],
--@p21 AS [State],
--@p22 AS [ActiveDate]
--FROM [Bas_Client] t0
--INNER JOIN [Sys_CloudServer] t1 ON t0.[CloudServerId] = t1.[CloudServerId]
--WHERE t0.[ClientId] <= @p23 AND t1.[CloudServerId] <> @p24
从产生的 SQL 可以看出,除去第一行的 INSERT,剩下的就是整个 SELECT 语句,也就是上面示例代码中 query 变量表示的查询语义。这里需要注意的是在解析 SELECT 语句的同时要把所的字段记录下来,INSERT INTO 语句需要拼接上这些字段。来看看代码实现:
// INSERT INTO 片断
builder.Append("INSERT INTO ");
builder.AppendMember(typeRuntime.TableName, !typeRuntime.IsTemporary);
builder.Append('('); // 解析 SELECT 块
MappingCommand cmd2 = this.ParseSelectCommand(nQueryInfo.SelectInfo, 0, true, token) as MappingCommand;
int i = 0;
// 拼接 INSERT INTO 字段
foreach (Column column in cmd2.PickColumns)
{
builder.AppendMember(column.NewName);
if (i < cmd2.PickColumns.Count - 1) builder.Append(',');
i++;
}
builder.Append(')');
// 最后合并所有的片断形成最终SQL语句
builder.AppendNewLine();
builder.Append(cmd2.CommandText);
3. 多表关联删除
// Query 关联批量删除
var query =
from a in context.GetTable<Model.Client>()
join b in context.GetTable<Model.ClientAccount>() on a.ClientId equals b.ClientId
join c in context.GetTable<Model.ClientAccountMarket>() on new { b.ClientId, b.AccountId } equals new { c.ClientId, c.AccountId }
where c.ClientId > 100 && c.AccountId == "1" && c.MarketId == 1
select a;
context.Delete<Model.Client>(query1); -- 产生的SQL
--DELETE t0 FROM [Bas_Client] t0
--INNER JOIN [Bas_ClientAccount] t1 ON t0.[ClientId] = t1.[ClientId]
--INNER JOIN [Bas_ClientAccountMarket] t2 ON t1.[ClientId] = t2.[ClientId] AND t1.[AccountId] = t2.[AccountId]
--WHERE t2.[ClientId] > @p2 AND t2.[AccountId] = @p3 AND t2.[MarketId] = @p4
删除跟更新的更新的原理是一样的,无非是 UPDATE 换成了 DELETE。另外 Oracle 也没有 DELETE FROM 语法,我们换一种取巧一下,用 ROWID 一样能达到关联删除的效果。来看看最后的代码实现:
// DELETE FROM 片断
builder.Append("DELETE t0 FROM ");
builder.AppendMember(typeRuntime.TableName, !typeRuntime.IsTemporary);
builder.Append(" t0 ");
// 预先解析表别名,将查询语义中出现的如a,b,c这些变量表达成 t0,t1,t2的形式
TableAliasCache aliases = this.PrepareAlias<T>(dQueryInfo.SelectInfo, token);
var cmd2 = new MappingCommand(this, aliases, token) { HasMany = dQueryInfo.SelectInfo.HasMany };
// 解析外键表
ExpressionVisitorBase visitor = new JoinExpressionVisitor(this, aliases, dQueryInfo.SelectInfo.Joins);
visitor.Write(cmd2.JoinFragment);
// 解析WHERE条件
visitor = new WhereExpressionVisitor(this, aliases, dQueryInfo.SelectInfo.WhereExpression);
visitor.Write(cmd2.WhereFragment);
cmd2.AddNavMembers(visitor.NavMembers);
// 最后合并所有的片断形成最终SQL语句
builder.Append(cmd2.CommandText);
【结语】
经过大半月的努力,TZM.XFramework 也已正式支持 SQLite了,托管地址:GitHub托管地址:https://github.com/TANZAME/XFramework 。最后借用某公众号上面的一句话与大家共勉,有趣和好奇心是为了取悦自己,然后才能有意思和有用是去取悦别人。撸码不易,不喜轻喷,有不同看法老友欢迎加群交流。
技术交流群:816425449
【原创】基于.NET的轻量级高性能 ORM - TZM.XFramework 之让代码更优雅的更多相关文章
- 【原创】基于.NET的轻量级高性能 ORM - TZM.XFramework
[前言] 接上一篇<[原创]打造基于Dapper的数据访问层>,Dapper在应付多表自由关联.分组查询.匿名查询等应用场景时不免显得吃力,经常要手写SQL语句(或者用工具生成SQL配置文 ...
- 【原创】基于.NET的轻量级高性能 ORM - TZM.XFramework 之优雅增删改
[前言] 大家好,我是TANZAME.出乎意料的,我们在立冬的前一天又见面了,天气慢慢转凉,朋友们注意添衣保暖,愉快撸码.距离 TZM.XFramework 的首秀已数月有余,期间收到不少朋友的鼓励. ...
- 轻量级高性能ORM框架:Dapper高级玩法
Dapper高级玩法1: 数据库中带下划线的表字段自动匹配无下划线的Model字段. Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; 备 ...
- PetaPoco - 轻量级高性能的ORM框架(支持.NET Core)
我们都知道ORM全称叫做Object Relationship Mapper,也就是可以用object来map我们的db. 而且市面上的orm框架有很多,有重量级的Entity Framework,有 ...
- 轻量级.NET ORM、高性能.NET ORM 之 SqlSugar 开源ORM - ASP.NET
3.0最新API: http://www.cnblogs.com/sunkaixuan/p/5911334.html 1.前言/Preface SqlSugar从去年到现在已经一年了,版本从1.0升到 ...
- 分享自己写的基于Dapper的轻量级ORM框架~
1.说明 本项目是一个使用.NET Standard 2.0开发的,基于 Dapper 的轻量级 ORM 框架,包含基本的CRUD以及根据表达式进行一些操作的方法,目前只针对单表,不包含多表连接操作. ...
- Android高性能ORM数据库DBFlow入门
DBFlow,综合了 ActiveAndroid, Schematic, Ollie,Sprinkles 等库的优点.同时不是基于反射,所以性能也是非常高,效率紧跟greenDAO其后.基于注解,使用 ...
- 基于nginx+lua+redis高性能api应用实践
基于nginx+lua+redis高性能api应用实践 前言 比较传统的服务端程序(PHP.FAST CGI等),大多都是通过每产生一个请求,都会有一个进程与之相对应,请求处理完毕后相关进程自动释放. ...
- FluentData - 轻量级.NET ORM持久化技术解决方式
FluentData - 轻量级.NET ORM持久化技术解决方式 文件夹: 一.什么是ORM? 二.使用ORM的优势 三.使用ORM的缺点 四.NET下的ORM框架有哪些? 五.几 ...
随机推荐
- Java 学习笔记之 Synchronized锁重入
Synchronized锁重入: 当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁.这也证明在一个Synchronized方法/块的内部调用本类的其他Synchronized方法 ...
- k阶斐波那契数列fibonacci第n项求值
已知K阶斐波那契数列定义为:f0 = 0, f1 = 0, … , fk-2 = 0, fk-1 = 1;fn = fn-1 + fn-2 + … + fn-k , n = k , k + 1, … ...
- Flutter 错误捕获的正确姿势
背景 我们知道,在软件开发过程中,错误和异常总是在所难免. 不管是客户端的逻辑错误导致的,还是服务器的数据问题导致的,只要出现了异常,我们都需要一个机制来通知我们去处理. 在 APP 的开发过程中,我 ...
- 从0开始学FreeRTOS-1
我们知道,(单核)单片机某一时刻只能干一件事,会造成单片机资源的浪费,而且还有可能响应不够及时,所以,在比较庞大的程序或者是要求实时性比较高的情况下,我们可以移植操作系统.因为这种情况下操作系统比裸机 ...
- 并发编程之原子操作Atomic&Unsafe
原子操作:不能被分割(中断)的一个或一系列操作叫原子操作. 原子操作Atomic主要有12个类,4种类型的原子更新方式,原子更新基本类型,原子更新数组,原子更新字段,原子更新引用.Atomic包中的类 ...
- python编程基础之十八
字符串的查找和替换常用函数: str.count(sub,start = 0,end = len(str)) 计算sub 在str中出现的次数,[start,end)寻找区间 str.find(str ...
- 分库分表(6)--- SpringBoot+ShardingSphere实现分表+ 读写分离
分库分表(6)--- ShardingSphere实现分表+ 读写分离 有关分库分表前面写了五篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理论 ...
- 【NOIP2011】选择客栈
题文: 丽江河边有n 家很有特色的客栈,客栈按照其位置顺序从1 到n 编号.每家客栈都按照某一种色调进行装饰(总共k 种,用整数0 ~ k-1 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的 ...
- SEER见证人操作指南
SEER的见证人设计 共识方式 在区块生产者的产生方式上,SEER采取了PoS的共识方式,用户通过智能合约抵押自己持有的SEER竞选主力见证人(区块生产者). 对于SEER区块链来说,制约区块链TPS ...
- IT爱心求助站
最近发生的一些事情,让我对自己的专业有了另外一层认识. 小尹同学,你是做软件的是吗?能否帮我看一下我的电脑问题? 老同学,我的电脑安装一个软件这么都装不上,能否帮我看一下呢? 邻居你好,我的手机怎么没 ...