Skip to content

DbContext 配置

这一页关注运行时入口:怎么连数据库、怎么暴露 DbSet<T>、怎么补充模型配置。

适用场景

  • 已经定义好实体,准备让它真正接入数据库
  • 想知道 onConfiguringonModelCreating 分别负责什么
  • 需要统一配置日志、连接池、类型映射、默认跟踪策略

核心问题

DbContext 是 SqlSharp 的运行时中心。它至少做三件事:

  1. 暴露 DbSet<T>
  2. 配置数据库连接
  3. 持有模型和变更跟踪上下文

有一个关键点需要先理解:SqlSharp 能自动生成 SQL。当你调用 ctx.add()ctx.products.find()ctx.saveChanges() 时,背后并没有人手工写 INSERT INTO ...——是框架根据实体元数据拼出来的。

要生成正确的 SQL,框架必须提前知道:

  • 实体对应哪张表(@Table / toTable()
  • 字段对应哪个列(@Column / hasColumnName()
  • 哪个字段是主键(@Key / hasKey()
  • 哪些字段需要排除、哪些有并发保护

这些信息统称为模型元数据(Model Metadata)DbContext 初始化时会扫描所有被引用的实体,构建这份元数据。只有通过 DbSet<T>onModelCreating() 引用到的实体才会被扫描——打了 @Entity 但没在任何地方引用的类,框架看不见它,自然无法为它生成 SQL。

快速开始

先看最小配置:

cangjie
public class AppDbContext <: DbContext {
    public prop products: DbSet<Product> {
        get() { set<Product>() }
    }

    public override func onConfiguring(b: DbContextOptionsBuilder): Unit {
        b.useMysql("mariadb://localhost:3306?username=root&password=1024&database=mydb", "mariadb")
    }
}

定义好 DbContext 后,实体即可参与 CRUD,详见 定义实体

核心概念

onConfiguring

这里负责运行时配置:数据源、SQL 日志、类型映射、连接池、默认跟踪策略等。

cangjie
public override func onConfiguring(b: DbContextOptionsBuilder): Unit {
    b.useMysql("mariadb://localhost:3306?username=root&password=1024&database=mydb", "mariadb")
    b.logTo { sql, params => println("[SQL] ${sql}") }

    let mapper = TypeMapper()
    mapper.register<Address, String>(AddressJsonConverter())
    b.useTypeMapper(mapper)

    b.useQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
}

PostgreSQL 连接示例:

cangjie
b.usePostgres("postgres://test:1004@127.0.0.1:5432/cangjie_framework?sslmode=disable&TimeZone=Asia/Shanghai")

多数据库按环境切换也是在这里处理:

cangjie
public override func onConfiguring(b: DbContextOptionsBuilder): Unit {
    if (env == "production") {
        b.useMysql("mariadb://host1:3306?...", "mariadb")
    } else {
        b.usePostgres("postgres://test:1004@127.0.0.1:5432/devdb?sslmode=disable")
    }
}

SQL 日志

logTo 接收一个 (String, DbParameters) -> Unit 回调——每次框架生成 SQL 时,把 SQL 文本和参数传给你:

cangjie
b.logTo { sql, params =>
    println("=== SQL ===")
    println(sql)
    println("参数: ${params}")
}

回调拿到的就是框架最终发给数据库的 SQL 和参数值,适合开发调试。可以按需筛选:

cangjie
// 只看慢查询
b.logTo { sql, params =>
    if (sql.startsWith("SELECT") && elapsed > 1000) {
        println("[慢查询] ${sql}")
    }
}

// 接入应用的 ILogger
b.logTo { sql, params =>
    logger.debug("category: sql", sql)
}

生产环境建议通过配置开关控制,避免日志量过大。

onModelCreating

这里负责模型层面的补充配置。适合放不方便写在注解里的内容,或者需要统一覆盖的规则。

cangjie
protected override func onModelCreating(mb: ModelBuilder): Unit {
    let entity = mb.entity<Product>()
    entity.toTable("t_product")
    entity.hasKey(["sku"])
    entity.property("name").hasColumnName("p_name")
    entity.property("version").isConcurrencyToken()
    entity.ignore("tempField")
}

注解和 FluentAPI 的关系

SqlSharp 支持两种配置方式:注解写在实体类上,FluentAPI 写在 onModelCreating 中。两者的优先级从低到高依次为:

约定默认值 < 注解 < FluentAPI
配置项默认值来源注解FluentAPI
表名DbSet<T> 属性名@Table["name"]entity.toTable("name")
列名属性名@Column["name"].property("p").hasColumnName("name")
主键_id + 整数类型@Keyentity.hasKey(["p"])

FluentAPI 在 onModelCreating 中最后执行,可以覆盖注解和默认值。具体的注解和 FluentAPI 用法对比见 定义实体 - 常见用法

常见配置项

方法说明
.useMysql(connectionString, driverName?)MySQL / MariaDB
.usePostgres(connectionString)PostgreSQL
.useOpenGauss(connectionString)OpenGauss
.useDatasource(connectionString, driverName)其他已注册驱动
.logTo { sql, params => ... }输出 SQL 日志
.useTypeMapper(mapper)注册类型映射
.usePooling(options?)连接池配置
.useQueryTrackingBehavior(behavior)默认跟踪策略

与其他模块组合

  • DbContext变更跟踪 的宿主
  • Queryable、手写 SQL、事务都从 DbContextDbSet<T> 出发
  • 在 Web 项目里,通常会进一步接到 依赖注入

生产建议

  • 连接字符串、日志开关、连接池参数尽量统一收敛到配置系统,不要散落在业务代码里
  • 约定能表达的规则优先用约定,确实需要覆盖时再上 FluentAPI
  • 如果系统读多写少,可以考虑默认 NoTracking,在需要更新的查询处显式开启跟踪