Known 框架层不锁定 ORM。Known/Data 只定义三样东西:数据访问契约、结果模型、通用工具。
具体的 DbContext / Dapper / SqlKit 由项目自己接入。
三个抽象
Database 基类
Database 是"应用级"的数据访问协调者,暴露:
InitializeAsync()— 建表 / 种子 / 元模型加载。GetContext()— 返回项目自定义的上下文对象(EFDbContext、Dapper 连接工厂或别的)。MigrateSchema(...)— Schema 变更钩子,供应用自定义的迁移流程调用。
Known 内置一个内存实现用于 Demo 与单元测试;生产项目重写它接自己的 EF/Dapper。
DatabaseOption
配置对象,登记一组数据库连接与监控器。它没有 Provider / ConnectionString 这类单值字段,
而是按连接名维护一张 Databases(List<DatabaseInfo>)表,并暴露一组注册方法:
每种库一个快捷方法 AddSQLServer<TProvider> / AddSQLite<TProvider> / AddMySQL / AddPostgreSQL
/ AddOracle / AddAccess / AddDM(参数是连接串 + 可选连接名),以及通用的
AddProvider<TProvider>(name, DatabaseType, connString)。这里的 TProvider 就是对应 ADO.NET 的
DbProviderFactory(如 Microsoft.Data.SqlClient.ClientFactory、Microsoft.Data.Sqlite.SqliteFactory)。
另有 SqlMonitors / OperateMonitors(SQL 与增删改监控钩子)、DefaultAppId、IsMemoryDatabase 等字段。
注意它不在 AddKnown 上配置——AddKnown 的 AppInfo 只有 Id / Name / Type / Assembly / CreateTime。
数据库挂在服务端入口 AddKnownWeb 的 ServerOption.Database 回调(Action<DatabaseOption>)上:
builder.Services.AddKnownWeb(o =>
{
o.Database = db =>
{
// 默认连接名可省;多库时传第二个参数区分
db.AddSQLServer<Microsoft.Data.SqlClient.SqlClientFactory>("Server=...;Database=...;");
// db.AddSQLite<Microsoft.Data.Sqlite.SqliteFactory>("Data=app.db");
// db.SqlMonitors.Add(info => Console.WriteLine($"{DateTime.Now:HH:mm:ss} {info}"));
};
});
Result(非泛型)与 PagingResult<T>
Known 的业务返回值只有两种,没有 Result<T> 这种泛型包装:
public class Result // 一次写操作(保存 / 删除)的成败
{
public bool IsValid { get; set; } = true; // 注意是 IsValid,不是 Success
public string Message { get; set; } // 面向用户的文案
public object Data { get; set; } // 可选回传数据
public T DataAs<T>();
public static Result Success(string message, object data = null);
public static Result Error(string message, object data = null);
}
public class PagingResult<T> // 一次分页查询的结果
{
public int Total { get; set; }
public List<T> Data { get; set; } = [];
public string Message { get; set; }
public bool IsValid => string.IsNullOrEmpty(Message);
}
对应服务层标准写法的返回类型:分页查询 Task<PagingResult<T>>、按主键取单条 Task<T>(无数据返回 null)、保存/删除 Task<Result>。[Service] 实现里成功用 Result.Success("消息")、失败用 Result.Error("消息"),判断一律看 IsValid、取文案看 Message。ServiceProxy<T> 在客户端把 Result.IsValid == false 透出来,配合 UIService.Error(...) 做全局提示。
建表与迁移
Known 侧不生成 EF Migration 文件。日常建表 / 增列由框架按实体类自动、非破坏性同步:宿主启动时经 UseDatabase 触发 EnsureSchemaAsync(),比对实体与库表,只 ADD、不改删列,幂等安全。
破坏性 DDL(DROP COLUMN / DROP TABLE / 类型收窄)框架不会自动执行——需要你手写参数化 SQL、自行纳入项目的版本管理(如 Migrations/*.sql)并在发布流程里手动执行。
与业务模块的关系
模块程序集不应自带 DbContext。原因:
- 一份数据库、一个
Database实例;多模块各持一份会导致连接数爆炸、事务无法跨模块。 - 元模型工件(
SysSchemaItem)与业务实体在同一张表上,跨模块共享上下文更简单。
正确的做法:模块只写 [Service] 类,注入宿主提供的 Database,
从 GetContext() 拿到底层句柄。
常见坑
InitializeAsync死锁:不要在OnInitialized里同步等待;始终await。- 连接串明文写在
appsettings:Demo 可以、生产必须走环境变量或 Secret 管理; 参考Website/Website/appsettings.Production.json的_comment写法。 Result.Data为 null 却直接解引用:客户端应先判res.IsValid再用res.Data, 这是ServiceProxy契约的一部分。
未来
- 读写分离(
ReadOnlyDatabase变体):属 DG 数据治理切片,Batch 3 之后。 - 多数据源(一个应用挂两套 DB):Batch 4 企业版;
DatabaseOption会变成集合。 - Dapper 一等支持:目前靠项目自建
Database子类;官方 Dapper 适配计划在 DG-08 之后。
扫码关注公众号
扫码加入 QQ 群