Scrutor in C# – 3 Simple Tips to Level Up Dependency Injection
Tags: csharp, coding, webdev, programming
Published: 2024-03-10 16:00:00

The post [Scrutor in C# – 3 Simple Tips to Level Up Dependency Injection](https://www.devleader.ca/2024/03/01/scrutor-in-c-3-simple-tips-to-level-up-dependency-injection/) appeared first on [Dev Leader](https://www.devleader.ca).

Dependency Injection in C# is a lifesaver when organizing dependencies — especially in more complex ASP.NET Core applications. And if you’re already [familiar with IServiceCollection](https://www.devleader.ca/2024/02/21/iservicecollection-in-c-simplified-beginners-guide-for-dependency-injection/) or just want to stick as close as possible to the already provided DI offerings, [Scrutor in C#](https://www.devleader.ca/2024/02/23/scrutor-vs-autofac-in-c-what-you-need-to-know/) is a great enhancement.

In this article, I’ll provide you with a high-level overview of Dependency Injection and Scrutor in C#. But from there, we’re jumping right into 3 tips that you can use with Scrutor that could prove to be very helpful in your application!

---

## What is Scrutor in CSharp?

Scrutor is a powerful NuGet package in C# that enhances dependency injection. It [simplifies the registration and resolution of dependencies,](https://www.devleader.ca/2024/02/21/iservicecollection-in-c-simplified-beginners-guide-for-dependency-injection/) making it easier for you to manage and organize your code.

Dependency injection is a popular approach to configuring applications that promotes loose coupling and improves testability and maintainability. It involves injecting dependencies into a class rather than directly creating them within the class. This means that the responsibility of creating the dependency is not owned by the class requiring it, but instead, owned by someone up the call stack. Eventually, this pushes nearly all dependency creation to the entry point of an application, making it unwieldy. But dependency injection frameworks help [clean up and organize all of this logic](https://www.devleader.ca/2023/10/02/how-to-organize-autofac-modules-5-tips-for-organizing-code/).

Scrutor takes this concept a step further by providing a simple and intuitive way to register and resolve dependencies. With Scrutor, you no longer need to manually register each dependency one by one. Instead, you can use conventions and attributes to automate the process.

{% embed https://devleaderweekly.substack.com/embed %} 

## 1 – Using Scrutor for Registration and Resolution

To illustrate how to use Scrutor in C# for registration and resolution, let’s consider a simple scenario. Imagine we have an application that requires the use of different data repositories, such as UserRepository and ProductRepostiory.

First, we need to install the Scrutor NuGet package in our project. Open the Package Manager Console and run the following command:

```csharp
Install-Package Scrutor
```

Next, we need to define our repositories and their corresponding interfaces. This example is essentially empty, but we’ll be referring to these in a moment:

```csharp
public interface IUserRepository
{
    // Interface methods
}

public class UserRepository : IUserRepository 
{
    // Implementation
}

public interface IProductRepository
{
    // Interface methods
}

public class ProductRepository : IProductRepository
{
    // Implementation
}
```

Now, let’s wire up our dependencies using Scrutor. In your startup code (such as in the ConfigureServices method in ASP.NET Core), add the following code:

```csharp
services.Scan(scan => scan
    .FromAssemblyOf<Startup>()
    .AddClasses(classes => classes.AssignableToAny(
        typeof(IUserRepository),
        typeof(IProductRepository)))
    .AsImplementedInterfaces()
    .WithScopedLifetime());
```

This code uses the Scan method from Scrutor to scan the assembly that contains the Startup class. It then filters out the classes that are assignable to IUserRepository or IProductRepository interfaces. Finally, it maps the implementations of these interfaces and registers them with their respective interfaces.

Now, we can inject these dependencies into our classes. For example, let’s say we have a UserService class that requires IUserRepository:

```csharp
public class UserService 
{
    private readonly IUserRepository _userRepository; 
    
    public UserService(IUserRepository userRepository) 
    { 
        _userRepository = userRepository;
    }

    // Rest of the class implementation
}
```

By declaring IUserRepository as a dependency in the constructor, Scrutor along with IServiceCollection will automatically resolve and inject the UserRepository instance for us.

---

## 2 – The Decorator Pattern – Example of Logging for Services

Imagine you have a service interface `IService` and an implementation `MyService`. You want to log the entry and exit of each method call in `MyService` without polluting its business logic with logging concerns.

First, define the `IService` interface and its implementation:

```csharp
public interface IService
{
    void DoWork();
}

public class MyService : IService
{
    public void DoWork()
    {
        // Business logic here
    }
}
```

Next, create a decorator class `LoggingDecorator` that implements `IService`. This class will wrap the actual service and add logging around the delegated method calls:

```csharp
public class LoggingDecorator : IService
{
    private readonly IService _decorated;
    private readonly ILogger<LoggingDecorator> _logger;

    public LoggingDecorator(IService decorated, ILogger<LoggingDecorator> logger)
    {
        _decorated = decorated;
        _logger = logger;
    }

    public void DoWork()
    {
        _logger.LogInformation("Starting work.");
        _decorated.DoWork();
        _logger.LogInformation("Finished work.");
    }
}
```

Now, use Scrutor to register your service and its decorator in the `Startup.cs` or wherever you configure your services:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    // Register the base service
    services.AddScoped<IService, MyService>();

    // Use Scrutor to apply the decorator
    services.Decorate<IService, LoggingDecorator>();

    // Make sure to register the ILogger or ILogger<T> dependencies if not already done

}
```

This setup uses Scrutor to wrap the `IService` registration with the `LoggingDecorator`. When resolving `IService`, the DI container will provide an instance of `LoggingDecorator`, which in turn wraps an instance of `MyService`. This approach achieves the separation of concerns by keeping the logging logic outside of your business logic.

---

## 3 – Feature Toggle for Service Implementations

When we’re building complex systems, there are often cases where we want to introduce something like feature flags. These allow us to take different code paths based on configuration and not have to recompile and deploy an entire application. In some situations, it’s not just about picking a different code path, it’s about swapping an entire implementation of something!

Let’s try out an example together! Assume you have an `IFeatureService` interface with multiple implementations: `NewFeatureService` (a new, experimental feature) and `StandardFeatureService` (the current, stable feature). You want to switch between these implementations at runtime based on a configuration flag.

First, define the `IFeatureService` interface and its implementations:

```csharp
public interface IFeatureService
{
    void ExecuteFeature();
}

public class NewFeatureService : IFeatureService
{
    public void ExecuteFeature()
    {
        // New feature logic
    }
}

public class StandardFeatureService : IFeatureService
{
    public void ExecuteFeature()
    {
        // Standard feature logic
    }
}
```

Next, you need a way to determine which implementation to use based on a feature toggle setting. This could be a value in `appsettings.json`, a database setting, or any other configuration source.

Now, use Scrutor to dynamically register the appropriate service implementation based on the feature toggle:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    // Assume GetFeatureToggleValue() retrieves the current feature toggle setting
    var useNewFeature = GetFeatureToggleValue("UseNewFeature");

    // Dynamically register the appropriate implementation based on the feature toggle
    if (useNewFeature)
    {
        services.AddScoped<IFeatureService, NewFeatureService>();
    }
    else
    {
        services.AddScoped<IFeatureService, StandardFeatureService>();
    }

    // Optional: You could combine this with the previous example
    // to use the decorator pattern too!
    // services.Decorate<IFeatureService, FeatureServiceProxy>();
}

private bool GetFeatureToggleValue(string featureName)
{
    // This method would typically check your
    // configuration source to determine if the feature is enabled
    // For simplicity, this is just a placeholder
    return false; // or true based on actual configuration
}
```

Taking an approach like this can help us with:

* **Flexibility**: Allows the application to switch between feature implementations without needing to redeploy. You can enable or disable features with just a configuration change.
    
* **Testing in Production**: You can test new features in a production environment with a limited set of users by toggling the feature on for those users.
    
* **Gradual Rollouts**: Helps in rolling out features gradually to monitor the impact and ensure stability before a wider release.
    
* **Risk Mitigation**: Quickly revert to the old implementation if issues arise with the new feature, minimizing downtime and impact on users.
    

---

## Wrapping Up Scrutor in CSharp

In this article, I provided you with 3 simple tips for dependency injection with Scrutor in C#. After a brief overview of what dependency injection is and how Scrutor fits into it, we jumped right into our examples. We got to see assembly scanning, how to decorate dependencies, and even how to consider feature flagging entire implementations!

Dependency injection is something that can greatly simplify your applications as they grow in complexity, and Scrutor can be a huge help if you want to stick to the [built-in IServiceCollection offering from Microsoft](https://www.devleader.ca/2024/02/21/iservicecollection-in-c-simplified-beginners-guide-for-dependency-injection/). If you found this useful and you’re looking for more learning opportunities, consider [subscribing to my free weekly software engineering newsletter](https://subscribe.devleader.ca) and check out my [free videos on YouTube](https://www.youtube.com/@devleader?sub_confirmation=1)! Join me and the [other community members on Discord](https://www.devleader.ca/discord-community-access/)!

{% embed https://devleaderweekly.substack.com/embed %}

---

## **Want More Dev Leader Content?**

* Follow along on this platform if you haven’t already!    
* Subscribe to my free weekly software engineering and dotnet-focused newsletter. I include exclusive articles and early access to videos:  
    [**SUBSCRIBE FOR FREE**](https://subscribe.devleader.ca/)    
* Looking for courses? Check out my offerings:  
    [**VIEW COURSES**](https://devleader.ca/courses)    
* E-Books & other resources:  
    [**VIEW RESOURCES**](https://products.devleader.ca/)    
* Watch hundreds of full-length videos on my YouTube channel:  
    [**VISIT CHANNEL**](https://youtube.com/@devleader?sub_confirmation=1)    
* Visit my website for hundreds of articles on various software engineering topics (including code snippets):  
    [**VISIT WEBSITE**](https://devleader.ca/)    
* Check out the repository with many code examples from my articles and videos on GitHub:  
    [**VIEW REPOSITORY**](https://github.com/ncosentino/DevLeader)