多数据库 Keyed 注册
当应用需要连接多个数据库时,使用 `add_dbcontext_keyed`。v1.1.0 起支持通过 `#[context("key")]` 属性实现**实体隔离**——每个 keyed `DbContext` 只管理属于自己的实体。
读写分离示例
let provider = ServiceCollection::new()
.add_dbcontext_keyed("read", |options| {
options.use_postgres("host=read-replica/db");
})
.add_dbcontext_keyed("write", |options| {
options.use_postgres("host=primary/db");
})
.build()
.unwrap();
// 查询走读库(owned 解析,推荐)
let mut read_ctx: DbContext = provider.get_keyed_owned("read").unwrap();
// 写入走主库(owned 解析,推荐)
let mut write_ctx: DbContext = provider.get_keyed_owned("write").unwrap();
多租户示例
// 每个租户独立数据库
for tenant in &tenants {
let key = format!("tenant_{}", tenant.id);
svc.add_dbcontext_keyed(&key, |options| {
options.use_postgres(&tenant.connection_string);
});
}
实体隔离(v1.1.0+)
当不同 keyed 上下文管理不同的实体集合时,使用 `#[context("key")]` 属性标记实体归属:
use rust_ef::prelude::*;
// 默认上下文实体 —— 不标注 #[context]
#[derive(Debug, Clone, EntityType)]
#[table("blogs")]
pub struct Blog {
#[primary_key] #[auto_increment] pub id: i32,
pub url: String,
}
// "logs" 上下文实体 —— 标注 #[context("logs")]
#[derive(Debug, Clone, EntityType)]
#[context("logs")]
#[table("log_entries")]
pub struct LogEntry {
#[primary_key] #[auto_increment] pub id: i32,
pub message: String,
}
配置也可按上下文隔离,使用 `#[entity(T, "key")]` 的第二参数:
#[derive(Default)]
pub struct LogEntryConfig;
#[entity(LogEntry, "logs")]
impl IEntityTypeConfiguration<LogEntry> for LogEntryConfig {
fn configure(&self, entity: &mut EntityTypeBuilder<'_, LogEntry>) {
entity.property_named("message").has_index();
}
}
注册后,每个 keyed DbContext 只自动发现属于自己的实体:
let provider = ServiceCollection::new()
.add_dbcontext_keyed("primary", |options| {
options.use_postgres("host=primary/db");
})
.add_dbcontext_keyed("logs", |options| {
options.use_sqlite("logs.db");
})
.build()
.unwrap();
// Owned 解析(推荐):
let mut primary: DbContext = provider.get_keyed_owned("primary").unwrap();
let mut logs: DbContext = provider.get_keyed_owned("logs").unwrap();
// primary 只管理 Blog(context_key = None)
// logs 只管理 LogEntry(context_key = Some("logs"))
// 共享解析(Arc<DbContext>,&self 访问):
// let primary: Arc<DbContext> = scope.get_keyed("primary");
过滤规则
| 实体的 `context_key` | DbContext 的 key | 自动注册 | |----------------------|-----------------|---------| | `None`(无 `#[context]`) | `None`(默认上下文) | 是 | | `None` | `Some("logs")` | 否 | | `Some("logs")` | `None` | 否 | | `Some("logs")` | `Some("logs")` | 是 |
> 不使用 `#[context]` 的实体归属于默认上下文(`context_key = None`),只会被默认 `DbContext` 发现。 详见 [自动注册与发现](../02-quickstart/auto-registration.md#多数据库上下文隔离v110)。
设计要点
| 实践 | 说明 | |------|------| | Keyed 上下文独立管理 | 每个 key 对应独立的 Provider 和连接池 | | 实体用 `#[context("key")]` 隔离 | 避免跨数据库实体污染,每个上下文只发现自己的实体 | | 读写分离在应用层控制 | `rust-ef` 不自动路由,需代码显式选择 | | 注意连接池限制 | 每个 key 都会创建一组连接,避免 key 爆炸 | | Owned 解析优先 | `get_keyed_owned()` → `DbContext`,`&mut self` 访问 |
下一节:[SaveChanges 拦截器](save-changes-interceptors.md)