Skip to content

Dependency Injection and Lifetimes

Mike Christensen edited this page Aug 28, 2026 · 1 revision

Dependency injection and lifetimes

Imp constructs each resolved page with ActivatorUtilities.CreateInstance and the current HttpContext.RequestServices. Normal constructor injection works without registering page types explicitly.

builder.Services.AddSingleton<TaskStore>();
builder.Services.AddScoped<ICurrentAccount, CurrentAccount>();
builder.Services.AddTransient<EmailFormatter>();
public sealed class Tasks(
   TaskStore store,
   ICurrentAccount account,
   ILogger<Tasks> logger) : BasePage
{
   // Use dependencies during this request.
}

Page lifetime

Normal page classes are constructed per request. Treat them as transient request objects:

  • keep per-request display and validation state on the page;
  • do not cache a page instance;
  • do not pass the page or HttpRequest to background work;
  • dispose scoped services through the normal ASP.NET Core request scope.

The configured NotFoundPageType is an exception in the current implementation: its instance is cached. Keep that page stateless and avoid scoped constructor dependencies. A custom page type returned by OnNotFound is created per request and can use normal scoped dependencies.

Service lifetime selection

  • Singleton: immutable configuration, thread-safe process-local stores/caches, expensive shared clients designed for reuse.
  • Scoped: current-user/application unit of work, database contexts, request-specific services.
  • Transient: lightweight stateless helpers.

The Todo sample uses a thread-safe singleton store so tasks survive between requests but reset on process restart. A production application would usually replace it with a scoped persistence service or repository.

Concurrency

Imp can process requests concurrently. Singleton services and static state must be thread-safe. Do not assume template compilation serializes dynamic rendering: the cache protects compilation lookup, while dynamic page methods still run concurrently on different page instances.

Current request and user

Use Request.HttpContext or inject IHttpContextAccessor into a service when necessary. Prefer passing explicit domain values into deeper application layers instead of coupling them to HTTP.

Authentication should establish HttpContext.User before Imp runs. Pages and services can then consume claims normally.

Custom fallback pages

OnNotFound returns a Type, and Imp constructs it through DI just like an ordinary routed page. This makes a single permalink page useful for many database-backed URLs while keeping storage access injectable and testable.

Clone this wiki locally