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:
- Adding authorization handlers:
services.Scan(scan => scan.FromAssemblyOf<Program>() .AddClasses(classes => classes.AssignableTo<IAuthorizationHandler>()) .AsImplementedInterfaces() .WithSingletonLifetime());
- 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.