找回密码
 立即注册
首页 业界区 业界 DBShadow.net之化繁为简

DBShadow.net之化繁为简

孓访懔 2026-1-25 15:20:00
一、简化查询

1. 先看一下查询的例子
  1. /// <summary>
  2. /// 账户获取服务
  3. /// </summary>
  4. /// <param name="table"></param>
  5. /// <param name="builder"></param>
  6. public class AccountGetService(AccountTable table, IShadowBuilder builder)
  7. {
  8.     private readonly SqlSource _source = new(builder.DataSource);
  9.     private readonly IParamQuery _accountQuery
  10.         = builder.BuildResult(
  11.             table.ToQuery()
  12.                 .And(account => account.Id.Equal())
  13.                 .ToSelect()
  14.                 .SelectSelfColumns()
  15.         );
  16.     /// <summary>
  17.     /// 获取账户
  18.     /// </summary>
  19.     /// <param name="param"></param>
  20.     /// <param name="token"></param>
  21.     /// <returns></returns>
  22.     public Task GetAsync(Account param, CancellationToken token = default)
  23.         => _accountQuery.GetFirstAsync(_source, param, token);
  24. }
复制代码
2. 调用方式如下
  1. var account = await accountGetService.GetAsync(new Account { Id = 1L });
复制代码
3. 这个例子我们可以简化一下



  • 参数直接使用Id(类型由Account简化为long)
  • 很大一部分数据库操作都只有一个参数(GetById、GetByName等)
  • 为此定义一个含单个属性类有点浪费
  • 按上面的例子使用实体类作为参数也感觉怪怪的
  • DBShadow.net支持这种简化操作
  • 单个参数无需封装,直接使用参数类型即可
  1. public class AccountGetService(AccountTable table, IShadowBuilder builder)
  2. {
  3.     private readonly SqlSource _source = new(builder.DataSource);
  4.     private readonly IParamQuery<long, Account> _accountQuery
  5.         = builder.BuildResult<long, Account>(
  6.             table.ToQuery()
  7.                 .And(account => account.Id.Equal())
  8.                 .ToSelect()
  9.                 .SelectSelfColumns()
  10.         );
  11.     public Task GetAsync(long accountId, CancellationToken token = default)
  12.         => _accountQuery.GetFirstAsync(_source, param, token);
  13. }
  14. var account = await accountGetService.GetAsync(1L);
复制代码
4. 特别注意不要在Dapper中这样使用



  • Dapper是不支持这种简化操作
  • 以下Dapper错误示例会抛出异常
  • System.InvalidOperationException:“Must add values for the following parameters: @Id”
  1. await using var conn = _dataSource.CreateConnection();
  2. var sql = "SELECT "Id","Title","Content","Done","LastTime" FROM "Todo" WHERE "Id"=@Id";
  3. var first = await conn.QueryFirstOrDefaultAsync<Todo>(sql, 1L);
复制代码
二、 集合参数也支持简化操作

1. IN查询简化的例子



  • In的参数化名是可选和前面例子的Equal是一样的,默认字段名
  • In查询的实参支持数组、集合及字典
  • 只有In一个集合参数是支持直接传数组、集合或字典
  • 这时参数名(eg: AccountIds)就无所谓取什么名了
  • 因为不需要反射获取属性值了
  1. /// <summary>
  2. /// 批量账户获取服务
  3. /// </summary>
  4. /// <param name="table"></param>
  5. /// <param name="builder"></param>
  6. public class AccountBatchService(AccountTable table, IShadowBuilder builder)
  7. {
  8.     private readonly SqlSource _source = new(builder.DataSource);
  9.     private readonly IParamQuery<long[], Account> _accountQuery
  10.         = builder.BuildResult<long[], Account>(
  11.             table.ToQuery()
  12.                 .And(account => account.Id.In("AccountIds"))
  13.                 .ToSelect()
  14.                 .SelectSelfColumns()
  15.         );
  16.     /// <summary>
  17.     /// 批量获取账户
  18.     /// </summary>
  19.     /// <param name="accountIds"></param>
  20.     /// <param name="token"></param>
  21.     /// <returns></returns>
  22.     public IAsyncEnumerable GetAsync(long[] accountIds, CancellationToken token = default)
  23.         => _accountQuery.QueryAsync(_source, accountIds, token);
  24. }
复制代码
2. 以上调用的例子如下
  1. [Fact]
  2. public async Task Batch()
  3. {
  4.     var count = 0;
  5.     var service = new AccountBatchService(table, builder);
  6.     var list = service.GetAsync([1L, 2L, 3L]);
  7.     await foreach (var item in list)
  8.     {
  9.         _output.WriteLine($"{item.Id}:{item.Amount}");
  10.         count++;
  11.     }
  12.     Assert.Equal(3, count);
  13. }
  14. // 1:100
  15. // 2:200
  16. // 3:300
复制代码
三、泛型查询

1. 泛型服务类代码



  • 该服务用来按字段Id查询表Account
  • 参数TParam是泛型,这样可以直接使用DTO参数来查询,减少类型转化的开销
  • 返回值TAccount也是泛型,这样就可以直接返回视图模型或者领域模型
  • 这样的泛型服务类就非常的通用
  1. /// <summary>
  2. /// 账户获取泛型服务
  3. /// </summary>
  4. /// <param name="table"></param>
  5. /// <param name="builder"></param>
  6. public class AccountGetService<TParam, TAccount>(AccountTable table, IShadowBuilder builder)
  7. {
  8.     private readonly SqlSource _source = new(builder.DataSource);
  9.     private readonly IParamQuery<TParam, TAccount> _accountQuery
  10.         = builder.BuildResult<TParam, TAccount>(
  11.             table.ToQuery()
  12.                 .And(account => account.Id.Equal())
  13.                 .ToSelect()
  14.                 .SelectSelfColumns()
  15.         );
  16.     /// <summary>
  17.     /// 获取账户
  18.     /// </summary>
  19.     /// <param name="param"></param>
  20.     /// <param name="token"></param>
  21.     /// <returns></returns>
  22.     public Task<TAccount?> GetAsync(TParam param, CancellationToken token = default)
  23.         => _accountQuery.GetFirstAsync(_source, param, token);
  24. }
复制代码
2. 中规中矩的调用方式
  1. var service = new AccountGetService(_table, _builder);
  2. var account = await service.GetAsync(new Account { Id = 1L });
  3. Assert.NotNull(account);
复制代码
3. 支持简单调用方式
  1. var service = new AccountGetService<long, Account>(_table, _builder);
  2. var account = await service.GetAsync(1L);
  3. Assert.NotNull(account);
复制代码
四、总结



  • DBShadow.net预编译比较智能
  • 只有1个参数时支持化繁为简,支持直接传值做为参数值
  • 这样可以节约定义只有一个属性的参数类
  • 参数和返回值类型还可以定义为泛型,可以做到更加灵活
另外源码托管地址: https://github.com/donetsoftwork/DBShadow.net ,欢迎大家直接查看源码。
gitee同步更新:https://gitee.com/donetsoftwork/DBShadow.net
如果大家喜欢请动动您发财的小手手帮忙点一下Star,谢谢!!!

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

7 天前

举报

4 天前

举报

您需要登录后才可以回帖 登录 | 立即注册