Registering services with Scrutor
Using Kristian Hellang's Scrutor library to scan and automatically register dependencies with the Microsoft.Extensions.DependencyInjection IoC container.

I've been using Microsoft.Extensions.DependencyInjection as a lightweight IoC container a lot recently (even in some non-ASP.NET projects).

One feature I've found myself needing is to be able to scan an assembly and automatically register types by convention. While this functionality isn't currently available out-of-the-box, Kristian Hellang's very useful Scrutor library makes it a breeze.

The best way to demonstrate how Scrutor works is with the canonical example from the official documentation. I've reproduced it below for illustrative purposes:

var collection = new ServiceCollection();

collection.Scan(scan => scan
     // We start out with all types in the assembly of ITransientService
    .FromAssemblyOf<ITransientService>()
        // AddClasses starts out with all public, non-abstract types in this assembly.
        // These types are then filtered by the delegate passed to the method.
        // In this case, we filter out only the classes that are assignable to ITransientService
        .AddClasses(classes => classes.AssignableTo<ITransientService>())
            // Whe then specify what type we want to register these classes as.
            // In this case, we wan to register the types as all of its implemented interfaces.
            // So if a type implements 3 interfaces; A, B, C, we'd end up with three separate registrations.
            .AsImplementedInterfaces()
            // And lastly, we specify the lifetime of these registrations.
            .WithTransientLifetime()
        // Here we start again, with a new full set of classes from the assembly above.
        // This time, filtering out only the classes assignable to IScopedService.
        .AddClasses(classes => classes.AssignableTo<IScopedService>())
            // Now, we just want to register these types as a single interface, IScopedService.
            .As<IScopedService>()
            // And again, just specify the lifetime.
            .WithScopedLifetime());

For some real-world examples, I've been using Scrutor in a number of practical ways:

  1. Adding authorization handlers:
services.Scan(scan =>
    scan.FromAssemblyOf<Program>()
        .AddClasses(classes => classes.AssignableTo<IAuthorizationHandler>())
            .AsImplementedInterfaces()
            .WithSingletonLifetime());
  1. Registering MediatR handlers:
services.Scan(scan =>
    scan.FromAssemblyOf<Program>()
        .AddClasses(classes => classes.AssignableTo(typeof(IRequestHandler<,>)))
            .AsImplementedInterfaces()
            .WithTransientLifetime()
        .AddClasses(classes => classes.AssignableTo(typeof(IAsyncRequestHandler<,>)))
            .AsImplementedInterfaces()
            .WithTransientLifetime());

(Unrelated note: You can also use MediatR.Extensions.Microsoft.DependencyInjection to register your MediatR handlers. I've still included this example because it demonstrates a good real-world use case for Scrutor.)

You can probably imagine many other situations where Scrutor can be leveraged to save time and effort when registering your dependencies.

Thanks for reading.


Posted by Matthew King on 3 September 2016
Permission is granted to use all code snippets under CC BY-SA 3.0 (just like StackOverflow), or the MIT license - your choice!
If you enjoyed this post, and you want to show your appreciation, you can buy me a beverage on Ko-fi or Stripe