Think of a technique that can conveniently manage object dependencies while promoting loose coupling between software components; you guessed right…Dependency Injection!
In modern applications, registering services with a Dependency Injection (DI) container is a common practice. However, manually registering every defined service can become cumbersome and error-prone, especially in large-scale projects. To save you some precious time, I have designed this tutorial that explores advanced approach to registering service interfaces. The goal is to help you reduce the manual efforts originally required to register services and improves code maintainability (You are welcome!). Okay, seriously; how do we really simplify service registration?
Simplifying Service Registration
To simplify service registration, we can define a set of extension methods for the `IServiceCollection` interface. The extension method `AddServices` allows us to register services by inheriting from specific interfaces. Let’s take a closer look at the implementation of these extension methods:
The `AddServices` method accepts the `IServiceCollection` and uses method chaining to register services for different lifetimes (`Transient`, `Scoped`, and `Singleton`). By providing the interface type and lifetime, this method automatically scans the application’s assemblies and registers all classes that inherit from the specified interface. See an example below:
The `AddServices` method utilises reflection to scan all assemblies in the current application domain and retrieves all types that implement the specified interface. It then iterates over these types, extracting the service interface and the corresponding implementation. Finally, it invokes the `AddService` method to register the service with the appropriate lifetime as shown below:
The `AddService` method is a helper method that maps the specified lifetime to the corresponding DI registration method (`AddTransient`, `AddScoped`, or `AddSingleton`). It adds the service and implementation types to the `IServiceCollection`, ensuring proper lifecycle management of the registered services.
Let’s assume we have a `TodoService` class that implements the `ITodoService` interface. We can demonstrate how to register this service using the inherited service interface registration approach.
Assuming we want to register the `TodoService` as a transient service. we need to ensure that `ITodoService` inherits the `ITransientService` predefined service interface. See an example below:
Now, you can easily inject the `ITodoService` into your dependent classes and let the DI container handle the service resolution and lifecycle management.
…and that is it. That is how you can easily simplify dependency injection with inherited service interface registration. This technique promotes consistency and convention-based registration. It offers developers that easy guide to follow in naming and organising services which leads to a more standardised and predictable codebase. One key thing we have to take out from this lesson is that inherited service interface registration is the reduction of code duplication. The technique I have shared in this article could help you define a base interface for common dependencies and have multiple services inherit from it. This eliminates the need to define the same dependencies across different services, leading to cleaner and more concise code.
If you enjoyed reading this, don’t forget to share!
Thank you for reading!
About the Author:
Michael Andifon Etim is a talented software engineer and with experience in C#, .Net framework, and other advanced software development stacks. In his spare time, he write to educate upcoming tech stars.