Discord.NET 机器人在使用 .AddDbContext() 和依赖注入时在所有模块之间共享相同的 DatabaseContext
Discord.NET bot sharing the same DatabaseContext between all modules when using .AddDbContext() and Dependency Injection
我正在创建一个 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
是一样的。
这会导致我的应用程序出现严重的缓存问题,因为该 discord 机器人和另一个应用程序共享数据库。
我该怎么做才能在每次发出命令时创建一个 DatabaseContext
?
这是因为你的机器人是一个单一的实例,因此注入的服务只会解析一次。
而不是注入 DbContext
注入 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);
}
}
}
我正在创建一个 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
是一样的。
这会导致我的应用程序出现严重的缓存问题,因为该 discord 机器人和另一个应用程序共享数据库。
我该怎么做才能在每次发出命令时创建一个 DatabaseContext
?
这是因为你的机器人是一个单一的实例,因此注入的服务只会解析一次。
而不是注入 DbContext
注入 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);
}
}
}