Polly 与 IDistributedCache 和 IHttpClientFactory 策略
Polly with IDistributedCache and IHttpClientFactory Policy
使用以下代码可以正常编译,但收到以下运行时错误。似乎在使用 IHttpClientFactory
时仅支持 HttpResponseMessage
的策略之间存在冲突?
最终目标是能够使用多种策略,如重试、超时等,如果一切正常,使用缓存策略缓存结果...
Unable to cast object of type
'Polly.Caching.AsyncCachePolicy'1[System.String]' to type
'Polly.IAsyncPolicy'1[System.Net.Http.HttpResponseMessage]'.'
serviceCollection.AddStackExchangeRedisCache(options =>
{
options.Configuration = "...";
});
IPolicyRegistry<string> registry = serviceCollection.AddPolicyRegistry();
var cacheProvider = ServiceProvider.GetRequiredService<IDistributedCache>().AsAsyncCacheProvider<string>();
serviceCollection.AddSingleton(serviceProvider => cacheProvider);
AsyncCachePolicy<string> cachePolicy =
Policy.CacheAsync(
cacheProvider: cacheProvider,
TimeSpan.FromSeconds(30));
registry.Add("CachingPolicy", cachePolicy);
serviceCollection.AddHttpClient<IMyClient, MyClient>()
.AddPolicyHandlerFromRegistry(this.PolicySelector)
private IAsyncPolicy<HttpResponseMessage> PolicySelector(IReadOnlyPolicyRegistry<string> policyRegistry, HttpRequestMessage httpRequestMessage)
{
return policyRegistry.Get<IAsyncPolicy<HttpResponseMessage>>("CachingPolicy");
}
如错误所述,您无法将 AsyncCachePolicy<string>
转换为 IAsyncPolicy<HttpResponseMessage>
。由于所有 AsyncXYZPolicy
都实现了 IAsyncPolicy
接口,这就是问题不是出自此处的原因。而不是来自类型参数。
AddHttpClient
return 是 IHttpClientBuilder
。它们上面有 several extension methods,例如 AddPolicyHandlerFromRegistry
、AddTransientHttpErrorPolicy
或 AddPolicyHandler
。在所有情况下,您都需要注册 return 类型为 HttpResponseMessage
.
的策略
如果您尝试直接通过 AddPolicyHandler
注册您的缓存策略,那么它会导致编译错误而不是 run-time 错误。但是因为您从注册表中动态检索策略,这就是它在运行时抛出异常的原因。
如何解决?
与其将策略定义为 AsyncCachePolicy<string>
,不如将其定义为 AsyncCachePolicy<HttpResponseMessage>
。为此,您需要更改 AsAsyncCacheProvider
方法的类型参数。
var cacheProvider = ServiceProvider
.GetRequiredService<IDistributedCache>()
.AsAsyncCacheProvider<HttpResponseMessage>();
您还需要更改 cachePolicy
的类型
AsyncCachePolicy<HttpResponseMessage> cachePolicy =
Policy.CacheAsync(
cacheProvider: cacheProvider,
TimeSpan.FromSeconds(30));
旁注:我还建议将策略注册表项 ("CachingPolicy"
) 存储在常量中,并在注册和检索时引用它。
更新 #1:
我什至不确定您是否必须调用 AsAsyncCacheProvider
方法。让我仔细检查一下。
更新#2
在阅读 AsAsyncCacheProvider
的源代码后,我意识到它只支持 byte[]
或 string
作为类型参数。这表明您不能使用此处的 AddPolicyHandler
方法来自动缓存响应。
相反,您必须在 MyClient
实现中直接使用 AsyncPolicyCache<string>
。您需要修改 MyClient
的构造函数以接收 IReadonlyPolicyRegister<string>
参数。
private readonly IAsyncPolicy<string> _cachePolicy;
public MyClient(HttpClient client, IReadOnlyPolicyRegistry<string> policyRegistry)
{
_cachePolicy = policyRegistry.Get<IAsyncPolicy<string>>("CachingPolicy");
// ...
}
并且在您公开的方法中,您需要显式使用 ExecuteAsync
await _cachePolicy.ExecuteAsync(context => getXYZ(), new Context("XYZUniqueKey"));
getXYZ
需要 return 一个字符串(可能是响应主体)。
使用以下代码可以正常编译,但收到以下运行时错误。似乎在使用 IHttpClientFactory
时仅支持 HttpResponseMessage
的策略之间存在冲突?
最终目标是能够使用多种策略,如重试、超时等,如果一切正常,使用缓存策略缓存结果...
Unable to cast object of type 'Polly.Caching.AsyncCachePolicy'1[System.String]' to type 'Polly.IAsyncPolicy'1[System.Net.Http.HttpResponseMessage]'.'
serviceCollection.AddStackExchangeRedisCache(options =>
{
options.Configuration = "...";
});
IPolicyRegistry<string> registry = serviceCollection.AddPolicyRegistry();
var cacheProvider = ServiceProvider.GetRequiredService<IDistributedCache>().AsAsyncCacheProvider<string>();
serviceCollection.AddSingleton(serviceProvider => cacheProvider);
AsyncCachePolicy<string> cachePolicy =
Policy.CacheAsync(
cacheProvider: cacheProvider,
TimeSpan.FromSeconds(30));
registry.Add("CachingPolicy", cachePolicy);
serviceCollection.AddHttpClient<IMyClient, MyClient>()
.AddPolicyHandlerFromRegistry(this.PolicySelector)
private IAsyncPolicy<HttpResponseMessage> PolicySelector(IReadOnlyPolicyRegistry<string> policyRegistry, HttpRequestMessage httpRequestMessage)
{
return policyRegistry.Get<IAsyncPolicy<HttpResponseMessage>>("CachingPolicy");
}
如错误所述,您无法将 AsyncCachePolicy<string>
转换为 IAsyncPolicy<HttpResponseMessage>
。由于所有 AsyncXYZPolicy
都实现了 IAsyncPolicy
接口,这就是问题不是出自此处的原因。而不是来自类型参数。
AddHttpClient
return 是 IHttpClientBuilder
。它们上面有 several extension methods,例如 AddPolicyHandlerFromRegistry
、AddTransientHttpErrorPolicy
或 AddPolicyHandler
。在所有情况下,您都需要注册 return 类型为 HttpResponseMessage
.
如果您尝试直接通过 AddPolicyHandler
注册您的缓存策略,那么它会导致编译错误而不是 run-time 错误。但是因为您从注册表中动态检索策略,这就是它在运行时抛出异常的原因。
如何解决?
与其将策略定义为 AsyncCachePolicy<string>
,不如将其定义为 AsyncCachePolicy<HttpResponseMessage>
。为此,您需要更改 AsAsyncCacheProvider
方法的类型参数。
var cacheProvider = ServiceProvider
.GetRequiredService<IDistributedCache>()
.AsAsyncCacheProvider<HttpResponseMessage>();
您还需要更改 cachePolicy
的类型
AsyncCachePolicy<HttpResponseMessage> cachePolicy =
Policy.CacheAsync(
cacheProvider: cacheProvider,
TimeSpan.FromSeconds(30));
旁注:我还建议将策略注册表项 ("CachingPolicy"
) 存储在常量中,并在注册和检索时引用它。
更新 #1:
我什至不确定您是否必须调用 AsAsyncCacheProvider
方法。让我仔细检查一下。
更新#2
在阅读 AsAsyncCacheProvider
的源代码后,我意识到它只支持 byte[]
或 string
作为类型参数。这表明您不能使用此处的 AddPolicyHandler
方法来自动缓存响应。
相反,您必须在 MyClient
实现中直接使用 AsyncPolicyCache<string>
。您需要修改 MyClient
的构造函数以接收 IReadonlyPolicyRegister<string>
参数。
private readonly IAsyncPolicy<string> _cachePolicy;
public MyClient(HttpClient client, IReadOnlyPolicyRegistry<string> policyRegistry)
{
_cachePolicy = policyRegistry.Get<IAsyncPolicy<string>>("CachingPolicy");
// ...
}
并且在您公开的方法中,您需要显式使用 ExecuteAsync
await _cachePolicy.ExecuteAsync(context => getXYZ(), new Context("XYZUniqueKey"));
getXYZ
需要 return 一个字符串(可能是响应主体)。