我们有一个dotnet-core 2.1 web-api项目设置,如下所示。
我们正在尝试将MediatR与HttpClient一起使用,并在HttpMagicService中发送事件以响应不同的响应。在调用HttpClient.SendAsync()
之后尝试使用注入的服务时,问题再次出现。
IMagicService
和MediatR的初始设置。
public void ConfigureServices(IServiceCollection services)
{
// other services configured above
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestLoggingBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPerformanceBehaviour<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestValidationBehavior<,>));
services.AddMediatR(typeof(ApplicaitonCommand).GetTypeInfo().Assembly);
services.AddHttpClient<IMagicService, HttpMagicService>();
}
IMagicService
的实现。
public class HttpMagicService : IMagicService
{
private readonly IMediator mediator;
private readonly HttpClient httpClient;
public HttpMagicService(IMediator mediator, HttpClient httpClient)
{
this.mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public async Task AddAsync(Magic data, Metadata metadata)
{
// This one works
await this.mediator.Publish(new MagicAddInitIntegrationEvent(data));
var url = metadata.RemoteUri + "/command/AddMagic";
using (var request = new HttpRequestMessage(HttpMethod.Post, url))
{
try
{
var httpContent = CreateHttpContent(data);
request.Content = httpContent;
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", metadata.AuthKey);
// httpClient clears the ResolvedServices?!
var response = await this.httpClient.SendAsync(request).ConfigureAwait(false);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
// This one fails
await this.mediator.Publish(new MagicAddIntegrationEvent(data));
}
else
{
await this.mediator.Publish(new MagicAddFailedIntegrationEvent(data));
}
}
catch (Exception ex)
{
await this.mediator.Publish(new MagicAddFailedIntegrationEvent(meeting, ex));
}
}
}
private static HttpContent CreateHttpContent(object content)
{
// ...
}
}
对于我们的第一个呼叫await this.mediator.Publish(new MagicAddInitIntegrationEvent(data));
,事件已发布,并且处理程序可以处理该事件。
从this.mediator
看,我们发现有针对该服务的解决方案。
当我们运行var response = await this.httpClient.SendAsync(request).ConfigureAwait(false);
时,httpClient似乎决定为所有人分配所有服务。 ?
在此行之后查看this.mediator
时,我们发现一切都已清除。
由于没有服务,因此呼叫await this.mediator.Publish(new MagicAddIntegrationEvent(data));
将无法运行,并且我们会收到异常消息。
这是this.httpClient.SendAsync
的预期行为吗?我们应该用另一种方式吗?
一种可行的解决方案是忽略设置services.AddHttpClient<IMagicService, HttpMagicService>();
,而为HttpClient
设置单例服务,这似乎可行,但是我不确定这是否是正确的方法
解决方案
使用了Panagiotis Kanavos的建议并使用了范围限定的httpClient,所以我的解决方案改为了此。
public void ConfigureServices(IServiceCollection services)
{
// other services configured above
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestLoggingBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPerformanceBehaviour<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestValidationBehavior<,>));
services.AddMediatR(typeof(ApplicaitonCommand).GetTypeInfo().Assembly);
services.AddScoped<HttpClient>();
}
如果在不使用var mediator= scope.ServiceProvider.GetRequiredService<IMediator>();
的情况下尝试在范围内使用IMediator,则会出现与以前相同的问题。因此,我错过了DI容器存在的范围问题。
public class HttpMagicService : IMagicService
{
private readonly IMediator mediator;
private readonly IServiceProvider services;
public HttpMagicService(IServiceProvider services)
{
this.services = services?? throw new ArgumentNullException(nameof(services));
}
public async Task AddAsync(Magic data, Metadata metadata)
{
using (var scope = Services.CreateScope())
{
var httpClient = scope.ServiceProvider.GetRequiredService<HttpClient>();
var mediator= scope.ServiceProvider.GetRequiredService<IMediator>();
...
}
答案 0 :(得分:0)
当单例服务尝试使用作用域服务时,经常会发生此类异常。 This question about MediatR and scopes可能是相关的。无论如何,HttpClientFactory都会定期回收HttpClient实例(特别是 handler 实例),这意味着如果HttpMagicService
缓存HttpClient实例足够长的时间,它将在某个时候被处理掉。
无论哪种情况,您都可以通过将IServiceProvider注入HttpMagicService并显式创建作用域来使用HttpClient。选中consuming a scoped service in a background task。
您可以注入IServiceProvider而不是HttpClient,并在AddAsync
方法内创建范围,例如:
public class HttpMagicService : IMagicService
{
private readonly IMediator mediator;
private readonly IServiceProvider services;
public HttpMagicService(IMediator mediator, IServiceProvider services)
{
this.mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
this.services = services?? throw new ArgumentNullException(nameof(services));
}
public async Task AddAsync(Magic data, Metadata metadata)
{
using (var scope = Services.CreateScope())
{
var httpClient = scope.ServiceProvider.GetRequiredService<HttpClient>();
...
}