Table of Contents

Dependency Injection-related Questions

In the following section, you will find common questions and answers to utilizing dependency injection with Discord.Commands and Discord.Interactions, as well as common troubleshooting steps regarding DI.

What is a service? Why does my module not hold any data after execution?

In Discord.Net, modules are created similarly to ASP.NET, meaning that they have a transient nature; modules are spawned whenever a request is received, and are killed from memory when the execution finishes. In other words, you cannot store persistent data inside a module. Consider using a service if you wish to workaround this.

Service is often used to hold data externally so that they persist throughout execution. Think of it like a chest that holds whatever you throw at it that won't be affected by anything unless you want it to. Note that you should also learn Microsoft's implementation of Dependency Injection (video) before proceeding.

A brief example of service and dependency injection can be seen below.

public class MyService
{
    public string MyCoolString { get; set; }
}
public class Setup
{
    public IServiceProvider BuildProvider() => 
            new ServiceCollection()
            .AddSingleton<MyService>()
            .BuildServiceProvider();
}
public class MyModule : ModuleBase<SocketCommandContext>
{
    // Inject via public settable prop
    public MyService MyService { get; set; }
    
    // ...or via the module's constructor

    // private readonly MyService _myService;
    // public MyModule (MyService myService) => _myService = myService;
    
    [Command("string")]
    public Task GetOrSetStringAsync(string input)
    {
        if (string.IsNullOrEmpty(_myService.MyCoolString)) _myService.MyCoolString = input;
        return ReplyAsync(_myService.MyCoolString);
    }
}

Why is my Command/Interaction Service complaining about a missing dependency?

If you encounter an error similar to Failed to create MyModule, dependency MyExternalDependency was not found., you may have forgotten to add the external dependency to the dependency container.

For example, if your module, MyModule, requests a DatabaseService in its constructor, the DatabaseService must be present in the IServiceProvider when registering MyModule.

public class MyModule : ModuleBase<SocketCommandContext>
{
    private readonly DatabaseService _dbService;
    public MyModule(DatabaseService dbService)
        => _dbService = dbService;
}
public class CommandHandler
{
    private readonly CommandService _commands;
    private readonly IServiceProvider _services;
    public CommandHandler(DiscordSocketClient client)
    {
        _services = new ServiceCollection()
            .AddSingleton<CommandService>()
            .AddSingleton(client)
            // We are missing DatabaseService!
            .BuildServiceProvider();
    }
    public async Task RegisterCommandsAsync()
    {
        // ...
        // The method fails here because DatabaseService is a required
        // dependency and cannot be resolved by the dependency
        // injection service at runtime since the service is not
        // registered in this instance of _services.
        await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
        // ...

        // The same approach applies to the interaction service.
        // Make sure to resolve these issues!
    }
}