Expand description
DbContext, DbContextOptions, and ChangeTracker — the session / unit-of-work layer.
§Architecture
DbContext is the concrete context type. Entity sets use a type-map:
ctx.set::<Blog>() lazy-creates DbSet<Blog>. SetOps<T> dispatchers
enable save_changes() to iterate all entity types.
§Provider Factory
DbContextOptions stores a provider_factory closure injected by the
provider extension methods (use_sqlite, use_postgres, use_mysql).
DbContext::from_options() calls this factory to create the provider.
§Ownership and Mutation
DbContext methods (set::<T>(), save_changes(), detect_changes())
require &mut self — this is idiomatic Rust, not a limitation. The DI
integration (add_dbcontext) registers the context as Scoped and
supports two resolution modes:
- Owned (recommended for handlers):
provider.get_owned::<DbContext>()returns a freshDbContextwith direct&mut selfaccess. Handlers declare a barectx: DbContextfield marked with#[inject(owned)];#[derive(Inject)]resolves it viaget_owned(). Unmarked fields fall back toDefault::default(). - Shared (within a scope):
scope.get::<DbContext>()returnsArc<DbContext>for consumers that only need&selfaccess.
// Owned — idiomatic &mut self, no locks:
// rust-dix 0.6+: get_owned() returns Result<T, RdiError>
let mut ctx: DbContext = provider.get_owned()?;
ctx.set::<Blog>().add(blog);
ctx.save_changes().await?;§Thread Safety
DbContext is not thread-safe — a single instance must not be shared
across threads. This is a design decision (aligned with EFCore), not a
limitation.
Correct usage: each request / operation owns its own DbContext
instance (via get_owned() or a fresh scope):
let mut ctx: DbContext = provider.get_owned()?;
// This instance is exclusively owned — &mut self works directly.rust-webapp: the HTTP pipeline creates a DI scope per request and resolves handlers via
get_owned::<Handler>(). Handlers own a freshDbContext— no manual scope management needed.
Anti-pattern: sharing via Arc<Mutex<DbContext>> causes tracking
pollution — Thread A’s save_changes() would commit Thread B’s pending
changes. Prefer owned resolution.
Structs§
Functions§
- delete_
deleted_ phase - Phase 3: DELETE Deleted entities.
- insert_
added_ phase - Phase 1a: INSERT Added (non-upsert) entities, then backfill generated PKs.
- save_
one_ set - update_
modified_ phase - Phase 2: UPDATE Modified entities (partial update via modified_properties).
- upsert_
added_ phase - Phase 1b: UPSERT Added entities (is_upsert = true).