当前位置: 首页 > news >正文

DBShadow横空出世,Dapper.net的天花板盖不住了

一、DBShadow是什么

  • DBShadow是.net开源的高性能ORM
  • DBShadow使用开源项目ShadowSql高效拼接sql
  • DBShadow使用开源项目PocoEmit.Mapper高效映射查询参数和查询结果
  • 也就是说SqlBuilder(ShadowSql)+OOM(PocoEmit.Mapper)=ORM(DBShadow)

二、DBShadow和Dapper对比一下

1. Dapper代码

await using var conn = _dataSource.CreateConnection(); var sql = "SELECT \"Id\",\"Title\",\"Content\",\"Done\",\"LastTime\" FROM \"Todo\" WHERE \"Id\"=@Id"; var first = await conn.QueryFirstOrDefaultAsync<Todo>(sql, _todo);
DbDataSource _dataSource = new StringDataSource("Data Source=file::memory:;Cache=Shared", conn => new SqliteConnection(conn));

2. DBShadow代码

  • 使用SqliteEngine处理数据库方言
  • 使用Mapper.Default处理类型映射
  • ShadowCachedBuilder用来编译和缓存
var first = await _shadowSelect.GetFirstAsync<Todo, Todo?>(_executor, _todo);
ISqlEngine engine = new SqliteEngine(); ShadowExecutor _executor = ShadowBuilder.CreateCache(engine, Mapper.Default); TodoTable _table = new("Todo"); ISelect _shadowSelect = _table.ToQuery() .And(_table.Id.Equal()) .ToSelect() .SelectSelfColumns();

3. 用BenchmarkDotNet对比一下

  • DBShadow比Dapper快10%
  • 内存也占优
  • 以下是基于.net8,DBShadow支持.net10,Dapper没有.net10版本
  • 为了公平降级对比
  • 其实DBShadow在.net10下更快
MethodMeanErrorStdDevRatioRatioSDGen0Gen1AllocatedAlloc Ratio
Dapper4.636 us0.0194 us0.0216 us1.000.010.1400-2.38 KB1.00
DBShadow4.030 us0.0152 us0.0175 us0.870.010.1300-2.2 KB0.92

三、再用Mysql对比一下

1. Dapper代码

await using var conn = _dataSource.CreateConnection(); var sql = "SELECT `Id`,`Title`,`Content`,`Done`,`LastTime` FROM `Todo` WHERE `Id`=@Id"; var first = await conn.QueryFirstOrDefaultAsync<Todo>(sql, _todo);
string ConnectionString = "Server=localhost;Database=Benchmarks;User=root;Password=123456;"; DbDataSource _dataSource = new MySqlDataSource(ConnectionString);

2. DBShadow代码

  • 使用MySqlEngine处理数据库方言
  • 使用Mapper.Default处理类型映射
  • ShadowCachedBuilder用来编译和缓存
var first = await _shadowSelect.GetFirstAsync<Todo, Todo?>(_executor, _todo);
ISqlEngine engine = new MySqlEngine(); ShadowCachedBuilder _executor = ShadowBuilder.CreateCache(engine, Mapper.Default); TodoTable _table = new("Todo"); ISelect _shadowSelect = _table.ToQuery() .And(_table.Id.Equal()) .ToSelect() .SelectSelfColumns();

3. 再用BenchmarkDotNet对比一下

  • DBShadow比Dapper只快3%
  • 内存占优
  • 由于MySql耗时几乎是Sqlite的100倍,执行代码是一样的(能快3%就很不容易了)
  • MySql慢也与我本机资源限制有关,使用docker搭建MySql,也没用固态硬盘(固态硬盘用在系统盘)
MethodMeanErrorStdDevRatioRatioSDAllocatedAlloc Ratio
Dapper397.7 us8.83 us9.81 us1.000.038.08 KB1.00
DBShadow383.9 us14.62 us15.64 us0.970.047.23 KB0.89

四、DBShadow支持事务操作

1. 举个事务回滚的栗子

  • 建表Accounts
  • 账号1初始化余额为100
  • 查询账号1余额为100
  • 开启事务
  • 使用事务把余额设置为90
  • 在事务下查询余额为90
  • 事务回滚
  • 再次查询账号1余额为100
  • DBShadow事务操作很优雅
  • 是否事务只与使用哪个处理器或数据源有关
  • 正常处理器或数据源可以很方便的转化为事务相关对象
var table = new AccountTable(); try { await SqliteExecutor.ExecuteAsync(table.ToCreate()); // 建表Accounts } catch { } await new SingleInsert(table) .Insert(table.Id.InsertValue(1L)) .Insert(table.Amount.InsertValue(100L)) .ExecuteAsync(SqliteExecutor); // 账号1初始化余额为100 // 查询账号1 var query = table.ToSqlQuery().Where("Id=1"); var amount = await query .ToSelect() .Select(account => account.Amount) .GetScalarAsync<long>(SqliteExecutor); // 查询账号1余额为100 Assert.Equal(100L, amount); // 开启事务 await using var transaction = await SqliteExecutor.BeginTransaction(); { await query.ToUpdate() .Set(account => account.Amount.AssignValue(90L)) .ExecuteAsync(transaction); // 使用事务把余额设置为90 var amount2 = await query .ToSelect() .Select(account => account.Amount) .GetScalarAsync<long>(transaction); // 在事务下查询余额为90 // 减成了90 Assert.Equal(90L, amount2); // 事务回滚 await transaction.RollbackAsync(); } var amount3 = await query .ToSelect() .Select(account => account.Amount) .GetScalarAsync<long>(SqliteExecutor); // 回滚后恢复为100 Assert.Equal(100L, amount3);

2. 再举个事务提交和预编译的栗子

  • 事务提交和事务回滚特别相近,为此增加DBShadow预编译的内容
  • 建表预编译
  • 插入操作预编译
  • 查询账号余额预编译
  • 修改账号余额预编译
  • 建表Accounts
  • 账号1初始化余额为100
  • 查询账号1余额为100
  • 开启事务
  • 使用事务把余额设置为90
  • 在事务下查询余额为90
  • 事务提交
  • 再次查询账号1余额为90
  • 预编译能提高执行性能和稳定性
  • 在事务操作之前预编译很有必要
  • 预编译之后的结果对是否事务数据源都是一样的使用方式(也就是业务代码可以做到通用)
var builder = SqliteExecutor.Builder; var table = new AccountTable(); var query = table.ToSqlQuery().Where("Id=1"); #region Compile // 建表预编译 var createCompiled = builder.BuildQuery(table.ToCreate()); // 插入操作预编译 var insertCompiled = builder.BuildQuery(new SingleInsert(table) .Insert(table.Id.InsertValue(1L)) .Insert(table.Amount.InsertValue(100L))); // 查询账号余额预编译 var amountCompiled = builder.BuildScalar(query .ToSelect() .Select(account => account.Amount)); // 修改账号余额预编译 var updateCompiled = builder.BuildQuery(query.ToUpdate() .Set(account => account.Amount.AssignValue(90L))); #endregion try { await createCompiled.ExecuteAsync(SqliteSource); // 建表Accounts } catch { } await insertCompiled.ExecuteAsync(SqliteSource); // 账号1初始化余额为100 var amount = await amountCompiled.GetScalarAsync<long>(SqliteSource); // 查询账号1余额为100 Assert.Equal(100L, amount); // 开启事务 await using var transaction = await SqliteSource.BeginTransaction(); { await updateCompiled.ExecuteAsync(transaction); // 使用事务把余额设置为90 var amount2 = await amountCompiled.GetScalarAsync<long>(transaction); // 在事务下查询余额为90 Assert.Equal(90L, amount2); await transaction.CommitAsync(); // 事务提交 } var amount3 = await amountCompiled.GetScalarAsync<long>(SqliteSource); // 再次查询账号1余额为90 Assert.Equal(90L, amount3);

五、DBShadow解密

1. 首先DBShadow基于现代ADO.net

1.1 DbDataSource
  • 数据连接基于System.Data.Common.DbDataSource
  • DbDataSource的重要方法CreateConnection
  • 相当于数据库连接工厂或连接池
1.2 StringDataSource
  • 虽然微软推出DbDataSource很多年了,但是业界支持的并不是很好
  • 比如Sqlite不支持DbDataSource
  • 就算是System.Data也只能.net7+才支持
  • 这个破破烂烂的世界,需要缝缝补补
  • StringDataSource支持net4.5+和netstandard2.0+
  • 在.net7+下StringDataSource是DbDataSource的子类
  • 其他情况下DBShadow使用StringDataSource直接代替DbDataSource
1.3 IAsyncEnumerable<>
  • 这是异步下的迭代器
  • 在异步操作IO流下实现延迟加载和流式计算
  • DBShadow的列表都是基于IAsyncEnumerable<>
  • EFCore也支持IAsyncEnumerable<>,但Dapper不支持
http://www.jsqmd.com/news/571884/

相关文章:

  • 天然气脱碳装置厂家推荐:技术实力与2026市场口碑榜单 - 品牌推荐大师
  • 嵌入式系统中的累加和校验算法原理与实现
  • Phi-3-mini-4k-instruct-gguf实操手册:supervisorctl restart后自动加载新模型文件方法
  • 拓朋N37公网对讲机,物流园区高效协同的“沟通神器”
  • 4月1号
  • 国密双证书体系深度解读:为什么你的GMTLS客户端需要两个证书?从ECC到ECDHE模式全解析
  • Wan2.2-I2V-A14B效果展示:生成‘机械齿轮咬合运转’工业风10秒视频
  • 暗黑破坏神2存档修改完全解决方案:从问题诊断到高级应用指南
  • 革新性ESP32开发工具链:从环境构建到智能交互的全流程优化
  • HunyuanVideo-Foley开源镜像部署:24G显存GPU算力深度优化实战
  • intv_ai_mk11部署案例:CSDN GPU云环境免配置镜像开箱即用全流程详解
  • 四川防腐木哪家强?看这家20年老厂如何用“双认证”征服西南潮湿气候 - 深度智识库
  • Phi-4-mini-reasoning效果展示:含单位换算、科学计数法的复合型数学题求解
  • 赋能制造业精益运营——千匠网络制造业仓储物流解决方案重磅来袭 - 圆圆小达人
  • 映翰通 IG502 从零到上云:Modbus RTU + MQTT 工业数据采集实战(附代码)
  • 2026年紫外臭氧清洗机厂家推荐榜:半导体/光学镜片/等离子清洗机源头工厂家选择指南 - 品牌推荐大师
  • 2026最强Java面试八股文(精简、纯手打)
  • Phi-4-mini-reasoning多场景落地实践:编程辅助、数学解题与逻辑分析
  • Graphormer开源模型价值:替代传统QSAR方法的深度学习新范式
  • League Director:用开源工具重构电竞录像制作流程,效率提升200%的全指南
  • 数智出海,全域赋能——千匠网络跨境出口电商系统,重构全球贸易新生态 - 圆圆小达人
  • mysql/mariadb查看慢日志,MariaDB 记录执行过的 SELECT 语句
  • CnDataSeed 发布 :中国高校财政预算数据库(CUFBD)
  • 简书|# 本地好物在乐居 小城生意,选对一处安稳仓储
  • 未来图景对制造系统提出全面理解、
  • 链通能源全场景 数赋交易新未来——千匠网络能源供应链电商系统震撼登场 - 圆圆小达人
  • PyQt 使用QChartView绘制极坐标图(QPolarChart)详解
  • 2026年农用EM菌厂家推荐:种植/水产/发酵剂/功能菌专业供应,如何科学选型不踩坑? - 品牌推荐官
  • 04动态规划
  • cool-admin(midway版)前端路由动画:实现与优化