Expand description
DI integration — AddDbContext on rust-dix.
Supports single-context (default) and multi-context (keyed) registration.
DbContext is registered as Scoped and can be resolved either as
Arc<DbContext> (shared within a scope) or as owned DbContext (fresh
instance, idiomatic &mut self access).
§Recommended: owned resolution for handlers
DbContext methods (set::<T>(), save_changes(), detect_changes())
require &mut self. The idiomatic pattern is to own the context via
get_owned(), avoiding Arc<Mutex> and interior mutability entirely.
use rust_dix::*;
use rust_ef::di::*;
use rust_ef::db_context::DbContext;
use rust_ef_sqlite::DbContextOptionsBuilderExt as _;
// rust-dix 0.6+: `build()` returns `Arc<ServiceProvider>` directly,
// and `get_owned()` returns `Result<T, RdiError>`.
let provider = ServiceCollection::new()
.add_dbcontext(|options| {
options.use_sqlite("data source=app.db");
})
.build()
.unwrap();
// Owned: fresh instance, direct &mut self access — no locks needed.
let mut ctx: DbContext = provider.get_owned().expect("DbContext");
ctx.set::<Blog>().add(blog);
ctx.save_changes().await?;Handlers declare a bare ctx: DbContext field marked with
#[inject(owned)] — #[derive(Inject)] resolves it via get_owned().
Unmarked fields fall back to Default::default(). Use #[inject(scoped)]
(not bare #[inject], which defaults to Singleton) to avoid captive
dependency errors with the Scoped DbContext:
#[derive(Inject)]
pub struct CreateBlogHandler {
#[inject(owned)]
ctx: DbContext, // bare T + #[inject(owned)] → get_owned()
}
#[inject(scoped)]
#[async_trait]
impl IRequestHandler<CreateBlogRequest, BlogModel> for CreateBlogHandler {
async fn handle(&mut self, req: CreateBlogRequest) -> Result<BlogModel> {
self.ctx.set::<Blog>().add(blog);
self.ctx.save_changes().await?;
// ...
}
}§Shared resolution (within a scope)
When multiple consumers in the same scope must share a single instance
(e.g. an IHostedService that seeds data before handlers run), resolve
as Arc<DbContext>:
use rust_dix::scope::ScopeFactory; // for create_scope()
let scope = provider.create_scope();
let ctx: Arc<DbContext> = scope.get().expect("DbContext");
// Additional get() calls within this scope return the same instance.Note:
Arc<DbContext>only provides&selfaccess. Mutation requiresArc::get_mut(refcount == 1) or restructuring to owned resolution. Preferget_owned()for any scope that needs&mut self.
§Multiple databases (keyed)
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")
.add_interceptor(AuditInterceptor);
})
.build()
.unwrap();
// Owned keyed resolution (recommended for handlers):
let mut primary: DbContext = provider.get_keyed_owned("primary").expect("primary ctx");
let mut logs: DbContext = provider.get_keyed_owned("logs").expect("logs ctx");
// Shared keyed resolution (within a scope):
// let primary: Arc<DbContext> = scope.get_keyed("primary");§Scoped Lifetime
add_dbcontext registers the context as Scoped. Resolving via
get() shares the instance within a scope; resolving via get_owned()
bypasses the cache and returns a fresh instance each call (both are
isolated across scopes). Resolving directly from the root
ServiceProvider (without creating a scope) degrades to a fresh
instance per call (transient).
rust-webapp: the HTTP pipeline automatically creates a scope per request. Handlers are resolved via
get_owned::<Handler>(), which owns a freshDbContext— no manual scope management needed.
Structs§
Traits§
- DbContext
Service Collection Ext - Adds
add_dbcontextandadd_dbcontext_keyedtorust_dix::ServiceCollection.