Let’s explore two popular dependency injection frameworks for dotnet as we compare Scrutor vs Autofac in C#! Dependency injection becomes (almost) a necessity as your application grows in size and complexity, and having the right dependency injection framework to use can make a world of difference.
Starting with a brief explanation of what dependency injection is in C#: Dependency injection is a design approach that allows objects to be loosely coupled and promotes the concept of passing in (or “injecting”) dependencies rather than directly creating them within a class. This promotes modularity, testability, and flexibility in your codebase.
So why is it important to choose the right dependency injection framework for your C# projects? Well, the right framework can greatly simplify your codebase, improve maintainability, and enhance the overall performance of your application. Scrutor and Autofac are two popular options to consider, and in this article, we’ll compare and analyze them in detail.
Scrutor and Autofac are both popular dependency injection frameworks in the C# ecosystem. They provide developers with the tools to effectively manage dependencies and promote modular and extensible software architectures (and we love that kind of stuff around here!). In this section, I’ll provide a brief introduction to both Scrutor and Autofac to kick us off in our comparison.
Scrutor is a lightweight, convention-based assembly scanning extension for the default .NET Core DI container. It simplifies the registration of services by allowing developers to use attribute-based conventions rather than explicit registrations. With Scrutor, you can define rules and patterns that automatically discover and register types based on specific attributes or naming conventions.
Some key advantages of Scrutor include:
Autofac is a powerful and feature-rich DI container for .NET applications, and historically, this has been my go-to for dependency injection. It provides advanced capabilities and extensive configurability, making it suitable for complex projects. Autofac offers a ton of features and extensions that enable developers to achieve fine-grained control over dependency injection, but that might also make it overly complicated for many use-cases.
Some key advantages of Autofac include:
Scrutor and Autofac are both great options for dependency injection in C# applications. Scrutor offers a lightweight and convention-based approach, simplifying service registration, while Autofac provides extensive configurability and advanced features for complex projects. Depending on the specific needs and complexity of your project, you can choose the framework that best suits your requirements — there is no universal “best” here.
When comparing Scrutor and Autofac for dependency injection in C#, there are several key differences to consider. In this section, we’ll explore these differences in terms of key features, syntax, and usage, as well as their approach to registration and resolving dependencies.
Generally speaking, Scrutor is a lightweight library that offers a simple and intuitive way to scan assemblies and apply conventions for dependency registration. It provides powerful filtering capabilities, allowing developers to easily specify which types should be included or excluded during the scanning process. But the real benefit, in my opinion, is in the simplicity and the fact it works with IServiceCollection seamlessly.
On the other hand, Autofac is a more robust and feature-rich dependency injection container. It offers more advanced features such as instance scopes, lifetime management, interception, module system, and integration with other frameworks. That means also the IServiceCollection we get in ASP.NET Core apps — but I admit it feels a bit more clunky.
When it comes to syntax and usage, both Scrutor and Autofac provide clean and concise APIs for registering and resolving dependencies. However, they differ in their approach.
Scrutor leverages the power of LINQ expressions to define conventions for dependency scanning and registration. It allows developers to specify rules for identifying types based on naming conventions, interfaces implemented, attributes applied, or any custom criteria. This makes it easy to quickly set up and automate the registration process without writing explicit code for each dependency.
Autofac, on the other hand, uses a more traditional and explicit syntax for registration. Developers need to explicitly specify the mapping between interfaces and concrete implementations using its fluent registration API. While this requires more manual setup compared to Scrutor’s convention-based approach, it offers more control and flexibility for complex scenarios.
I’ve found that I’ve had to create my own dependency scanning solutions for Autofac, although they’re very straightforward with some reflection. You can see how I manage dependency scanning for Autofac in this video:
In this section, I’ll provide you with code examples and a step-by-step guide on how to use Scrutor for dependency injection in C#. Of course, this will be a contrived example so you’ll want to tailor the concepts here to be applicable to *your* situation. As much as I want to provide you with copy+paste code, I can’t possibly know everyone’s application structure. You’ve been warned! 🙂
To begin, make sure you have Scrutor installed in your project. You can do this by adding the Scrutor
NuGet package to your solution. At the time of writing, 4.2.2 is the latest available version of Scrutor. You can of course find more information about Scrutor in C# before diving too far in by checking out the GitHub for it.
First, let’s start by setting up the DI container in your application’s startup class. Here’s an example using the default DI container in ASP.NET Core:
using Microsoft.Extensions.DependencyInjection;
using Scrutor;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.Scan(scan => scan
.FromAssemblyOf<Startup>()
.AddClasses()
.AsMatchingInterface()
.WithScopedLifetime()
);
// Add other services and configurations here
}
}
In the above code, services.Scan
is used to scan the assemblies and register the types with their corresponding interfaces. The FromAssemblyOf<Startup>
method specifies which assembly to scan. The AddClasses()
method selects all classes to be registered, and the AsMatchingInterface()
method specifies that each class should be registered with its matching interface. Finally, the WithScopedLifetime()
method specifies the lifetime of the services to be scoped.
Let’s continue by creating an example interface and its corresponding implementation. Assuming we have an IEmailService
interface and the EmailService
class implementing it:
public interface IEmailService
{
void SendEmail(string recipient, string message);
}
public class EmailService : IEmailService
{
public void SendEmail(string recipient, string message)
{
// Code to send email
}
}
Now, we can consume the registered services using constructor injection. Let’s assume we have a HomeController
, which requires an IEmailService
:
public class HomeController : Controller
{
private readonly IEmailService _emailService;
public HomeController(IEmailService emailService)
{
_emailService = emailService;
}
public IActionResult Index()
{
// Use the email service to send emails
_emailService.SendEmail("[email protected]", "Hello, Scrutor!");
return View();
}
}
In the above code, the HomeController
class has a constructor that takes in an IEmailService
dependency. The DI container takes care of injecting the EmailService
implementation when the HomeController
is instantiated.
In this section, I’ll provide a step-by-step guide on how to use Autofac for dependency injection in C#. I’ll include code snippets and explanations to help you understand the process.
Before we can start using Autofac for dependency injection, we need to install the necessary packages. Open your project in Visual Studio and navigate to the NuGet Package Manager. Search for “Autofac” and install the package.
Once the package is installed, we can start setting up the Autofac container. The container is responsible for managing and resolving dependencies in our application. In your project, open the Startup.cs
or similar file where you configure your services.
First, add the necessary namespaces at the top of the file:
using Autofac;
using Autofac.Extensions.DependencyInjection;
Next, create a method to configure the Autofac container:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var builder = new ContainerBuilder();
// Add your services and dependencies here
builder.Populate(services);
var container = builder.Build();
return new AutofacServiceProvider(container);
}
In the ConfigureServices
method, create an instance of the ContainerBuilder
class. This class will be used to register your services and dependencies. Populate the container with the services from the services
parameter, and then build the container.
Finally, return a new instance of the AutofacServiceProvider
class, passing in the container as a parameter.
With the container set up, we can now register our services and dependencies. In the ConfigureServices
method, you can use the builder
instance to register your services.
Here’s an example registering a service with a single implementation:
builder.RegisterType<MyService>().As<IMyService>();
In this example, we register MyService
as the implementation of IMyService
. This means that whenever IMyService
is requested, Autofac will provide an instance of MyService
.
You can also register multiple implementations of an interface using the RegisterType
method:
builder.RegisterType<MyService1>().As<IMyService>();
builder.RegisterType<MyService2>().As<IMyService>();
In this case, there are two implementations of IMyService
, MyService1
and MyService2
. Whenever IMyService
is requested, Autofac will provide an instance of either MyService1
or MyService2
.
Now that our services are registered, we can resolve them using the Autofac container. In your classes where you need to use a dependency, you can add a constructor parameter of the dependency type.
public class MyController
{
private readonly IMyService _myService;
public MyController(IMyService myService)
{
_myService = myService;
}
// Use _myService in your methods
}
In this example, MyController
depends on IMyService
. By adding it as a constructor parameter, Autofac will automatically resolve and provide an instance of IMyService
when creating a new instance of MyController
.
In a modular C# application with plugin support, dependency injection becomes vital for integrating and managing plugins seamlessly. It’s difficult for me to imagine how someone might need to either manually wire these types of things up, or roll their own type of dependency/plugin loading system… especially when these things work so well out-of-the-box! Let’s see how Scrutor and Autofac can be applied to handle this scenario effectively.
With Scrutor, we can use the Assemblies.FromPath
method to scan and register assemblies dynamically. This enables us to discover and register plugins at runtime.
public void ConfigureServices(IServiceCollection services)
{
services.Scan(scan => scan
.FromAssembliesFromPath(
AppDomain.CurrentDomain.BaseDirectory,
"SomePluginAssemblies")
.AddClasses()
.AsImplementedInterfaces()
.WithTransientLifetime());
}
Autofac provides a similar capability with its AssemblyFinder
class, allowing us to scan and register assemblies dynamically. We can use the RegisterAssemblyTypes
method to achieve this.
public void ConfigureContainer(ContainerBuilder builder)
{
var assemblyFinder = new AssemblyFinder();
var pluginAssemblies = assemblyFinder.GetAssembliesInFolder("SomePluginAssemblies");
builder.RegisterAssemblyTypes(pluginAssemblies)
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
Throughout this article, we looked at a comparison of Scrutor and Autofac in C# with the goal of understanding the differences between these two dependency injection frameworks in C#. We explored their features, advantages, and use cases through code examples to give readers a clear understanding of how they work in practice.
When choosing between Scrutor vs Autofac in C#, it is important to consider factors such as project complexity, performance needs, community support, and personal familiarity with the frameworks. Scrutor’s simplicity and flexibility make it a good choice for small to medium-sized projects or situations where rapid development and convention-based registration are preferred. Autofac, with its advanced features and mature ecosystem, is well-suited for complex enterprise-level applications that require extensive customization and fine-grained control over the dependency injection process.
In the end, you’ll want to work with your team to decide what’s a better fit. But either of these two options is likely going to be better than no dependency injection at all! If you found this useful and you’re looking for more learning opportunities, consider subscribing to my free weekly software engineering newsletter and check out my free videos on YouTube!
Also published here.