Skip to main content

DbContext

Struct DbContext 

Source
pub struct DbContext { /* private fields */ }
Expand description

The session / unit-of-work entry point.

Entity sets are lazy-created via ctx.set::<T>(). Metadata is loaded from the process-level MetadataCache on DbContextOptions (built once per context_key, then Arc-shared). save_changes() commits all pending changes in a transaction.

Implementations§

Source§

impl DbContext

Source

pub fn from_options(options: &DbContextOptions) -> EFResult<Self>

Creates the context from options (uses the provider factory stored in options).

Entity metadata is loaded from the process-level MetadataCache on DbContextOptions — inventory::iter + IEntityTypeConfiguration::configure() run once per context_key (first call), then the result is Arc-shared across all DbContext instances. Per-instance ModelBuilder mutations (has_query_filter, etc.) after construction only affect this instance.

Source

pub fn set<T>(&mut self) -> &mut DbSet<T>

Source

pub fn model(&mut self) -> &mut ModelBuilder

Returns the model builder for Fluent API configuration.

Source

pub fn model_builder(&self) -> &ModelBuilder

Returns a read-only reference to the model builder.

Source

pub fn entity_metas_contains<T: IEntityType>(&self) -> bool

Returns true if an entity of type T has been discovered and registered in the entity metadata map.

Source

pub fn discover_entities(&mut self) -> EFResult<()>

No-op since metadata caching was introduced.

Historically, this method iterated inventory::iter to register entity metadata and apply #[entity(T)] configurations. That work now happens in MetadataCache::build(), invoked lazily by from_options() via MetadataCache::get_or_build(). The result is Arc-shared across all DbContext instances with the same context_key.

Retained as a public method for backward compatibility — existing code calling ctx.discover_entities()? after from_options() continues to compile and run (as a no-op). The metadata is already populated.

Source

pub fn detect_changes(&mut self)

Detects changes on all tracked DbSets by comparing property snapshots.

Source

pub async fn ensure_created(&self) -> EFResult<()>

Creates all tables for registered entity types.

Sources metas from model_builder.build(), which applies all Fluent API configurations and #[entity(T)] overrides. Entities are discovered automatically via #[derive(EntityType)]; call discover_entities() first, or use set::<T>() to register manually.

Source

pub async fn ensure_deleted(&self) -> EFResult<()>

Drops all tables for registered entity types.

Source§

impl DbContext

Source

pub fn provider(&self) -> &dyn IDatabaseProvider

Returns the database provider.

Source

pub fn change_tracker(&self) -> &ChangeTracker

Returns a read-only reference to the change tracker.

Source

pub fn change_tracker_mut(&mut self) -> &mut ChangeTracker

Returns a mutable reference to the change tracker.

Source

pub fn clear_tracking(&mut self)

Detaches every tracked entity and clears the change tracker.

Called automatically when a DbContext::transaction closure returns TxOutcome::Rollback: the database rolled back, so keeping the entities tracked would let those writes leak into a later save_changes. Entity instances themselves are not modified — only the context’s tracking state is dropped.

Source

pub async fn sql_query<T: IFromRow + IEntityType>( &self, sql: &str, params: &[DbValue], ) -> EFResult<Vec<T>>

Executes a raw SQL query and materializes the result rows into entities.

This is the escape hatch for complex queries (multi-table JOINs, CTEs, window functions) that are hard to express via LINQ. The caller is responsible for SQL correctness and parameterization.

§Example
ⓘ
let blogs: Vec<Blog> = ctx
    .sql_query("SELECT * FROM blogs WHERE id = ?", &[DbValue::I32(1)])
    .await?;
Source

pub fn transaction_mut(&mut self) -> Option<&mut Box<dyn ITransaction>>

Returns a mutable reference to the ambient transaction handle, if one is active (registered by use_transaction() / transaction()).

Inside a scoped transaction closure, use this to access the ambient handle for savepoint and isolation operations. A handle returned by DbContext::begin_transaction is owned by the caller instead, so use that handle directly. The borrow must be released (by scoping) before calling save_changes() or other &mut self methods.

Source

pub async fn begin_transaction(&mut self) -> EFResult<Box<dyn ITransaction>>

Begins a transaction and returns a typed handle.

The handle is standalone: it is not registered as the context’s current transaction. Queries and a later save_changes() do not join it; save_changes() keeps self-managing its own transaction. Use DbContext::transaction for a context-scoped transaction where queries, bulk DML and save_changes() all join.

commit / rollback consume the handle by value, preventing use-after-commit at the type level.

Source

pub async fn begin_transaction_with( &mut self, isolation: IsolationLevel, ) -> EFResult<Box<dyn ITransaction>>

Like DbContext::begin_transaction, with an isolation level applied at transaction start.

Source

pub async fn use_transaction<F, R>(&mut self, f: F) -> EFResult<R>
where for<'a> F: FnOnce(&'a mut Self) -> Pin<Box<dyn Future<Output = EFResult<R>> + Send + 'a>>, R: Send + 'static,

Executes a closure within an ambient transaction.

Registers the transaction as ambient for the duration of f, so every statement issued through the context — queries, navigation loading, bulk DML, raw SQL and save_changes() — reuses the same transaction connection. Commits on Ok, rolls back on Err.

For an abort-with-result exit, use DbContext::transaction instead.

The closure receives &mut DbContext and must return a pinned boxed future. This signature works around Rust’s async borrow checker by letting the closure capture ctx by mutable reference while still producing a Send future.

§Example
ⓘ
ctx.use_transaction(|ctx| Box::pin(async move {
    ctx.add::<Blog>(blog);
    ctx.save_changes().await?;
    Ok(())
})).await?;
Source

pub async fn use_transaction_with<F, R>( &mut self, isolation: IsolationLevel, f: F, ) -> EFResult<R>
where for<'a> F: FnOnce(&'a mut Self) -> Pin<Box<dyn Future<Output = EFResult<R>> + Send + 'a>>, R: Send + 'static,

Like DbContext::use_transaction, with an explicit isolation level applied at transaction start.

Source

pub async fn transaction<F, R>(&mut self, f: F) -> EFResult<R>
where for<'a> F: FnOnce(&'a mut Self) -> Pin<Box<dyn Future<Output = EFResult<TxOutcome<R>>> + Send + 'a>>, R: Send + 'static,

Executes a closure within an ambient transaction and lets the closure decide whether to commit or roll back — while still returning a value.

This is the handler-level unit of work: every statement issued through ctx inside the closure (queries, save_changes, execute_update, execute_delete, raw SQL) uses the same transaction connection, so reads observe the handler’s own uncommitted writes.

  • Ok(TxOutcome::Commit(value)) — commit, return Ok(value)
  • Ok(TxOutcome::Rollback(value)) — roll back, detach tracked entities, return Ok(value)
  • Err(e) — roll back, detach tracked entities, return Err(e)

Unlike DbContext::use_transaction, a business rejection does not have to be encoded as an error.

§Example
ⓘ
let result = ctx.transaction(|ctx| Box::pin(async move {
    // Batch update A, then join-query A/B/C — both on the transaction
    // connection, so the query sees the uncommitted update.
    linq!(ctx.set::<A>(), |a: A| a.flag; set a.flag, true; execute_update).await?;
    let rows = linq!(ctx.set::<A>(), |a: A| a.flag).to_list().await?;

    if !rows.iter().all(|r| r.ok()) {
        return Ok(TxOutcome::Rollback(BizResult::Rejected));
    }
    Ok(TxOutcome::Commit(BizResult::Done))
})).await?;
Source

pub async fn transaction_with<F, R>( &mut self, isolation: IsolationLevel, f: F, ) -> EFResult<R>
where for<'a> F: FnOnce(&'a mut Self) -> Pin<Box<dyn Future<Output = EFResult<TxOutcome<R>>> + Send + 'a>>, R: Send + 'static,

Like DbContext::transaction, with an explicit isolation level applied at transaction start.

Source§

impl DbContext

Source

pub fn add<T>(&mut self, entity: T)

Adds a new entity in Added state. The entity will be INSERTed during save_changes().

Source

pub fn attach<T>(&mut self, entity: T)

Attaches an existing entity in Unchanged state with a snapshot for future change detection.

Source

pub fn update<T>(&mut self, entity: T)

Marks an entity as Modified — all fields will be UPDATEd during save_changes().

Source

pub fn upsert<T>(&mut self, entity: T)

Marks an entity for upsert (INSERT … ON CONFLICT DO UPDATE).

Source

pub fn remove_at<T>(&mut self, index: usize) -> EFResult<()>

Marks the entity at the given index as Deleted.

Source

pub fn remove_all<T>(&mut self)

Marks all entities of type T as Deleted.

Source

pub async fn load_all<T>(&mut self) -> EFResult<()>

Loads all rows from the database into the DbSet as Unchanged. Clears existing entries and tracker state for type T first.

Source§

impl DbContext

Source

pub async fn save_changes(&mut self) -> EFResult<SaveChangesResult>

Saves all pending changes across all DbSets.

Detects changes, runs interceptors, executes INSERT/UPDATE/DELETE in a transaction, and clears tracked entries on success.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.