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)
FindByRedirectUriAsync 和 FindByResourceAsync 将全表数据加载到内存后过滤:
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");
建议通过配置文件或环境变量管理证书路径和密码。