如何解决使用 SimpleInjector
根据 https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory#extending-the-convenience-addtransienthttperrorpolicy-definition,我可以看到添加了一个名称的策略。
var httpClientOptions = HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(6,retryAttempt => TimeSpan.FromSeconds(Math.Pow(2,retryAttempt)));
services.AddHttpClient(/*have to add a name*/)
.AddPolicyHandler(httpClientOptions);
我使用 Simple Injector 与 .net 依赖项注入并行,并将 http 客户端工厂注入到我的消费类中,如下所示:
public polygonIoApiFetcher(IHttpClientFactory clientFactory)
{
this.client = clientFactory.CreateClient(/* what to use here */);
}
我的问题是我在其他库中编写了许多函数,这些函数不一定在库之间共享客户端工厂策略名称。
此外,为了简单起见,我可能希望为工厂创建的所有 HttpClient
使用一个 poly 重试策略 - IHttpClientFactory
有什么方法可以返回默认的 HttpClient
是否应用了默认的 poly 策略?
我不想对每个 https://github.com/simpleinjector/SimpleInjector/issues/654 使用类型化注册,我需要通过注入的 IHttpClientFactory
而不是注入的 HttpClient
来控制对象的生命周期消费类。
总而言之,启动时的连接/注入应该决定注入和轻松重新连接/更改的策略,而不是由消费类决定。
我已经提供了到目前为止我所做的事情,但如果我怀疑这是一个已经解决的问题,请指点迷津。
下面的想法 - 创建一个包装类
HttpClient CreateClient(this IHttpClientFactory factory)
中的无参数 System.Net.Http
扩展方法不能被覆盖,所以我想使用包装类并在注入过程中手动选择策略:
public interface IPollyHttpClientFactory
{
HttpClient CreateClient();
}
public class PollyHttpClientFactoryWrapper
{
private readonly string policyName;
private readonly Container container;
public PollyHttpClientFactoryWrapper(
string policyName,Container container)
{
this.policyName = policyName ?? throw new ArgumentNullException();
this.container = container ?? throw new ArgumentNullException();
}
public HttpClient CreateClient()
{
return this.container
.GetInstance<IHttpClientFactory>()
.CreateClient(this.policyName);
}
}
注册时使用:
Container.RegisterSingleton<IHistoricalAggregates>(
() => new polygonIoApiFetcher(
new PollyHttpClientFactoryWrapper("RetryPolicy",Container),Container.GetInstance<ApiKeys>().polygonIoApiKey));
唯一的“问题”是我的类需要使用 IPollyHttpClientFactory
接口,但我可以接受。
解决方法
我不确定我能不能给你一个满意的答案,但这里有一个可能实现的想法。您可以使 IPollyHttpClientFactory
成为有条件的,让每个使用者都获得自己的版本。这可以使用 RegisterConditional
方法完成。例如:
var container = new Container();
container.RegisterSingleton<IHistoricalAggregates,PolygonIoApiFetcher>();
container.RegisterConditional(
typeof(IPollyHttpClientFactory),c => typeof(PollyHttpClientFactory<>)
.MakeGenericType(c.Consumer.ImplementationType),Lifestyle.Singleton,c => true);
container.MapPolicy<PolygonIoApiFetcher>("RetryPolicy");
此处,MapPolicy
是自定义扩展方法:
public static class ContainerPolicyExtensions
{
public static void MapPolicy<TImplementation>(
this Container container,string policyName) =>
container.RegisterInstance(new Policy<TImplementation>(policyName));
}
Policy<>
是一个简单的通用数据对象,它允许保存要用于 HttpClient
的策略名称(您可以向该策略类添加与创建 http 客户端相关的任何其他数据):
public sealed record Policy<TConsumer>(string PolicyName);
PollyHttpClientFactory<>
是 IPollyHttpClientFactory
的通用实现。它被注入了相关的 Policy<T>
,因此能够创建特定于其使用者的 http 客户端:
public record PollyHttpClientFactory<TConsumer>(
IHttpClientFactory Factory,Policy<TConsumer> Policy)
: IPollyHttpClientFactory
{
public HttpClient CreateClient() =>
this.Factory.CreateClient(Policy.PolicyName);
}
或者,您可以将 Register
和 MapPolicy
方法合并为一个扩展方法:
public static void RegisterWithPolicy<TService,TImplementation>(
this Container container,string policyName,Lifestyle lifestyle = null)
where TService : class
where TImplementation : class,TService
{
if (lifestyle is null) container.Register<TService,TImplementation>();
else container.Register<TService,TImplementation>(lifestyle);
container.MapPolicy<TImplementation>(policyName);
}
这让您可以一举完成注册和映射:
container.RegisterWithPolicy<IHistoricalAggregates,PolygonIoApiFetcher>(
"RetryPolicy",Lifestyle.Singleton);
最后一点。在您的问题中,您从构造函数中的 调用 IHttpClientFactory.CreateClient
方法,并将客户端存储在使用者中。这通常不是一个好主意,因为它可能会让客户端存活很长时间并违背 IHttpClientFactory
的目的。相反,将 IPollyHttpClientFactory
(或 IHttpClientFactory
)存储在私有字段中,并且仅从使用者的方法中调用其 CreateClient
方法,并在方法的末尾处置客户端。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。