无法通过 MediatR 使用作用域服务
Unable to Consume Scoped Service via MediatR
我有一个 ASP.NET 核心应用程序 运行 .NET 5 和 C# 9。它还在后台运行一个 Discord 机器人。我在 Startup.cs
中的 ConfigureServices()
方法如下所示。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
var client = new DiscordSocketClient(new DiscordSocketConfig
{
AlwaysDownloadUsers = true,
MessageCacheSize = 10000,
GatewayIntents = GatewayIntents.Guilds | GatewayIntents.GuildMessages |
GatewayIntents.GuildMessageReactions | GatewayIntents.GuildPresences,
LogLevel = LogSeverity.Info
});
var commandService = new CommandService(new CommandServiceConfig
{
LogLevel = LogSeverity.Debug,
DefaultRunMode = RunMode.Sync,
CaseSensitiveCommands = false,
IgnoreExtraArgs = false,
});
services
.AddMediatR(Assembly.GetEntryAssembly())
.AddHostedService<StartupService>()
.AddHostedService<DiscordListener>()
.AddScoped<ITestService, TestService>()
.AddSingleton(client)
.AddSingleton(provider =>
{
commandService.AddModulesAsync(Assembly.GetEntryAssembly(), provider);
return commandService;
})
.AddSingleton(Configuration);
}
如您所见,我已将 ITestService
和 TestService
添加为作用域服务。
public class TestService : ITestService
{
public async Task<string> GetString()
{
await Task.Delay(1);
return "hey";
}
}
public interface ITestService
{
Task<string> GetString();
}
然后我将此服务注入我的命令模块。
public class TestModule : ModuleBase<SocketCommandContext>
{
private readonly ITestService _testService;
public TestModule(ITestService testService)
{
_testService = testService;
}
[Command("ping")]
public async Task Ping()
{
var str = await _testService.GetString();
await ReplyAsync(str);
}
}
但是,应用程序不响应 ping
命令。事实上,我用于接收消息的处理程序根本没有命中(我已经通过断点进行了检查)。这是监听事件并发布相关 MediatR 通知的托管服务。
public partial class DiscordListener : IHostedService
{
private readonly DiscordSocketClient _client;
private readonly IServiceScopeFactory _serviceScopeFactory;
public DiscordListener(
DiscordSocketClient client,
IServiceScopeFactory serviceScopeFactory)
{
_client = client;
_serviceScopeFactory = serviceScopeFactory;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_client.MessageReceived += MessageReceived;
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_client.MessageReceived -= MessageReceived;
return Task.CompletedTask;
}
// Creating our own scope here
private async Task MessageReceived(SocketMessage message)
{
using var scope = _serviceScopeFactory.CreateScope();
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
await mediator.Publish(new MessageReceivedNotification(message));
}
}
这是处理通知的通知处理程序。
public class CommandListener : INotificationHandler<MessageReceivedNotification>
{
private readonly IConfiguration _configuration;
private readonly DiscordSocketClient _client;
private readonly CommandService _commandService;
private readonly IServiceProvider _serviceProvider;
public CommandListener(
IConfiguration configuration,
DiscordSocketClient client,
CommandService commandService,
IServiceProvider serviceProvider)
{
_configuration = configuration;
_client = client;
_commandService = commandService;
_serviceProvider = serviceProvider;
}
public async Task Handle(MessageReceivedNotification notification, CancellationToken cancellationToken)
{
if (!(notification.Message is SocketUserMessage message)
|| !(message.Author is IGuildUser user)
|| user.IsBot)
{
return;
}
var argPos = 0;
var prefix = _configuration["Prefix"];
if (message.HasStringPrefix(prefix, ref argPos))
{
var context = new SocketCommandContext(_client, message);
using var scope = _serviceProvider.CreateScope();
await _commandService.ExecuteAsync(context, argPos, scope.ServiceProvider);
}
}
}
澄清一下,_client.MessageReceoved += ...
处的断点未命中。如果我将 ITestService
和 TestService
实现更改为 Singleton,则会命中处理程序并且命令按预期工作。知道我做错了什么吗?
如果您想查看完整代码,Here 是项目的 GitHub 回购。不算太大。
这是混合单例和作用域服务时的典型问题。如果您最终遇到的情况是单例正在解析范围内的服务,则这是不允许的。
来自此处的文档
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1
不要从单例中解析作用域服务。它可能导致服务在处理后续请求时具有不正确的状态。没关系:
从作用域服务或瞬态服务中解析单例服务。
从另一个作用域或瞬态服务解析作用域服务。
默认情况下,在开发环境中,从另一个生命周期更长的服务解析一个服务会抛出异常。有关详细信息,请参阅范围验证。
的更多讨论
我有一个 ASP.NET 核心应用程序 运行 .NET 5 和 C# 9。它还在后台运行一个 Discord 机器人。我在 Startup.cs
中的 ConfigureServices()
方法如下所示。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
var client = new DiscordSocketClient(new DiscordSocketConfig
{
AlwaysDownloadUsers = true,
MessageCacheSize = 10000,
GatewayIntents = GatewayIntents.Guilds | GatewayIntents.GuildMessages |
GatewayIntents.GuildMessageReactions | GatewayIntents.GuildPresences,
LogLevel = LogSeverity.Info
});
var commandService = new CommandService(new CommandServiceConfig
{
LogLevel = LogSeverity.Debug,
DefaultRunMode = RunMode.Sync,
CaseSensitiveCommands = false,
IgnoreExtraArgs = false,
});
services
.AddMediatR(Assembly.GetEntryAssembly())
.AddHostedService<StartupService>()
.AddHostedService<DiscordListener>()
.AddScoped<ITestService, TestService>()
.AddSingleton(client)
.AddSingleton(provider =>
{
commandService.AddModulesAsync(Assembly.GetEntryAssembly(), provider);
return commandService;
})
.AddSingleton(Configuration);
}
如您所见,我已将 ITestService
和 TestService
添加为作用域服务。
public class TestService : ITestService
{
public async Task<string> GetString()
{
await Task.Delay(1);
return "hey";
}
}
public interface ITestService
{
Task<string> GetString();
}
然后我将此服务注入我的命令模块。
public class TestModule : ModuleBase<SocketCommandContext>
{
private readonly ITestService _testService;
public TestModule(ITestService testService)
{
_testService = testService;
}
[Command("ping")]
public async Task Ping()
{
var str = await _testService.GetString();
await ReplyAsync(str);
}
}
但是,应用程序不响应 ping
命令。事实上,我用于接收消息的处理程序根本没有命中(我已经通过断点进行了检查)。这是监听事件并发布相关 MediatR 通知的托管服务。
public partial class DiscordListener : IHostedService
{
private readonly DiscordSocketClient _client;
private readonly IServiceScopeFactory _serviceScopeFactory;
public DiscordListener(
DiscordSocketClient client,
IServiceScopeFactory serviceScopeFactory)
{
_client = client;
_serviceScopeFactory = serviceScopeFactory;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_client.MessageReceived += MessageReceived;
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_client.MessageReceived -= MessageReceived;
return Task.CompletedTask;
}
// Creating our own scope here
private async Task MessageReceived(SocketMessage message)
{
using var scope = _serviceScopeFactory.CreateScope();
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
await mediator.Publish(new MessageReceivedNotification(message));
}
}
这是处理通知的通知处理程序。
public class CommandListener : INotificationHandler<MessageReceivedNotification>
{
private readonly IConfiguration _configuration;
private readonly DiscordSocketClient _client;
private readonly CommandService _commandService;
private readonly IServiceProvider _serviceProvider;
public CommandListener(
IConfiguration configuration,
DiscordSocketClient client,
CommandService commandService,
IServiceProvider serviceProvider)
{
_configuration = configuration;
_client = client;
_commandService = commandService;
_serviceProvider = serviceProvider;
}
public async Task Handle(MessageReceivedNotification notification, CancellationToken cancellationToken)
{
if (!(notification.Message is SocketUserMessage message)
|| !(message.Author is IGuildUser user)
|| user.IsBot)
{
return;
}
var argPos = 0;
var prefix = _configuration["Prefix"];
if (message.HasStringPrefix(prefix, ref argPos))
{
var context = new SocketCommandContext(_client, message);
using var scope = _serviceProvider.CreateScope();
await _commandService.ExecuteAsync(context, argPos, scope.ServiceProvider);
}
}
}
澄清一下,_client.MessageReceoved += ...
处的断点未命中。如果我将 ITestService
和 TestService
实现更改为 Singleton,则会命中处理程序并且命令按预期工作。知道我做错了什么吗?
Here 是项目的 GitHub 回购。不算太大。
这是混合单例和作用域服务时的典型问题。如果您最终遇到的情况是单例正在解析范围内的服务,则这是不允许的。
来自此处的文档 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1
不要从单例中解析作用域服务。它可能导致服务在处理后续请求时具有不正确的状态。没关系:
从作用域服务或瞬态服务中解析单例服务。 从另一个作用域或瞬态服务解析作用域服务。 默认情况下,在开发环境中,从另一个生命周期更长的服务解析一个服务会抛出异常。有关详细信息,请参阅范围验证。
的更多讨论