IDataContext

Trax's own data context: an EF Core context over the trax tables (runs, logs, manifests, the work queue, dead letters and the rest), and the effect provider that persists a train's metadata. UsePostgres, UseSqlite and UseInMemory each register an implementation.

Inject it to read Trax's tables, or to make a write commit with an enqueue through IEnqueueContextAccessor. Your own tables belong in a domain data context, not here.

Signature

namespace Trax.Effect.Data.Services.DataContext;
 
public interface IDataContext : IEffectProvider, IAsyncDisposable  // IEffectProvider is IDisposable
{
    DbSet<Metadata> Metadatas { get; }
    DbSet<Log> Logs { get; }
    DbSet<Manifest> Manifests { get; }
    DbSet<ManifestGroup> ManifestGroups { get; }
    DbSet<WorkQueue> WorkQueues { get; }
    DbSet<DeadLetter> DeadLetters { get; }
    DbSet<BackgroundJob> BackgroundJobs { get; }
    DbSet<SchedulerConfig> SchedulerConfigs { get; }
    DbSet<PersistedOperation> PersistedOperations { get; }
    DbSet<PersistedOperationHistory> PersistedOperationHistories { get; }
    DbSet<RunnerNonce> RunnerNonces { get; }
    DbSet<SnapshotDraft> SnapshotDrafts { get; }
    DbSet<EffectClaim> EffectClaims { get; }
 
    int Changes { get; set; }
 
    Task<IDataContextTransaction> BeginTransaction();
    Task<IDataContextTransaction> BeginTransaction(CancellationToken cancellationToken);
    Task<IDataContextTransaction> BeginTransaction(IsolationLevel isolationLevel);
    Task<IDataContextTransaction> BeginTransaction(IsolationLevel isolationLevel, CancellationToken cancellationToken);
    Task CommitTransaction();
    Task RollbackTransaction();
    void Reset();
    DataContext<TDbContext> Raw<TDbContext>() where TDbContext : DbContext;
}
 
namespace Trax.Effect.Data.Services.DataContextTransaction;
 
public interface IDataContextTransaction : IDisposable
{
    Task Commit();
    Task Rollback();
}

From IEffectProvider it also has Track(IModel), Update(IModel) and SaveChanges(CancellationToken), which the effect runner uses to persist a run.

Tables

PropertyTableHolds
Metadatastrax.metadataOne row per train run: state, input, output, timing, failure
Logstrax.logLog entries written by AddDataContextLogging
Manifeststrax.manifestScheduled jobs: train, input properties, schedule
ManifestGroupstrax.manifest_groupGroups of manifests that share dispatch settings
WorkQueuestrax.work_queueRuns waiting to be dispatched
DeadLetterstrax.dead_letterManifests that exhausted their retries
BackgroundJobstrax.background_jobDispatched runs waiting for, or held by, a worker; only in-flight work
SchedulerConfigstrax.scheduler_configThe persisted, dashboard-editable scheduler settings; zero or one row
PersistedOperations, PersistedOperationHistoriestrax.persisted_operation, trax.persisted_operation_historyPersisted GraphQL operations and their audit history
RunnerNoncestrax.runner_nonceNonces a runner accepted on signed requests
SnapshotDraftstrax.snapshot_draftState-machine drafts, one per user and draft id
EffectClaimstrax.effect_claimExactly-once state-machine effect intents

RunnerNonces, SnapshotDrafts and EffectClaims have default implementations on the interface, so an implementation written before they existed still loads.

Members

MemberDescription
BeginTransaction(...)Starts a transaction, optionally at an isolation level. Returns an IDataContextTransaction to commit, roll back or dispose.
CommitTransaction() / RollbackTransaction()Commits or rolls back the context's current transaction
Reset()Clears the change tracker, so a long-lived context stops tracking what it loaded
ChangesA count of tracked changes not yet saved
Raw<TDbContext>()Casts to the concrete DataContext<TDbContext>, for EF members the interface does not surface. Throws InvalidCastException when the implementation is not that type.

Getting one

HowLifetimeUse
Inject IDataContextScoped: one per scope, created from the provider's context factoryReads and writes inside a request or a train's scope
Inject IDataContextProviderFactory and call CreateDbContextAsync(ct)A new context per call; dispose itBackground services and anything outside a scope

Example

using Microsoft.EntityFrameworkCore;
using Trax.Effect.Data.Services.DataContext;
using Trax.Effect.Enums;
 
public class FailedRunReport(IDataContext data)
{
    public Task<List<string>> TrainsFailingSince(DateTime sinceUtc, CancellationToken ct) =>
        data.Metadatas.AsNoTracking()
            .Where(m => m.TrainState == TrainState.Failed && m.EndTime > sinceUtc)
            .Select(m => m.Name)
            .Distinct()
            .ToListAsync(ct);
}

Treat the trax tables as read-only from application code. Trax's services write them, and a row changed behind their back (a run's state, a work queue entry's status) can break the scheduler's invariants.

Package

dotnet add package Trax.Effect.Data

A data provider package (Trax.Effect.Data.Postgres, Trax.Effect.Data.Sqlite or Trax.Effect.Data.InMemory) brings it in.