Skip to content
Client v5: BLE, BLE Hosting, HTTP, Jobs - Linux, MacOS, & Blazor Support! Full AOT, RX on BLE only & MANY other features! Power up!

Migrating v10 → v11

v11 is a breaking release. Its centrepiece is a redesign we call store-as-connection: the document store is now a two-level model — a long-lived IDocumentStore and a short-lived IDocumentSession (the unit of work, the EF-DbContext analogue). This removes the ambient thread-safety / scoping traps the old singleton-store-as-the-operation-API forced, and it’s the reason for most of the changes below.

There are no [Obsolete] shims — v11 is a clean break. This guide lists every change you need to make, with before/after code, then the optional new features worth adopting.

What changedv10v11
PackagesShiny.DocumentDb.Extensions.DependencyInjection + Shiny.DocumentDb.Diagnosticsfolded into Shiny.DocumentDb — remove both refs
Unit of workstore.CreateUnitOfWork()UnitOfWorkstore.OpenSession()IDocumentSession
Discard bufferuow.Clear()session.ClearPending()
Named storesIDocumentStoreProviderIDocumentSessionFactory
Typed contextDocumentContext over a store (stateless)DocumentContext over a session (disposable)
TelemetryAddDocumentStoreInstrumentation() / o.Instrumentation = truealways-on, embedded — just subscribe your OTel pipeline

DI registration and diagnostics now ship inside the core Shiny.DocumentDb package. Delete the two now-empty package references; the namespaces are unchanged, so nothing else moves.

<!-- remove these two -->
<PackageReference Include="Shiny.DocumentDb.Extensions.DependencyInjection" Version="10.*" />
<PackageReference Include="Shiny.DocumentDb.Diagnostics" Version="10.*" />

using Shiny.DocumentDb;, AddDocumentStore(...), AddDocumentContext(...), seeding, and multi-tenancy all keep working from the core package.

CreateUnitOfWork() and the public UnitOfWork type are removed. Open a session instead — the buffered write verbs (Add/AddRange/Update/Upsert/Remove) and SaveChanges are identical. A session is IAsyncDisposable, so await using it.

// v10
var uow = store.CreateUnitOfWork();
uow.Add(order).Update(customer).Remove<Cart>(cartId);
await uow.SaveChanges();
// v11
await using var session = store.OpenSession();
session.Add(order).Update(customer).Remove<Cart>(cartId);
await session.SaveChanges();
  • uow.Clear()session.ClearPending().
  • SaveChanges(suppressInterceptors: true) is unchanged.
  • Contiguous same-type runs still coalesce into the batch fast path.
HostHowRegister
Any (one-off)store.OpenSession()AddDocumentStore(...)
ASP.NET Coreinject scoped IDocumentSessionadd .AddScopedDocumentSession()
MAUI / desktop / background / Orleansinject singleton IDocumentSessionFactory, factory.OpenSession() per unitAddDocumentStore(...) (factory always registered)

IDocumentSession is the DbContext analogue: short-lived, single-flow, not thread-safe. In ASP.NET register it scoped; elsewhere open one per unit of work from the factory (mirrors EF’s IDbContextFactory).

3. Named / multi-store: IDocumentStoreProviderIDocumentSessionFactory

Section titled “3. Named / multi-store: IDocumentStoreProvider → IDocumentSessionFactory”
// v10
public class MyService(IDocumentStoreProvider stores)
{
void Work() => stores.GetStore("orders").Insert(...);
}
// v11
public class MyService(IDocumentSessionFactory stores)
{
void Work() => stores.GetStore("orders").Insert(...); // GetStore unchanged
// …or a unit of work on a named store:
Task Unit() { using var s = stores.OpenSession("orders"); s.Add(...); return s.SaveChanges(); }
}

IDocumentStoreProvider is removed; IDocumentSessionFactory absorbs GetStore(name) and adds OpenSession(name).

4. Typed DocumentContext is now session-backed

Section titled “4. Typed DocumentContext is now session-backed”

The generated DocumentContext now wraps an IDocumentSession (it is a unit of work) and is IAsyncDisposable/IDisposable — like EF’s DbContext. The DI registrations are unchanged in name:

  • AddAppContext(...) — scoped context (ASP.NET). Inject AppContext in a request handler.
  • AddAppContextFactory(...) — singleton IDocumentContextFactory<AppContext> (MAUI/desktop/background). Call factory.Create() per unit of work and await using the result.

What you must change:

// v10 — manual construction took a store
var db = new AppContext(store);
// v11 — the generated constructor takes a session
await using var db = new AppContext(store.OpenSession());
  • context.CreateUnitOfWork() is gone — the context is the unit of work: context.Add(x) + await context.SaveChanges(), or reach the session via context.Session. context.BeginTransaction() is available too.
  • Typed set writes (db.Users.Insert(...), Update, Upsert, Remove) are unchanged (still immediate).
  • Dispose the context (await using) — a factory-created context owns a DI scope + session. In ASP.NET the container disposes the scoped context for you.
  • If you call the low-level AddDocumentContext<T> / AddDocumentContextFactory<T> directly (rare), the activator delegate is now Func<IDocumentSession, TContext> instead of Func<IDocumentStore, TContext>.

5. Telemetry is embedded — remove the opt-in

Section titled “5. Telemetry is embedded — remove the opt-in”

The instrumentation decorator, AddDocumentStoreInstrumentation(), and DocumentStoreOptions.Instrumentation are removed. Every store now emits OpenTelemetry metrics + spans directly, on every provider and every construction path, zero-cost when unobserved.

// v10
services.AddDocumentStore(o => { o.Instrumentation = true; o.DatabaseProvider = …; });
services.AddDocumentStoreInstrumentation();
// v11 — just register the store; subscribe your OTel pipeline
services.AddDocumentStore(o => o.DatabaseProvider = …);
builder.Services.AddOpenTelemetry()
.WithTracing(t => t.AddSource("Shiny.DocumentDb"))
.WithMetrics(m => m.AddMeter("Shiny.DocumentDb"));

Operation names and db.* tags are unchanged, so existing dashboards keep working.

Interceptors did not break — v11 adds capabilities. Your existing IDocumentInterceptor keeps working, and now:

  • ctx.Services gives you the write’s DI scope (non-nullable) — resolve scoped services inside the hook.
  • Scoped interceptors are supported: services.AddScoped<IDocumentInterceptor, X>().
  • ctx.Session (and the existing ctx.Store) is bound to the write’s transaction for atomic re-entrant side effectsctx.Session.Add(outbox); await ctx.Session.SaveChanges();.
  • Bug fix: DI-registered interceptors now fire on every provider (they previously never ran on MongoDB, Cosmos, LiteDB, IndexedDB, DynamoDB, Azure Table, or Orleans grain storage).

See Write Interceptors.

Optional, non-breaking, and enabled by the session model:

  • Explicit transactions + consistent readsawait using var tx = await session.BeginTransaction(); for pessimistic locking reads (session.Get(id, LockMode.Update)) and grouping set-based ExecuteUpdate/ ExecuteDelete. Pass an isolation level (BeginTransaction(IsolationLevel.Snapshot)) for a consistent-read session. Relational providers.
  • Scope-aware multi-tenancy — register ITenantResolver scoped and it resolves the request’s own tenant through a scoped session; no ambient IHttpContextAccessor needed. See Multi-tenancy.
  • Scope-aware temporal actoro.MapTemporal<Order>(t => t.ResolveActor = sp => sp.GetService<ICurrentUser>()?.Id) captures “who” from the request scope. See Temporal.
  • Unit-of-work telemetry — a session emits a unit_of_work parent span (db.session.id) so its operations correlate into one trace, plus a db.client.unit_of_work.operations histogram. See Telemetry.
  1. Remove the Extensions.DependencyInjection and Diagnostics package references.
  2. Delete AddDocumentStoreInstrumentation() and o.Instrumentation = true; add OTel .AddSource/.AddMeter("Shiny.DocumentDb").
  3. store.CreateUnitOfWork()await using store.OpenSession(); uow.Clear()session.ClearPending().
  4. IDocumentStoreProviderIDocumentSessionFactory.
  5. Add .AddScopedDocumentSession() (ASP.NET) or inject IDocumentSessionFactory where there’s no scope.
  6. new AppContext(store)new AppContext(store.OpenSession()); context.CreateUnitOfWork()context.Add/SaveChanges; await using factory-created contexts.
  7. Build, then run your tests.