Tag Archives: Scrutor

A Cleaner Program.cs / Startup.cs with Scrutor

I’ve previously written about the Scrutor library. However, this post covers something that has long irritated me about using an IoC container. Typically, when you have a fairly complex site, you’ll end up with dozens of statements like the following:

builder.Services.AddScoped<ISearchService, SearchService>();
builder.Services.AddScoped<IResourceDataAccess, ResourceDataAccess>();

It turns out that one of the other things that Scrutor can do for you is to work out which dependencies you need to register. For example, let’s consider the two classes above; let’s say that the first is in the main assembly of the project:

builder.Services.Scan(scan => scan
    .FromCallingAssembly()
        .AddClasses(true)
            .AsMatchingInterface()
            .WithScopedLifetime());

So, what does this do?

Well, FromCallingAssembly points it at the main assembly (that is, the one which you’re calling this registration from). FromClasses(true) then returns a list of all public classes and interfaces.

Finally, AsMatchingInterface matches classes with their interfaces, assuming a one-to-one pairing (if you don’t have that then there are other options to cope with that); and WithScopedLifetime will register them as scoped.

That worked well, but when I ran it, I realised that the second class (ResourceDataAccess) hadn’t registered. The reason being that it wasn’t from the calling assembly, but lived in a referenced project. An easy way to fix this was:

builder.Services.Scan(scan => scan
    .FromCallingAssembly()
        .AddClasses(true)
            .AsMatchingInterface()
            .WithScopedLifetime()
    .FromAssemblyOf<IResourceDataAccess>()
        .AddClasses(true)
            .AsMatchingInterface()
            .WithScopedLifetime());

Notice that we can simply return to the start following the With…Lifetime(), and this time, we tell it to register any classes found in the same assembly as IResourceDataAccess.

If we look at the list of assemblies, we can see this has worked:

What this means is that, each time you add a new class, you don’t have to add a registration in the startup / program file. This is perhaps a good and bad thing: arguably, if the list gets so large that it’s noticeable, then you might have gauged your decomposition incorrectly.

Using Scrutor to Implement the Decorator Pattern

I recently came across a very cool library, thanks to this video by Nick Chapsas. The library is Scrutor. In this post, I’m going to run through a version of the Open-Closed Principle that this makes possible.

An Overly Complex Hello World App

Let’s start by creating a needlessly complex app that prints Hello World. Instead of simply printing Hello World we’ll use DI to inject a service that prints it. Let’s start with the main program.cs code (in .Net 6):

using Microsoft.Extensions.DependencyInjection;
using scrutortest;

var serviceCollection = new ServiceCollection();

serviceCollection.AddSingleton<ITestLogger, TestLogger>();

var serviceProvider = serviceCollection.BuildServiceProvider();

var testLogger = serviceProvider.GetRequiredService<ITestLogger>();
testLogger.Log("hello world");

Impressive, eh? Here’s the interface that we now rely on:

internal interface ITestLogger
{
    public void Log(string message);
}

And here is our TestLogger class:

    internal class TestLogger : ITestLogger
    {
        public void Log(string message)
        {
            Console.WriteLine(message);
        }
    }

If you implement this, and run it, you’ll see that it works fine – almost as well as the one line version. However, let’s imagine that we now have a requirement to extend this class. After every message, we need to display —OVER— for… some reason.

Extending Our Overly Complex App to be Even More Pointless

There’s a few ways to do this: you can obviously just change the class itself, but that breaches the Open-Closed Principle. That’s where the Decorator Pattern comes in. Scrutor allows us to create a new class that looks like this:

    internal class TestLoggerExtended : ITestLogger
    {
        private readonly ITestLogger _testLogger;

        public TestLoggerExtended(ITestLogger testLogger)
        {
            _testLogger = testLogger;
        }

        public void Log(string message)
        {
            _testLogger.Log(message);
            _testLogger.Log("---OVER---");
        }
    }

There’s a few things of note here: firstly, we’re implementing the same interface as the main / first class; secondly, we’re Injecting said interface into our constructor; and finally, in the Log method, we’re calling the original class. Obviously, if you just register this in the DI container as normal, bad things will happen; so we use the Scrutor Decorate method:

using Microsoft.Extensions.DependencyInjection;
using scrutortest;

var serviceCollection = new ServiceCollection();

serviceCollection.AddSingleton<ITestLogger, TestLogger>();
serviceCollection.Decorate<ITestLogger, TestLoggerExtended>();

var serviceProvider = serviceCollection.BuildServiceProvider();

var testLogger = serviceProvider.GetRequiredService<ITestLogger>();
testLogger.Log("hello world");

If you now run this, you’ll see that the functionality is very similar to inheritance, but you haven’t coupled the two services directly: