Dependency Injection
Dependency injection (DI) is a technique that helps make your code more modular and testable by letting you pass services from the outside. It reduces tight coupling between components, making your applications easier to maintain and extend.
Scopes
With NetCord.Hosting.Services
scopes are created for each command/interaction and disposed after the command/interaction is completely executed by default. Therefore all code relevant to the command/interaction like for example the IApplicationCommandResultHandler<TContext> will be executed within the same scope.
You can control whether to use scopes or not by setting the UseScopes
property in the options class. For example UseScopes for application commands.
Minimal APIs
Dependency injection with minimal APIs can seem complicated at first, but it is actually quite simple.
What you need to know is that parameters preceding the context parameter are treated as services and parameters following the context parameter are treated as command/interaction parameters. When the context parameter is not present, all parameters are treated as command/interaction parameters.
You can see an example slash command below, but the same rules apply to all services:
host.AddSlashCommand<SlashCommandContext>(
name: "data",
description: "Shows the data!",
(IDataProvider dataProvider, SlashCommandContext context, int count) => string.Join(' ', dataProvider.GetData()
.Take(count)));
Modules
Dependency injection with modules is like everywhere else. You just inject the services via the constructor. The modules behave as if they were transient services, so they are created for each command/interaction.
You can see an example with text commands below, but the same rules apply to all services:
public class DataModule(IDataProvider dataProvider) : CommandModule<CommandContext>
{
[Command("data")]
public string Data(int count) => string.Join(' ', dataProvider.GetData().Take(count));
}
Autocomplete Providers
Same for autocomplete providers, you just inject the services via the constructor. They also behave as if they were transient services.
You can see an example autocomplete provider below:
public class DataAutocompleteProvider(IDataProvider dataProvider) : IAutocompleteProvider<AutocompleteInteractionContext>
{
public ValueTask<IEnumerable<ApplicationCommandOptionChoiceProperties>?> GetChoicesAsync(
ApplicationCommandInteractionDataOption option,
AutocompleteInteractionContext context)
{
var input = option.Value!;
var data = dataProvider.GetData();
var result = data.Where(d => d.Contains(input))
.Take(25)
.Select(d => new ApplicationCommandOptionChoiceProperties(d, d));
return new(result);
}
}