首页 > 解决方案 > 使用 .AddDbContext() 和依赖注入时,Discord.NET 机器人在所有模块之间共享相同的 DatabaseContext

问题描述

我正在创建一个 Discord Bot,它应该在行DatabaseContext会成员发出命令时创建和处理一个。DatabaseContext问题是模块之间共享相同的内容。我已经在使用.AddDbContext()od.AddDbContextPool()所以我不理解这种行为。

我正在使用 Discord.NET、EFCore 和依赖注入。

这是我的 Program.cs 文件:

class Program
{
    private const string _token = "token";
    private readonly DiscordSocketClient _client;
    private readonly CommandService _commands;
    private readonly IServiceProvider _services;

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

    private Program()
    {
        _client = new DiscordSocketClient();
        _commands = new CommandService();
        _client.Log += Log;
        _commands.Log += Log;
        _services = ConfigureServices();
    }

    private IServiceProvider ConfigureServices()
    {
        var map = new ServiceCollection()
            .AddSingleton(_client)
            .AddSingleton(_commands)
            .AddDbContext<DatabaseContext>(options => 
                options.UseMySql(SharedConfiguration.connectionString,
                new MySqlServerVersion(new Version(5, 7))));

        return map.BuildServiceProvider();
    }

    private Task Log(LogMessage msg)
    {
        Console.WriteLine($"[{DateTime.UtcNow} - {msg.Severity}] {msg.ToString()}");
        return Task.CompletedTask;
    }

    private async Task MainAsync()
    {
        await InitCommands();

        await _client.LoginAsync(TokenType.Bot, _token);
        await _client.StartAsync();

        await Task.Delay(Timeout.Infinite);
    }

    private async Task InitCommands()
    {
        await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);

        _client.MessageReceived += HandleCommandAsync;
    }

    private async Task HandleCommandAsync(SocketMessage messageParam)
    {
        var message = messageParam as SocketUserMessage;
        if (message == null) return;

        int argPos = 0;

        if (!(message.HasStringPrefix("b.", ref argPos) ||
            message.HasMentionPrefix(_client.CurrentUser, ref argPos)) ||
            message.Author.IsBot)
            return;

        var context = new SocketCommandContext(_client, message);

        await _commands.ExecuteAsync(context, argPos, _services);
    }
}

这是一个模块的例子:

public class UserModule : ModuleBase<SocketCommandContext>
{
    private readonly DiscordSocketClient _client;
    private readonly CommandService _commands;
    private readonly DatabaseContext _context;

    private UserModule(DiscordSocketClient client, CommandService commands, DatabaseContext context)
    {
        _client = client;
        _commands = commands;
        _context = context;
    }

    [Command("user")]
    public async Task Stats(IUser dUser = null)
    {
        Console.WriteLine(_context.ContextId);
    }
}

但是,当我运行b.user两次时,情况ContextId是一样的。这导致我的应用程序出现严重的缓存问题,因为数据库在这个不和谐机器人和另一个应用程序之间共享。

我该怎么做才能DatabaseContext在每次发出命令时创建一个?

标签: c#dependency-injectionentity-framework-corediscord.net

解决方案


这是因为您的机器人是单个实例,因此注入的服务仅被解析一次。

而不是注入DbContext注入 a IServiceScopeFactory,并为每个请求创建一个单独的范围:

public class UserModule : ModuleBase<SocketCommandContext>
{
    private readonly DiscordSocketClient _client;
    private readonly CommandService _commands;
    private readonly IServiceScopeFactory _scopeFactory;

    private UserModule(DiscordSocketClient client, CommandService commands, IServiceScopeFactory scopeFactory)
    {
        _client = client;
        _commands = commands;
        _scopeFactory = scopeFactory;
    }

    [Command("user")]
    public async Task Stats(IUser dUser = null)
    {
        using(var scope = _scopeFactory.CreateScope())
        {   
            var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();           
            Console.WriteLine(dbContext.ContextId);
        }
    }
}

推荐阅读