Discord.Net 依赖注入 - 运行命令时未找到依赖项

Posted

技术标签:

【中文标题】Discord.Net 依赖注入 - 运行命令时未找到依赖项【英文标题】:Discord.Net dependency injection - dependency was not found when running command 【发布时间】:2020-05-01 16:53:35 【问题描述】:

我正在尝试通过模块构造函数进行依赖注入,它似乎可以工作,因为我可以成功地使用构造函数内部的类,但是每当我运行命令时它就会中断。这是我尝试运行命令时得到的结果:

---> System.InvalidOperationException: Failed to create "AnimeCalendarBot.Modules.InfoModule", dependency "TestService" was not found.
   at Discord.Commands.ReflectionUtils.GetMember(CommandService commands, IServiceProvider services, Type memberType, TypeInfo ownerType)
   at Discord.Commands.ReflectionUtils.<>c__DisplayClass2_0`1.<CreateBuilder>b__0(IServiceProvider services)
   at Discord.Commands.ModuleClassBuilder.<>c__DisplayClass6_0 <<BuildCommand>g__ExecuteCallback|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Discord.Commands.CommandInfo.ExecuteInternalAsync(ICommandContext context, Object[] args, IServiceProvider services)
   --- End of inner exception stack trace ---

但是在运行命令之前,一切都很好。

程序:

public class Program

    public static void Main(string[] args)
        => new Program().MainAsync().GetAwaiter().GetResult();

    private readonly DiscordSocketClient _client;
    private readonly CommandService _commands;
    private readonly CommandHandler _commandHandler;
    private readonly IServiceProvider _services;

    private Program()
    
        _client = new DiscordSocketClient(new DiscordSocketConfig
        
            LogLevel = LogSeverity.Info,
        );

        _commands = new CommandService(new CommandServiceConfig
        
            LogLevel = LogSeverity.Info,
            CaseSensitiveCommands = false,
        );

        _services = ConfigureServices();

        _commandHandler = new CommandHandler(_client, _commands, _services);

        _client.Log += Log;
        _commands.Log += Log;
    

    private static IServiceProvider ConfigureServices()
    
        var map = new ServiceCollection()
            .AddSingleton(new TestService());

        return map.BuildServiceProvider();
    

    private static Task Log(LogMessage message)
    
        switch (message.Severity)
        
            case LogSeverity.Critical:
            case LogSeverity.Error:
                Console.ForegroundColor = ConsoleColor.Red;
                break;
            case LogSeverity.Warning:
                Console.ForegroundColor = ConsoleColor.Yellow;
                break;
            case LogSeverity.Info:
                Console.ForegroundColor = ConsoleColor.White;
                break;
            case LogSeverity.Verbose:
            case LogSeverity.Debug:
                Console.ForegroundColor = ConsoleColor.DarkGray;
                break;
        
        Console.WriteLine($"DateTime.Now,-19 [message.Severity,8] message.Source: message.Message message.Exception");
        Console.ResetColor();

        return Task.CompletedTask;
    

    public async Task MainAsync()
    
        await _commandHandler.InstallCommandsAsync();

        _client.Log += Log;

        await _client.LoginAsync(TokenType.Bot,
            Environment.GetEnvironmentVariable("AnimeCalendarBotToken"));
        await _client.StartAsync();

        // Block this task until the program is closed.
        await Task.Delay(-1);
    

CommandHandler:

public class CommandHandler

    private readonly DiscordSocketClient _client;
    private readonly CommandService _commands;
    private readonly IServiceProvider _services;

    public CommandHandler(DiscordSocketClient client, CommandService commands, IServiceProvider services)
    
        _client = client;
        _commands = commands;
        _services = services;
    

    public async Task InstallCommandsAsync()
    
        _client.MessageReceived += HandleCommandAsync;

        await _commands.AddModulesAsync(assembly: Assembly.GetEntryAssembly(),
                                        services: _services);
    

    private async Task HandleCommandAsync(SocketMessage messageParam)
    
        // Don't process the command if it was a system message
        var message = messageParam as SocketUserMessage;
        if (message == null) return;

        // Create a number to track where the prefix ends and the command begins
        int argPos = 0;

        // Determine if the message is a command based on the prefix and make sure no bots trigger commands
        if (!(message.HasCharPrefix('!', ref argPos) ||
            message.HasMentionPrefix(_client.CurrentUser, ref argPos)) ||
            message.Author.IsBot)
            return;

        // Create a WebSocket-based command context based on the message
        var context = new SocketCommandContext(_client, message);

        // Execute the command with the command context we just
        // created, along with the service provider for precondition checks.

        // Keep in mind that result does not indicate a return value
        // rather an object stating if the command executed successfully.
        var result = await _commands.ExecuteAsync(
            context: context,
            argPos: argPos,
            services: null);
    

信息模块:

public class InfoModule : ModuleBase<SocketCommandContext>

    private readonly TestService _testService;

    public InfoModule(TestService testService)
    
        _testService = testService;
        _testService.Start(); // this works
    

    [Command("startTest")]
    [Summary("Starts test service.")]
    public Task StartTestService()
    
        _testService.Start();
        return Task.CompletedTask;
    

    [Command("stopTest")]
    [Summary("Stops test service.")]
    public Task StopTestService()
    
        _testService.Stop();
        return Task.CompletedTask;
    

测试服务:

public class TestService

    private readonly Timer _timer;

    public TestService()
    
        _timer = new Timer(1000)  AutoReset = true ;
        _timer.Elapsed += TimerElapsed;
    

    private void TimerElapsed(object sender, ElapsedEventArgs e)
    
        String currentTime = DateTime.Now.ToString();
        Console.WriteLine(currentTime);
    

    public void Start()
    
        _timer.Start();
    

    public void Stop()
    
        _timer.Stop();
    

【问题讨论】:

您没有将服务提供者传递给 ExecuteAsync 【参考方案1】:

我之前遇到过同样的问题。问题是CommandHandler没有注入服务。

首先,我会像@EnderIce2 的回答那样分配您的服务。

var map = new ServiceCollection()
        .AddSingleton<TestService>()

之后你需要转到CommandHandler 并更改结尾InstallCommandsAsync 方法:

await _commands.AddModulesAsync(assembly: Assembly.GetEntryAssembly(),
                                            services: null);

await _commands.AddModulesAsync(assembly: Assembly.GetEntryAssembly(),
                                            services: _services);

HandleCommandAsync 也是如此;最后更改:

await _commands.ExecuteAsync(
            context: context,
            argPos: argPos,
            services: null);

收件人:

await _commands.ExecuteAsync(
                context: context,
                argPos: argPos,
                services: _services);

【讨论】:

【参考方案2】:

在 ConfigureServices 的 Program 上试试这个:

private static IServiceProvider ConfigureServices()

    var map = new ServiceCollection()
        .AddSingleton<TestService>()

    return map.BuildServiceProvider();

【讨论】:

以上是关于Discord.Net 依赖注入 - 运行命令时未找到依赖项的主要内容,如果未能解决你的问题,请参考以下文章

使用依赖注入时未注册持久的 Azure 函数绑定类型

如何在单独的文件中实现事件处理程序并使用依赖注入附加它?

Discord.Net SendMessageAsync 没有命令

Discord.NET - 获取命令中抛出了哪种类型的异常

如何让我的命令在 Discord.Net 中工作

Discord.net 无法在 Linux 上运行