What it is
Scrutor adds assembly scanning and decoration to the built-in .NET dependency injection container, without replacing it.
Scan assemblies to register types by convention, and wrap existing registrations with decorators — both as extension methods on IServiceCollection.
Installation
dotnet add package ScrutorGetting started
The smallest useful thing you can do with it, and what each part means.
csharp
services.Scan(scan => scan
.FromAssemblyOf<BookService>()
.AddClasses(c => c.Where(t => t.Name.EndsWith("Service")))
.AsImplementedInterfaces()
.WithScopedLifetime()
.AddClasses(c => c.AssignableTo(typeof(IRepository<>)))
.AsImplementedInterfaces()
.WithScopedLifetime());
// Replaces dozens of lines like:
// services.AddScoped<IBookService, BookService>();
// services.AddScoped<IAuthorService, AuthorService>();Advanced usage
Where the library earns its place over a simpler alternative.
csharp
services.AddScoped<IBookRepository, SqlBookRepository>();
// Each wraps the previous; consumers still inject IBookRepository.
services.Decorate<IBookRepository, CachingBookRepository>();
services.Decorate<IBookRepository, LoggingBookRepository>();
public class CachingBookRepository : IBookRepository
{
private readonly IBookRepository _inner; // the wrapped instance
private readonly IMemoryCache _cache;
public CachingBookRepository(IBookRepository inner, IMemoryCache cache)
=> (_inner, _cache) = (inner, cache);
public Task<Book?> GetAsync(int id) =>
_cache.GetOrCreateAsync($"book:{id}", _ => _inner.GetAsync(id));
}Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Could not find any registered services for type
- Decorate ran before the underlying registration. Register the implementation first.
- A type is registered twice
- Overlapping scan predicates. Narrow the filters, or use AsSelfWithInterfaces deliberately.
Best practices
- Use Scrutor before reaching for a third-party container — it usually covers the gap.
- Register the base implementation before decorating it, or Decorate throws.
- Keep scan predicates narrow; over-broad scanning registers types you did not intend.
- Prefer decorators over conditional logic inside a service for cross-cutting concerns.
Background
Why it exists, and what it was reacting to.
Microsoft's container is deliberately minimal and omits scanning and decorators. Scrutor adds exactly those two capabilities, which is often the entire reason teams considered switching to Autofac.
