DBShadow横空出世,Dapper.net的天花板盖不住了
一、DBShadow是什么DBShadow是.net开源的高性能ORMDBShadow使用开源项目ShadowSql高效拼接sqlDBShadow使用开源项目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.QueryFirstOrDefaultAsyncTodo(sql, _todo);DbDataSource _dataSource new StringDataSource(Data Sourcefile::memory:;CacheShared, conn new SqliteConnection(conn));2. DBShadow代码使用SqliteEngine处理数据库方言使用Mapper.Default处理类型映射ShadowCachedBuilder用来编译和缓存var first await _shadowSelect.GetFirstAsyncTodo, 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 RatioDapper4.636 us0.0194 us0.0216 us1.000.010.1400-2.38 KB1.00DBShadow4.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 IdId; var first await conn.QueryFirstOrDefaultAsyncTodo(sql, _todo);string ConnectionString Serverlocalhost;DatabaseBenchmarks;Userroot;Password123456;; DbDataSource _dataSource new MySqlDataSource(ConnectionString);2. DBShadow代码使用MySqlEngine处理数据库方言使用Mapper.Default处理类型映射ShadowCachedBuilder用来编译和缓存var first await _shadowSelect.GetFirstAsyncTodo, 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 RatioDapper397.7 us8.83 us9.81 us1.000.038.08 KB1.00DBShadow383.9 us14.62 us15.64 us0.970.047.23 KB0.89四、DBShadow支持事务操作1. 举个事务回滚的栗子建表Accounts账号1初始化余额为100查询账号1余额为100开启事务使用事务把余额设置为90在事务下查询余额为90事务回滚再次查询账号1余额为100DBShadow事务操作很优雅是否事务只与使用哪个处理器或数据源有关正常处理器或数据源可以很方便的转化为事务相关对象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(Id1); var amount await query .ToSelect() .Select(account account.Amount) .GetScalarAsynclong(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) .GetScalarAsynclong(transaction); // 在事务下查询余额为90 // 减成了90 Assert.Equal(90L, amount2); // 事务回滚 await transaction.RollbackAsync(); } var amount3 await query .ToSelect() .Select(account account.Amount) .GetScalarAsynclong(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(Id1); #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.GetScalarAsynclong(SqliteSource); // 查询账号1余额为100 Assert.Equal(100L, amount); // 开启事务 await using var transaction await SqliteSource.BeginTransaction(); { await updateCompiled.ExecuteAsync(transaction); // 使用事务把余额设置为90 var amount2 await amountCompiled.GetScalarAsynclong(transaction); // 在事务下查询余额为90 Assert.Equal(90L, amount2); await transaction.CommitAsync(); // 事务提交 } var amount3 await amountCompiled.GetScalarAsynclong(SqliteSource); // 再次查询账号1余额为90 Assert.Equal(90L, amount3);五、DBShadow解密1. 首先DBShadow基于现代ADO.net1.1 DbDataSource数据连接基于System.Data.Common.DbDataSourceDbDataSource的重要方法CreateConnection相当于数据库连接工厂或连接池1.2 StringDataSource虽然微软推出DbDataSource很多年了,但是业界支持的并不是很好比如Sqlite不支持DbDataSource就算是System.Data也只能.net7才支持这个破破烂烂的世界,需要缝缝补补StringDataSource支持net4.5和netstandard2.0在.net7下StringDataSource是DbDataSource的子类其他情况下DBShadow使用StringDataSource直接代替DbDataSource1.3 IAsyncEnumerable这是异步下的迭代器在异步操作IO流下实现延迟加载和流式计算DBShadow的列表都是基于IAsyncEnumerableEFCore也支持IAsyncEnumerable,但Dapper不支持
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2472805.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!