From 33ac43a4b8da897765eadda4ffd12d1c38862b61 Mon Sep 17 00:00:00 2001 From: Kevin Gysberg Date: Thu, 13 Sep 2018 21:52:47 -0500 Subject: [PATCH] Changed IServiceProvider in CommandService.ExecuteAsync to a scoped provider. Added a command context accessor, and setting the value before beginning the execute. This will allow injected services in modules to access the current command context. This mirrors the behavior of Asp.Net Core's HttpContextAccessor. --- src/Discord.Net.Commands/CommandService.cs | 20 +++++++++++++++++++ .../Commands/CommandContextAccessor.cs | 16 +++++++++++++++ .../Commands/ICommandContextAccessor.cs | 7 +++++++ 3 files changed, 43 insertions(+) create mode 100644 src/Discord.Net.Core/Commands/CommandContextAccessor.cs create mode 100644 src/Discord.Net.Core/Commands/ICommandContextAccessor.cs diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 7b7cffda2..5a092095f 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -1,3 +1,6 @@ +using Discord.Commands.Builders; +using Discord.Logging; +using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -350,8 +353,25 @@ namespace Discord.Commands => ExecuteAsync(context, context.Message.Content.Substring(argPos), services, multiMatchHandling); public async Task ExecuteAsync(ICommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) { + // Create service scope and command context so dependent services can consume the command context, with a scoped lifespan services = services ?? EmptyServiceProvider.Instance; + using (var serviceScope = services.CreateScope()) + { + var scopedServices = serviceScope.ServiceProvider; + // If the context accessor is registered in the IoC container, set it + var contextAccessor = scopedServices.GetService(); + if (contextAccessor != null) + { + contextAccessor.CommandContext = context; + } + return await ExecuteAsyncInternal(context, input, scopedServices, multiMatchHandling); + } + } + + internal async Task ExecuteAsyncInternal(ICommandContext context, string input, + IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) + { var searchResult = Search(input); if (!searchResult.IsSuccess) return searchResult; diff --git a/src/Discord.Net.Core/Commands/CommandContextAccessor.cs b/src/Discord.Net.Core/Commands/CommandContextAccessor.cs new file mode 100644 index 000000000..1effb3707 --- /dev/null +++ b/src/Discord.Net.Core/Commands/CommandContextAccessor.cs @@ -0,0 +1,16 @@ +using System.Threading; + +namespace Discord.Commands +{ + public class CommandContextAccessor : ICommandContextAccessor + { + private static AsyncLocal _commandContextCurrent = new AsyncLocal(); + + public ICommandContext CommandContext + { + get => _commandContextCurrent.Value; + set => _commandContextCurrent.Value = value; + } + + } +} diff --git a/src/Discord.Net.Core/Commands/ICommandContextAccessor.cs b/src/Discord.Net.Core/Commands/ICommandContextAccessor.cs new file mode 100644 index 000000000..8a9e641fa --- /dev/null +++ b/src/Discord.Net.Core/Commands/ICommandContextAccessor.cs @@ -0,0 +1,7 @@ +namespace Discord.Commands +{ + public interface ICommandContextAccessor + { + ICommandContext CommandContext { get; set; } + } +}