docs: Improved DI documentation (#2407)

This commit is contained in:
Armano den Boef
2022-08-02 11:20:27 +02:00
committed by GitHub
parent 503fa755a0
commit 6fdcf98240
28 changed files with 460 additions and 198 deletions

View File

@@ -1,51 +0,0 @@
---
uid: Guides.TextCommands.DI
title: Dependency Injection
---
# Dependency Injection
The Text Command Service is bundled with a very barebone Dependency
Injection service for your convenience. It is recommended that you use
DI when writing your modules.
> [!WARNING]
> If you were brought here from the Interaction Service guides,
> make sure to replace all namespaces that imply `Discord.Commands` with `Discord.Interactions`
## Setup
1. Create a @Microsoft.Extensions.DependencyInjection.ServiceCollection.
2. Add the dependencies to the service collection that you wish
to use in the modules.
3. Build the service collection into a service provider.
4. Pass the service collection into @Discord.Commands.CommandService.AddModulesAsync* / @Discord.Commands.CommandService.AddModuleAsync* , @Discord.Commands.CommandService.ExecuteAsync* .
### Example - Setting up Injection
[!code-csharp[IServiceProvider Setup](samples/dependency-injection/dependency_map_setup.cs)]
## Usage in Modules
In the constructor of your module, any parameters will be filled in by
the @System.IServiceProvider that you've passed.
Any publicly settable properties will also be filled in the same
manner.
> [!NOTE]
> Annotating a property with a [DontInjectAttribute] attribute will
> prevent the property from being injected.
> [!NOTE]
> If you accept `CommandService` or `IServiceProvider` as a parameter
> in your constructor or as an injectable property, these entries will
> be filled by the `CommandService` that the module is loaded from and
> the `IServiceProvider` that is passed into it respectively.
### Example - Injection in Modules
[!code-csharp[Injection Modules](samples/dependency-injection/dependency_module.cs)]
[!code-csharp[Disallow Dependency Injection](samples/dependency-injection/dependency_module_noinject.cs)]
[DontInjectAttribute]: xref:Discord.Commands.DontInjectAttribute

View File

@@ -187,7 +187,7 @@ service provider.
### Module Constructors
Modules are constructed using [Dependency Injection](xref:Guides.TextCommands.DI). Any parameters
Modules are constructed using [Dependency Injection](xref:Guides.DI.Intro). Any parameters
that are placed in the Module's constructor must be injected into an
@System.IServiceProvider first.

View File

@@ -1,65 +0,0 @@
public class Initialize
{
private readonly CommandService _commands;
private readonly DiscordSocketClient _client;
// Ask if there are existing CommandService and DiscordSocketClient
// instance. If there are, we retrieve them and add them to the
// DI container; if not, we create our own.
public Initialize(CommandService commands = null, DiscordSocketClient client = null)
{
_commands = commands ?? new CommandService();
_client = client ?? new DiscordSocketClient();
}
public IServiceProvider BuildServiceProvider() => new ServiceCollection()
.AddSingleton(_client)
.AddSingleton(_commands)
// You can pass in an instance of the desired type
.AddSingleton(new NotificationService())
// ...or by using the generic method.
//
// The benefit of using the generic method is that
// ASP.NET DI will attempt to inject the required
// dependencies that are specified under the constructor
// for us.
.AddSingleton<DatabaseService>()
.AddSingleton<CommandHandler>()
.BuildServiceProvider();
}
public class CommandHandler
{
private readonly DiscordSocketClient _client;
private readonly CommandService _commands;
private readonly IServiceProvider _services;
public CommandHandler(IServiceProvider services, CommandService commands, DiscordSocketClient client)
{
_commands = commands;
_services = services;
_client = client;
}
public async Task InitializeAsync()
{
// Pass the service provider to the second parameter of
// AddModulesAsync to inject dependencies to all modules
// that may require them.
await _commands.AddModulesAsync(
assembly: Assembly.GetEntryAssembly(),
services: _services);
_client.MessageReceived += HandleCommandAsync;
}
public async Task HandleCommandAsync(SocketMessage msg)
{
// ...
// Pass the service provider to the ExecuteAsync method for
// precondition checks.
await _commands.ExecuteAsync(
context: context,
argPos: argPos,
services: _services);
// ...
}
}

View File

@@ -1,37 +0,0 @@
// After setting up dependency injection, modules will need to request
// the dependencies to let the library know to pass
// them along during execution.
// Dependency can be injected in two ways with Discord.Net.
// You may inject any required dependencies via...
// the module constructor
// -or-
// public settable properties
// Injection via constructor
public class DatabaseModule : ModuleBase<SocketCommandContext>
{
private readonly DatabaseService _database;
public DatabaseModule(DatabaseService database)
{
_database = database;
}
[Command("read")]
public async Task ReadFromDbAsync()
{
await ReplyAsync(_database.GetData());
}
}
// Injection via public settable properties
public class DatabaseModule : ModuleBase<SocketCommandContext>
{
public DatabaseService DbService { get; set; }
[Command("read")]
public async Task ReadFromDbAsync()
{
await ReplyAsync(DbService.GetData());
}
}

View File

@@ -1,29 +0,0 @@
// Sometimes injecting dependencies automatically with the provided
// methods in the prior example may not be desired.
// You may explicitly tell Discord.Net to **not** inject the properties
// by either...
// restricting the access modifier
// -or-
// applying DontInjectAttribute to the property
// Restricting the access modifier of the property
public class ImageModule : ModuleBase<SocketCommandContext>
{
public ImageService ImageService { get; }
public ImageModule()
{
ImageService = new ImageService();
}
}
// Applying DontInjectAttribute
public class ImageModule : ModuleBase<SocketCommandContext>
{
[DontInject]
public ImageService ImageService { get; set; }
public ImageModule()
{
ImageService = new ImageService();
}
}