From 6fdcf982402e044135babe5407e61a0b5241c4dc Mon Sep 17 00:00:00 2001 From: Armano den Boef <68127614+Rozen4334@users.noreply.github.com> Date: Tue, 2 Aug 2022 11:20:27 +0200 Subject: [PATCH] docs: Improved DI documentation (#2407) --- docs/guides/dependency_injection/basics.md | 69 +++++++++++++++++++++ .../guides/dependency_injection/images/manager.png | Bin 0 -> 12198 bytes docs/guides/dependency_injection/injection.md | 44 +++++++++++++ .../samples/access-activator.cs | 9 +++ .../dependency_injection/samples/collection.cs | 13 ++++ .../dependency_injection/samples/ctor-injecting.cs | 14 +++++ .../dependency_injection/samples/enumeration.cs | 18 ++++++ .../samples/implicit-registration.cs | 12 ++++ .../guides/dependency_injection/samples/modules.cs | 16 +++++ .../guides/dependency_injection/samples/program.cs | 24 +++++++ .../samples/property-injecting.cs | 9 +++ .../dependency_injection/samples/provider.cs | 26 ++++++++ .../dependency_injection/samples/runasync.cs | 17 +++++ docs/guides/dependency_injection/samples/scoped.cs | 6 ++ .../samples/service-registration.cs | 21 +++++++ .../dependency_injection/samples/services.cs | 9 +++ .../dependency_injection/samples/singleton.cs | 6 ++ .../dependency_injection/samples/transient.cs | 6 ++ docs/guides/dependency_injection/scaling.md | 39 ++++++++++++ docs/guides/dependency_injection/services.md | 48 ++++++++++++++ docs/guides/dependency_injection/types.md | 52 ++++++++++++++++ docs/guides/int_framework/dependency-injection.md | 13 ---- docs/guides/int_framework/intro.md | 3 +- docs/guides/text_commands/dependency-injection.md | 51 --------------- docs/guides/text_commands/intro.md | 2 +- .../dependency-injection/dependency_map_setup.cs | 65 ------------------- .../dependency-injection/dependency_module.cs | 37 ----------- .../dependency_module_noinject.cs | 29 --------- 28 files changed, 460 insertions(+), 198 deletions(-) create mode 100644 docs/guides/dependency_injection/basics.md create mode 100644 docs/guides/dependency_injection/images/manager.png create mode 100644 docs/guides/dependency_injection/injection.md create mode 100644 docs/guides/dependency_injection/samples/access-activator.cs create mode 100644 docs/guides/dependency_injection/samples/collection.cs create mode 100644 docs/guides/dependency_injection/samples/ctor-injecting.cs create mode 100644 docs/guides/dependency_injection/samples/enumeration.cs create mode 100644 docs/guides/dependency_injection/samples/implicit-registration.cs create mode 100644 docs/guides/dependency_injection/samples/modules.cs create mode 100644 docs/guides/dependency_injection/samples/program.cs create mode 100644 docs/guides/dependency_injection/samples/property-injecting.cs create mode 100644 docs/guides/dependency_injection/samples/provider.cs create mode 100644 docs/guides/dependency_injection/samples/runasync.cs create mode 100644 docs/guides/dependency_injection/samples/scoped.cs create mode 100644 docs/guides/dependency_injection/samples/service-registration.cs create mode 100644 docs/guides/dependency_injection/samples/services.cs create mode 100644 docs/guides/dependency_injection/samples/singleton.cs create mode 100644 docs/guides/dependency_injection/samples/transient.cs create mode 100644 docs/guides/dependency_injection/scaling.md create mode 100644 docs/guides/dependency_injection/services.md create mode 100644 docs/guides/dependency_injection/types.md delete mode 100644 docs/guides/int_framework/dependency-injection.md delete mode 100644 docs/guides/text_commands/dependency-injection.md delete mode 100644 docs/guides/text_commands/samples/dependency-injection/dependency_map_setup.cs delete mode 100644 docs/guides/text_commands/samples/dependency-injection/dependency_module.cs delete mode 100644 docs/guides/text_commands/samples/dependency-injection/dependency_module_noinject.cs diff --git a/docs/guides/dependency_injection/basics.md b/docs/guides/dependency_injection/basics.md new file mode 100644 index 000000000..c553ee68c --- /dev/null +++ b/docs/guides/dependency_injection/basics.md @@ -0,0 +1,69 @@ +--- +uid: Guides.DI.Intro +title: Introduction +--- + +# Dependency Injection + +Dependency injection is a feature not required in Discord.Net, but makes it a lot easier to use. +It can be combined with a large number of other libraries, and gives you better control over your application. + +> Further into the documentation, Dependency Injection will be referred to as 'DI'. + +## Installation + +DI is not native to .NET. You need to install the extension packages to your project in order to use it: + +- [Meta](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection/). +- [Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection.Abstractions/). + +> [!WARNING] +> Downloading the abstractions package alone will not give you access to required classes to use DI properly. +> Please install both packages, or choose to only install the meta package to implicitly install both. + +### Visual Package Manager: + +[Installing](images/manager.png) + +### Command Line: + +`PM> Install-Package Microsoft.Extensions.DependencyInjection`. + +> [!TIP] +> ASP.NET already comes packed with all the necessary assemblies in its framework. +> You do not require to install any additional NuGet packages to make full use of all features of DI in ASP.NET projects. + +## Getting started + +First of all, you will need to create an application based around dependency injection, +which in order will be able to access and inject them across the project. + +[!code-csharp[Building the Program](samples/program.cs)] + +In order to freely pass around your dependencies in different classes, +you will need to register them to a new `ServiceCollection` and build them into an `IServiceProvider` as seen above. +The IServiceProvider then needs to be accessible by the startup file, so you can access your provider and manage them. + +[!code-csharp[Building the Collection](samples/collection.cs)] + +As shown above, an instance of `DiscordSocketConfig` is created, and added **before** the client itself is. +Because the collection will prefer to create the highest populated constructor available with the services already present, +it will prefer the constructor with the configuration, because you already added it. + +## Using your dependencies + +After building your provider in the Program class constructor, the provider is now available inside the instance you're actively using. +Through the provider, we can ask for the DiscordSocketClient we registered earlier. + +[!code-csharp[Applying DI in RunAsync](samples/runasync.cs)] + +> [!WARNING] +> Service constructors are not activated until the service is **first requested**. +> An 'endpoint' service will have to be requested from the provider before it is activated. +> If a service is requested with dependencies, its dependencies (if not already active) will be activated before the service itself is. + +## Injecting dependencies + +You can not only directly access the provider from a field or property, but you can also pass around instances to classes registered in the provider. +There are multiple ways to do this. Please refer to the +[Injection Documentation](Guides.DI.Injection) for further information. diff --git a/docs/guides/dependency_injection/images/manager.png b/docs/guides/dependency_injection/images/manager.png new file mode 100644 index 0000000000000000000000000000000000000000..91791f7a004b1f355cb7af8d274f10044f8bf3fe GIT binary patch literal 12198 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 [!NOTE] +> As mentioned above, the dependency *and* the target class have to be registered in order for the serviceprovider to resolve it. + +## Injecting through a constructor + +Services can be injected from the constructor of the class. +This is the preferred approach, because it automatically locks the readonly field in place with the provided service and isn't accessible outside of the class. + +[!code-csharp[Property Injection(samples/property-injecting.cs)]] + +## Injecting through properties + +Injecting through properties is also allowed as follows. + +[!code-csharp[Property Injection](samples/property-injecting.cs)] + +> [!WARNING] +> Dependency Injection will not resolve missing services in property injection, and it will not pick a constructor instead. +> If a publically accessible property is attempted to be injected and its service is missing, the application will throw an error. + +## Using the provider itself + +You can also access the provider reference itself from injecting it into a class. There are multiple use cases for this: + +- Allowing libraries (Like Discord.Net) to access your provider internally. +- Injecting optional dependencies. +- Calling methods on the provider itself if necessary, this is often done for creating scopes. + +[!code-csharp[Provider Injection](samples/provider.cs)] + +> [!NOTE] +> It is important to keep in mind that the provider will pick the 'biggest' available constructor. +> If you choose to introduce multiple constructors, +> keep in mind that services missing from one constructor may have the provider pick another one that *is* available instead of throwing an exception. diff --git a/docs/guides/dependency_injection/samples/access-activator.cs b/docs/guides/dependency_injection/samples/access-activator.cs new file mode 100644 index 000000000..29e71e894 --- /dev/null +++ b/docs/guides/dependency_injection/samples/access-activator.cs @@ -0,0 +1,9 @@ +async Task RunAsync() +{ + //... + + await _serviceProvider.GetRequiredService() + .ActivateAsync(); + + //... +} diff --git a/docs/guides/dependency_injection/samples/collection.cs b/docs/guides/dependency_injection/samples/collection.cs new file mode 100644 index 000000000..4d0457dc9 --- /dev/null +++ b/docs/guides/dependency_injection/samples/collection.cs @@ -0,0 +1,13 @@ +static IServiceProvider CreateServices() +{ + var config = new DiscordSocketConfig() + { + //... + }; + + var collection = new ServiceCollection() + .AddSingleton(config) + .AddSingleton(); + + return collection.BuildServiceProvider(); +} diff --git a/docs/guides/dependency_injection/samples/ctor-injecting.cs b/docs/guides/dependency_injection/samples/ctor-injecting.cs new file mode 100644 index 000000000..c412bd29c --- /dev/null +++ b/docs/guides/dependency_injection/samples/ctor-injecting.cs @@ -0,0 +1,14 @@ +public class ClientHandler +{ + private readonly DiscordSocketClient _client; + + public ClientHandler(DiscordSocketClient client) + { + _client = client; + } + + public async Task ConfigureAsync() + { + //... + } +} diff --git a/docs/guides/dependency_injection/samples/enumeration.cs b/docs/guides/dependency_injection/samples/enumeration.cs new file mode 100644 index 000000000..cc8c617f3 --- /dev/null +++ b/docs/guides/dependency_injection/samples/enumeration.cs @@ -0,0 +1,18 @@ +public class ServiceActivator +{ + // This contains *all* registered services of serviceType IService + private readonly IEnumerable _services; + + public ServiceActivator(IEnumerable services) + { + _services = services; + } + + public async Task ActivateAsync() + { + foreach(var service in _services) + { + await service.StartAsync(); + } + } +} diff --git a/docs/guides/dependency_injection/samples/implicit-registration.cs b/docs/guides/dependency_injection/samples/implicit-registration.cs new file mode 100644 index 000000000..52f84228b --- /dev/null +++ b/docs/guides/dependency_injection/samples/implicit-registration.cs @@ -0,0 +1,12 @@ +public static ServiceCollection RegisterImplicitServices(this ServiceCollection collection, Type interfaceType, Type activatorType) +{ + // Get all types in the executing assembly. There are many ways to do this, but this is fastest. + foreach (var type in typeof(Program).Assembly.GetTypes()) + { + if (interfaceType.IsAssignableFrom(type) && !type.IsAbstract) + collection.AddSingleton(interfaceType, type); + } + + // Register the activator so you can activate the instances. + collection.AddSingleton(activatorType); +} diff --git a/docs/guides/dependency_injection/samples/modules.cs b/docs/guides/dependency_injection/samples/modules.cs new file mode 100644 index 000000000..2fadc13d4 --- /dev/null +++ b/docs/guides/dependency_injection/samples/modules.cs @@ -0,0 +1,16 @@ +public class MyModule : InteractionModuleBase +{ + private readonly MyService _service; + + public MyModule(MyService service) + { + _service = service; + } + + [SlashCommand("things", "Shows things")] + public async Task ThingsAsync() + { + var str = string.Join("\n", _service.Things) + await RespondAsync(str); + } +} diff --git a/docs/guides/dependency_injection/samples/program.cs b/docs/guides/dependency_injection/samples/program.cs new file mode 100644 index 000000000..6d985319a --- /dev/null +++ b/docs/guides/dependency_injection/samples/program.cs @@ -0,0 +1,24 @@ +public class Program +{ + private readonly IServiceProvider _serviceProvider; + + public Program() + { + _serviceProvider = CreateProvider(); + } + + static void Main(string[] args) + => new Program().RunAsync(args).GetAwaiter().GetResult(); + + static IServiceProvider CreateProvider() + { + var collection = new ServiceCollection(); + //... + return collection.BuildServiceProvider(); + } + + async Task RunAsync(string[] args) + { + //... + } +} diff --git a/docs/guides/dependency_injection/samples/property-injecting.cs b/docs/guides/dependency_injection/samples/property-injecting.cs new file mode 100644 index 000000000..c0c50e150 --- /dev/null +++ b/docs/guides/dependency_injection/samples/property-injecting.cs @@ -0,0 +1,9 @@ +public class ClientHandler +{ + public DiscordSocketClient Client { get; set; } + + public async Task ConfigureAsync() + { + //... + } +} diff --git a/docs/guides/dependency_injection/samples/provider.cs b/docs/guides/dependency_injection/samples/provider.cs new file mode 100644 index 000000000..26b600b9d --- /dev/null +++ b/docs/guides/dependency_injection/samples/provider.cs @@ -0,0 +1,26 @@ +public class UtilizingProvider +{ + private readonly IServiceProvider _provider; + private readonly AnyService _service; + + // This service is allowed to be null because it is only populated if the service is actually available in the provider. + private readonly AnyOtherService? _otherService; + + // This constructor injects only the service provider, + // and uses it to populate the other dependencies. + public UtilizingProvider(IServiceProvider provider) + { + _provider = provider; + _service = provider.GetRequiredService(); + _otherService = provider.GetService(); + } + + // This constructor injects the service provider, and AnyService, + // making sure that AnyService is not null without having to call GetRequiredService + public UtilizingProvider(IServiceProvider provider, AnyService service) + { + _provider = provider; + _service = service; + _otherService = provider.GetService(); + } +} diff --git a/docs/guides/dependency_injection/samples/runasync.cs b/docs/guides/dependency_injection/samples/runasync.cs new file mode 100644 index 000000000..d24efc83e --- /dev/null +++ b/docs/guides/dependency_injection/samples/runasync.cs @@ -0,0 +1,17 @@ +async Task RunAsync(string[] args) +{ + // Request the instance from the client. + // Because we're requesting it here first, its targetted constructor will be called and we will receive an active instance. + var client = _services.GetRequiredService(); + + client.Log += async (msg) => + { + await Task.CompletedTask; + Console.WriteLine(msg); + } + + await client.LoginAsync(TokenType.Bot, ""); + await client.StartAsync(); + + await Task.Delay(Timeout.Infinite); +} diff --git a/docs/guides/dependency_injection/samples/scoped.cs b/docs/guides/dependency_injection/samples/scoped.cs new file mode 100644 index 000000000..9942f8d8e --- /dev/null +++ b/docs/guides/dependency_injection/samples/scoped.cs @@ -0,0 +1,6 @@ + +// With serviceType: +collection.AddScoped(); + +// Without serviceType: +collection.AddScoped(); diff --git a/docs/guides/dependency_injection/samples/service-registration.cs b/docs/guides/dependency_injection/samples/service-registration.cs new file mode 100644 index 000000000..f6e4d22dd --- /dev/null +++ b/docs/guides/dependency_injection/samples/service-registration.cs @@ -0,0 +1,21 @@ +static IServiceProvider CreateServices() +{ + var config = new DiscordSocketConfig() + { + //... + }; + + // X represents either Interaction or Command, as it functions the exact same for both types. + var servConfig = new XServiceConfig() + { + //... + } + + var collection = new ServiceCollection() + .AddSingleton(config) + .AddSingleton() + .AddSingleton(servConfig) + .AddSingleton(); + + return collection.BuildServiceProvider(); +} diff --git a/docs/guides/dependency_injection/samples/services.cs b/docs/guides/dependency_injection/samples/services.cs new file mode 100644 index 000000000..2e5235b69 --- /dev/null +++ b/docs/guides/dependency_injection/samples/services.cs @@ -0,0 +1,9 @@ +public class MyService +{ + public List Things { get; } + + public MyService() + { + Things = new(); + } +} diff --git a/docs/guides/dependency_injection/samples/singleton.cs b/docs/guides/dependency_injection/samples/singleton.cs new file mode 100644 index 000000000..f395d743e --- /dev/null +++ b/docs/guides/dependency_injection/samples/singleton.cs @@ -0,0 +1,6 @@ + +// With serviceType: +collection.AddSingleton(); + +// Without serviceType: +collection.AddSingleton(); diff --git a/docs/guides/dependency_injection/samples/transient.cs b/docs/guides/dependency_injection/samples/transient.cs new file mode 100644 index 000000000..ae1e1a5d8 --- /dev/null +++ b/docs/guides/dependency_injection/samples/transient.cs @@ -0,0 +1,6 @@ + +// With serviceType: +collection.AddTransient(); + +// Without serviceType: +collection.AddTransient(); diff --git a/docs/guides/dependency_injection/scaling.md b/docs/guides/dependency_injection/scaling.md new file mode 100644 index 000000000..356fb7c72 --- /dev/null +++ b/docs/guides/dependency_injection/scaling.md @@ -0,0 +1,39 @@ +--- +uid: Guides.DI.Scaling +title: Scaling your DI +--- + +# Scaling your DI + +Dependency injection has a lot of use cases, and is very suitable for scaled applications. +There are a few ways to make registering & using services easier in large amounts. + +## Using a range of services. + +If you have a lot of services that all have the same use such as handling an event or serving a module, +you can register and inject them all at once by some requirements: + +- All classes need to inherit a single interface or abstract type. +- While not required, it is preferred if the interface and types share a method to call on request. +- You need to register a class that all the types can be injected into. + +### Registering implicitly + +Registering all the types is done through getting all types in the assembly and checking if they inherit the target interface. + +[!code-csharp[Registering](samples/implicit-registration.cs)] + +> [!NOTE] +> As seen above, the interfaceType and activatorType are undefined. For our usecase below, these are `IService` and `ServiceActivator` in order. + +### Using implicit dependencies + +In order to use the implicit dependencies, you have to get access to the activator you registered earlier. + +[!code-csharp[Accessing the activator](samples/access-activator.cs)] + +When the activator is accessed and the `ActivateAsync()` method is called, the following code will be executed: + +[!code-csharp[Executing the activator](samples/enumeration.cs)] + +As a result of this, all the services that were registered with `IService` as its implementation type will execute their starting code, and start up. diff --git a/docs/guides/dependency_injection/services.md b/docs/guides/dependency_injection/services.md new file mode 100644 index 000000000..e021a88be --- /dev/null +++ b/docs/guides/dependency_injection/services.md @@ -0,0 +1,48 @@ +--- +uid: Guides.DI.Services +title: Using DI in Interaction & Command Frameworks +--- + +# DI in the Interaction- & Command Service + +For both the Interaction- and Command Service modules, DI is quite straight-forward to use. + +You can inject any service into modules without the modules having to be registered to the provider. +Discord.Net resolves your dependencies internally. + +> [!WARNING] +> The way DI is used in the Interaction- & Command Service are nearly identical, except for one detail: +> [Resolving Module Dependencies](xref:Guides.IntFw.Intro#resolving-module-dependencies) + +## Registering the Service + +Thanks to earlier described behavior of allowing already registered members as parameters of the available ctors, +The socket client & configuration will automatically be acknowledged and the XService(client, config) overload will be used. + +[!code-csharp[Service Registration](samples/service-registration.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. + +[!code-csharp[Module Injection](samples/modules.cs)] + +If you accept `Command/InteractionService` or `IServiceProvider` as a parameter in your constructor or as an injectable property, +these entries will be filled by the `Command/InteractionService` that the module is loaded from and the `IServiceProvider` that is passed into it respectively. + +> [!NOTE] +> Annotating a property with a [DontInjectAttribute] attribute will +> prevent the property from being injected. + +## Services + +Because modules are transient of nature and will reinstantiate on every request, +it is suggested to create a singleton service behind it to hold values across multiple command executions. + +[!code-csharp[Services](samples/services.cs)] + + diff --git a/docs/guides/dependency_injection/types.md b/docs/guides/dependency_injection/types.md new file mode 100644 index 000000000..e539d0695 --- /dev/null +++ b/docs/guides/dependency_injection/types.md @@ -0,0 +1,52 @@ +--- +uid: Guides.DI.Dependencies +title: Types of Dependencies +--- + +# Dependency Types + +There are 3 types of dependencies to learn to use. Several different usecases apply for each. + +> [!WARNING] +> When registering types with a serviceType & implementationType, +> only the serviceType will be available for injection, and the implementationType will be used for the underlying instance. + +## Singleton + +A singleton service creates a single instance when first requested, and maintains that instance across the lifetime of the application. +Any values that are changed within a singleton will be changed across all instances that depend on it, as they all have the same reference to it. + +### Registration: + +[!code-csharp[Singleton Example](samples/singleton.cs)] + +> [!NOTE] +> Types like the Discord client and Interaction/Command services are intended to be singleton, +> as they should last across the entire app and share their state with all references to the object. + +## Scoped + +A scoped service creates a new instance every time a new service is requested, but is kept across the 'scope'. +As long as the service is in view for the created scope, the same instance is used for all references to the type. +This means that you can reuse the same instance during execution, and keep the services' state for as long as the request is active. + +### Registration: + +[!code-csharp[Scoped Example](samples/scoped.cs)] + +> [!NOTE] +> Without using HTTP or libraries like EFCORE, scopes are often unused in Discord bots. +> They are most commonly used for handling HTTP and database requests. + +## Transient + +A transient service is created every time it is requested, and does not share its state between references within the target service. +It is intended for lightweight types that require little state, to be disposed quickly after execution. + +### Registration: + +[!code-csharp[Transient Example](samples/transient.cs)] + +> [!NOTE] +> Discord.Net modules behave exactly as transient types, and are intended to only last as long as the command execution takes. +> This is why it is suggested for apps to use singleton services to keep track of cross-execution data. diff --git a/docs/guides/int_framework/dependency-injection.md b/docs/guides/int_framework/dependency-injection.md deleted file mode 100644 index 31d001f4b..000000000 --- a/docs/guides/int_framework/dependency-injection.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -uid: Guides.IntFw.DI -title: Dependency Injection ---- - -# Dependency Injection - -Dependency injection in the Interaction Service is mostly based on that of the Text-based command service, -for which further information is found [here](xref:Guides.TextCommands.DI). - -> [!NOTE] -> The 2 are nearly identical, except for one detail: -> [Resolving Module Dependencies](xref:Guides.IntFw.Intro#resolving-module-dependencies) diff --git a/docs/guides/int_framework/intro.md b/docs/guides/int_framework/intro.md index b51aa8088..5cf38bff1 100644 --- a/docs/guides/int_framework/intro.md +++ b/docs/guides/int_framework/intro.md @@ -374,8 +374,7 @@ delegate can be used to create HTTP responses from a deserialized json object st - Use the interaction endpoints of the module base instead of the interaction object (ie. `RespondAsync()`, `FollowupAsync()`...). [AutocompleteHandlers]: xref:Guides.IntFw.AutoCompletion -[DependencyInjection]: xref:Guides.TextCommands.DI -[Post Execution Docuemntation]: xref:Guides.IntFw.PostExecution +[DependencyInjection]: xref:Guides.DI.Intro [GroupAttribute]: xref:Discord.Interactions.GroupAttribute [InteractionService]: xref:Discord.Interactions.InteractionService diff --git a/docs/guides/text_commands/dependency-injection.md b/docs/guides/text_commands/dependency-injection.md deleted file mode 100644 index 3253643ef..000000000 --- a/docs/guides/text_commands/dependency-injection.md +++ /dev/null @@ -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 diff --git a/docs/guides/text_commands/intro.md b/docs/guides/text_commands/intro.md index 6632c127a..1113b0821 100644 --- a/docs/guides/text_commands/intro.md +++ b/docs/guides/text_commands/intro.md @@ -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. diff --git a/docs/guides/text_commands/samples/dependency-injection/dependency_map_setup.cs b/docs/guides/text_commands/samples/dependency-injection/dependency_map_setup.cs deleted file mode 100644 index 16ca479db..000000000 --- a/docs/guides/text_commands/samples/dependency-injection/dependency_map_setup.cs +++ /dev/null @@ -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() - .AddSingleton() - .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); - // ... - } -} diff --git a/docs/guides/text_commands/samples/dependency-injection/dependency_module.cs b/docs/guides/text_commands/samples/dependency-injection/dependency_module.cs deleted file mode 100644 index 3e42074ca..000000000 --- a/docs/guides/text_commands/samples/dependency-injection/dependency_module.cs +++ /dev/null @@ -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 -{ - 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 -{ - public DatabaseService DbService { get; set; } - - [Command("read")] - public async Task ReadFromDbAsync() - { - await ReplyAsync(DbService.GetData()); - } -} diff --git a/docs/guides/text_commands/samples/dependency-injection/dependency_module_noinject.cs b/docs/guides/text_commands/samples/dependency-injection/dependency_module_noinject.cs deleted file mode 100644 index 48cd52308..000000000 --- a/docs/guides/text_commands/samples/dependency-injection/dependency_module_noinject.cs +++ /dev/null @@ -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 -{ - public ImageService ImageService { get; } - public ImageModule() - { - ImageService = new ImageService(); - } -} - -// Applying DontInjectAttribute -public class ImageModule : ModuleBase -{ - [DontInject] - public ImageService ImageService { get; set; } - public ImageModule() - { - ImageService = new ImageService(); - } -}