Truc's NotesVol. 1 (2026)

Decorator with Scrutor in .NET

Truc Nguyen*

AbstractA quick note on how to add caching (or any cross-cutting concern) to your services using the decorator pattern and Scrutor — without touching the original service.

Keywords:tech · c# · dotnet · design patterns

Use Scrutor to wrap your existing service with a decorator that adds caching, logging, or any cross-cutting concern — without modifying the original service.


Why the decorator pattern?

Injecting cache logic directly into your service violates the single responsibility principle. With a decorator, your LookupService only handles business logic. Caching is a separate concern.

Setup with Scrutor

Register the real service first, then decorate it:

builder.Services.AddScoped<ILookupService, LookupService>();
builder.Services.Decorate<ILookupService, LookupCacheDecorator>();
// stack decorator
// builder.Services.Decorate<ILookupService, LookupLoggingDecorator>();

That's it. Every time ILookupService is resolved, you get LookupCacheDecorator wrapping LookupService.

The decorator

public class LookupCacheDecorator(
    ILookupService inner,
    IHybridCache cache,
    ILogger<LookupCacheDecorator> logger) : ILookupService
{
    public async ValueTask<IList<LookupModel>> GetCategoriesAsync()
    {
        var isCacheMissed = false;

        var categories = await cache.GetOrCreateAsync("/categories", async (ct) =>
        {
            isCacheMissed = true;
            logger.LogInformation("[CACHE MISS] Fetching from database...");
            return await inner.GetCategoriesAsync();
        });

        if (!isCacheMissed)
        {
            logger.LogInformation("[CACHE HIT] Returned from cache.");
        }

        return categories;
    }
}

Key points

  • inner is the real LookupService, injected automatically by Scrutor.
  • the decorator is transparent to callers — they just use ILookupService.
  • you can stack multiple decorators (e.g., logging → caching → retry) — the last .Decorate call becomes the outermost layer, so it runs first.

Cite

@misc{nguyen2026:dotnet-decorator-scrutor,
  author = {Nguyen, Truc},
  title  = {Decorator with Scrutor in .NET},
  year   = {2026},
  url    = {https://tructn.github.io/blog/dotnet-decorator-scrutor},
  note   = {Truc's Notes}
}

← All articles