Method extension and Dependency Injection in C#.NET
Dependency injection needs the developer to have good knowledge of object-oriented programming paradigms. I won't teach you how to create an IoC Inversion of Control container. I will assume that you have the basics of knowledge and a good mastery of this software design pattern. Dependency Injection is a mechanism that allows us to implement the principle of Inversion of Control (IoC). Through this pattern, we can inject dynamic dependencies for different classes that need those.
When you are using some dotnet libraries, like Remora, SampSharp, or something else you may be confronted to this sort of situation :
.AddCommandGroup<MiscellaneousCommandGroup>()
.AddCommandGroup<SocialCommandGroup>()
.AddCommandGroup<ModerationCommandGroup>()
.AddCommandGroup<TicketCommandGroup>()
.AddCommandGroup<GeneratorCommandGroup>();
And the longer you maintain this code base, the more line will accumulate and you won't even see your base code anymore. You could use * reflections* (Reflection (C#) | Microsoft Docs) and loop through all derived classes of an interface or an abstract class and then automatically register them in your command manager or sort of thing. But if you care about performances, you should not use always reflections.
So how can you proceed to make this code better and more extensible ? That's the question I wondered during my discord bot project development. You can use dependency injection and methods extensions !
The goal is to separate the different responsibilities, you don't want to save your orders in the entry point, and so
have a 150,000 line file, but you want to separate the responsibilities, and so allow changes without actually going
through so many lines and the entry point.
In the architecture and structure of my project, I have a class library: CommandGroups, which allows me to separate the
responsibilities in the first place. I then separate the different command groups, (moderation, social, administrative,
etc.). This is already a good way to have a cleaner structure, but it is not enough! My entry point through my order
manager, records all command groups in this other project. So I create a Setup.cs
class in the root of the project,
and start using the method extensions.
I want to have to add only one line in my IoC container, in order to allow the registration of all the commands in the project:
internal static IServiceCollection AddCommandGroups(
this IServiceCollection services)
So I create a static method, and extension to IServiceCollection, then in this method, I add all the command records:
internal static IServiceCollection AddCommandGroups(
this IServiceCollection services)
{
return services
.AddCommandGroup<MiscellaneousCommandGroup>()
.AddCommandGroup<SocialCommandGroup>()
.AddCommandGroup<ModerationCommandGroup>()
.AddCommandGroup<TicketCommandGroup>()
.AddCommandGroup<GeneratorCommandGroup>();
}
However, this is not enough to be able to register my commands, I need to register it in the IoC container in my entry point.
private static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
ConfigureServices((_, services) =>
services
.AddHostedService<Worker>()
.AddCommandGroups()
.BuildServiceProvider());
}
And as a result, the IoC container, will register all my commands registered in my extension method, awesome right?!
To conclude, there are of course several methods to achieve a better separation, and respecting the SOLID principles, especially Open-Closed principle, but I leave you the opportunity to research about it and choose through your critical mind the best way.
More information: