[ on ] chu .tech
← Blog

ABP series · 2026-02-02

How ABP's ICurrentTenant Works Under the Hood (and When It Fails)

A practical look at ABP Framework tenant resolution: the request pipeline, the unit of work, connection string selection — and the three moments where ICurrentTenant silently returns null.

ICurrentTenant.Current is the most-used interface in an ABP multi-tenant app, and the least understood. When it returns null, half your queries misbehave. This is what actually happens between the HTTP request and your repository call.

The chain

  1. Tenant resolution middleware reads the tenant from the route ({tenantName}), a subdomain, or a header, and calls CurrentTenant.Change(tenantId).
  2. CurrentTenant.Change creates a new scope with AsyncLocal — the value flows down the async call chain, including across awaits, but not across threads you spawn manually (Task.Run, ThreadPool).
  3. The unit of work reads ICurrentTenant.Id at BeginTransaction time to decide which connection string to use.
  4. Repositories and IQueryable filter by the resolved tenant id inside the UoW’s data filter.

Where it breaks

  • Manual threads: the AsyncLocal never reaches a Task.Run body. Any tenant-dependent work inside it runs as null.
  • Timing vs. lazy loading: if you Change the tenant after the UoW already opened its connection, the filter uses the new id but the connection is the old tenant’s. Same session, wrong database.
  • IUnitOfWorkManager.Begin in a new scope: some ABP modules begin a fresh UoW that re-resolves the tenant from ICurrentTenant — fine — but if you also reset CurrentTenant in that scope, the new UoW picks up null.

Debugging in one step

Log this at the start of the first repository call:

Logger.LogInformation("tenant={TenantId} conn={ConnString}", CurrentTenant.Id, _dbContext.Database.GetConnectionString());

If tenant is non-null but conn is the host’s, the UoW opened its connection before tenant resolution — reorder the pipeline. If both are null, the ambient scope was lost — find the thread hop. Two log lines, every time, beats reading stack traces.

✉ Book a call