跳到主要内容

IGeekFan.OpenIddict.FreeSql 优化建议

已修复

1. ConcurrencyToken 缺失(P0)

OpenIddict 6.x 的 Store 接口要求实现 GetConcurrencyTokenAsync / SetConcurrencyTokenAsync 方法,用于乐观并发控制。

  • 4 个实体已添加 ConcurrencyToken 字段
  • 4 个 Store 已添加对应的 getter/setter 方法

2. PruneAsync 过滤条件逻辑错误(P1)

FreeSqlOpenIddictTokenStore.PruneAsync 原始条件:

x.Status == OpenIddictConstants.Statuses.Redeemed
|| x.Status != OpenIddictConstants.Statuses.Valid

这是一个逻辑恒等式 —— Status == Redeemed || Status != Valid 等价于 Status != Valid,会误删所有非 Valid 状态的 token。

已修复为:

x.Status == OpenIddictConstants.Statuses.Redeemed
|| x.Status == OpenIddictConstants.Statuses.Revoked

3. 编译错误修复(P0)

  • Store 文件缺少 using FreeSql;
  • Extensions 文件缺少 using Microsoft.Extensions.DependencyInjection;

待优化

4. 全表加载性能(P2)

FindByRedirectUriAsyncFindByResourceAsync 将全表数据加载到内存后过滤:

var applications = await Repository.Select.ToListAsync(cancellationToken);

原因:JSON 数组字段无法用 SQL 精确匹配。

可选优化方案:

  • 方案 A:增加冗余的纯文本字段做索引查询(如单独的 RedirectUri 表)
  • 方案 B:使用数据库的 JSON 查询函数(MySQL 5.7+ 的 JSON_CONTAINS,PostgreSQL 的 @> 操作符)
  • 方案 C:接受当前限制(官方 EF Core 实现也有类似做法),适用于客户端数量较少的场景

5. CountAsync 材料化(P2)

CountAsync<TResult> 重载将整个表加载到内存后计数:

return await query(Repository.Select.AsQueryable(), state).CountAsync();

这是必要的,因为 query 参数是任意的 Func<IQueryable<T>, IQueryable<TResult>>,可能使用 LINQ-to-Objects 特性。OpenIddict Manager 调用此方法时通常传入的查询可以被 FreeSql 翻译为 SQL,但无法保证。

6. 种子数据管理(P3)

当前没有提供 OpenIddict Application 的种子数据机制。建议:

  • SyncOpenIddictTables 后手动插入默认客户端配置
  • 或扩展为 SyncOpenIddictTables(seedData: true) 自动创建默认客户端

7. 生产环境证书配置(P3)

当前示例使用开发证书 AddDevelopmentEncryptionCertificate(),生产环境需要:

options.AddEncryptionCertificate(File.ReadAllBytes("cert.pfx"), "password")
.AddSigningCertificate(File.ReadAllBytes("cert.pfx"), "password");

建议通过配置文件或环境变量管理证书路径和密码。