| @@ -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. | |||||
| @@ -0,0 +1,44 @@ | |||||
| --- | |||||
| uid: Guides.DI.Injection | |||||
| title: Injection | |||||
| --- | |||||
| # Injecting instances within the provider | |||||
| You can inject registered services into any class that is registered to the `IServiceProvider`. | |||||
| This can be done through property or constructor. | |||||
| > [!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. | |||||
| @@ -0,0 +1,9 @@ | |||||
| async Task RunAsync() | |||||
| { | |||||
| //... | |||||
| await _serviceProvider.GetRequiredService<ServiceActivator>() | |||||
| .ActivateAsync(); | |||||
| //... | |||||
| } | |||||
| @@ -0,0 +1,13 @@ | |||||
| static IServiceProvider CreateServices() | |||||
| { | |||||
| var config = new DiscordSocketConfig() | |||||
| { | |||||
| //... | |||||
| }; | |||||
| var collection = new ServiceCollection() | |||||
| .AddSingleton(config) | |||||
| .AddSingleton<DiscordSocketClient>(); | |||||
| return collection.BuildServiceProvider(); | |||||
| } | |||||
| @@ -0,0 +1,14 @@ | |||||
| public class ClientHandler | |||||
| { | |||||
| private readonly DiscordSocketClient _client; | |||||
| public ClientHandler(DiscordSocketClient client) | |||||
| { | |||||
| _client = client; | |||||
| } | |||||
| public async Task ConfigureAsync() | |||||
| { | |||||
| //... | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| public class ServiceActivator | |||||
| { | |||||
| // This contains *all* registered services of serviceType IService | |||||
| private readonly IEnumerable<IService> _services; | |||||
| public ServiceActivator(IEnumerable<IService> services) | |||||
| { | |||||
| _services = services; | |||||
| } | |||||
| public async Task ActivateAsync() | |||||
| { | |||||
| foreach(var service in _services) | |||||
| { | |||||
| await service.StartAsync(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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); | |||||
| } | |||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| @@ -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) | |||||
| { | |||||
| //... | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| public class ClientHandler | |||||
| { | |||||
| public DiscordSocketClient Client { get; set; } | |||||
| public async Task ConfigureAsync() | |||||
| { | |||||
| //... | |||||
| } | |||||
| } | |||||
| @@ -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<AnyService>(); | |||||
| _otherService = provider.GetService<AnyOtherService>(); | |||||
| } | |||||
| // 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<AnyOtherService>(); | |||||
| } | |||||
| } | |||||
| @@ -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<DiscordSocketClient>(); | |||||
| client.Log += async (msg) => | |||||
| { | |||||
| await Task.CompletedTask; | |||||
| Console.WriteLine(msg); | |||||
| } | |||||
| await client.LoginAsync(TokenType.Bot, ""); | |||||
| await client.StartAsync(); | |||||
| await Task.Delay(Timeout.Infinite); | |||||
| } | |||||
| @@ -0,0 +1,6 @@ | |||||
| // With serviceType: | |||||
| collection.AddScoped<IScopedService, ScopedService>(); | |||||
| // Without serviceType: | |||||
| collection.AddScoped<ScopedService>(); | |||||
| @@ -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<DiscordSocketClient>() | |||||
| .AddSingleton(servConfig) | |||||
| .AddSingleton<XService>(); | |||||
| return collection.BuildServiceProvider(); | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| public class MyService | |||||
| { | |||||
| public List<string> Things { get; } | |||||
| public MyService() | |||||
| { | |||||
| Things = new(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,6 @@ | |||||
| // With serviceType: | |||||
| collection.AddSingleton<ISingletonService, SingletonService>(); | |||||
| // Without serviceType: | |||||
| collection.AddSingleton<SingletonService>(); | |||||
| @@ -0,0 +1,6 @@ | |||||
| // With serviceType: | |||||
| collection.AddTransient<ITransientService, TransientService>(); | |||||
| // Without serviceType: | |||||
| collection.AddTransient<TransientService>(); | |||||
| @@ -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. | |||||
| @@ -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)] | |||||
| @@ -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. | |||||
| @@ -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) | |||||
| @@ -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()`...). | - Use the interaction endpoints of the module base instead of the interaction object (ie. `RespondAsync()`, `FollowupAsync()`...). | ||||
| [AutocompleteHandlers]: xref:Guides.IntFw.AutoCompletion | [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 | [GroupAttribute]: xref:Discord.Interactions.GroupAttribute | ||||
| [InteractionService]: xref:Discord.Interactions.InteractionService | [InteractionService]: xref:Discord.Interactions.InteractionService | ||||
| @@ -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 | |||||
| @@ -187,7 +187,7 @@ service provider. | |||||
| ### Module Constructors | ### 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 | that are placed in the Module's constructor must be injected into an | ||||
| @System.IServiceProvider first. | @System.IServiceProvider first. | ||||
| @@ -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); | |||||
| // ... | |||||
| } | |||||
| } | |||||
| @@ -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()); | |||||
| } | |||||
| } | |||||
| @@ -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(); | |||||
| } | |||||
| } | |||||