You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core Web Api中Polly ResilienceContext属性为空问题排查

问题:ResilienceContext传递值时Properties始终为空

在.NET Core Web Api中使用基于Polly的Microsoft.Extensions.Http.Resilience实现弹性机制,尝试通过ResilienceContext传递值,但调试时发现args.Context.Properties始终为空。相关代码如下:

定义ResilienceKeys类

public static class ResilienceKeys
{
    public static readonly ResiliencePropertyKey<string> LocationId = new("location-id");
}

WordPressService中的调用逻辑

// WordPressService.cs

public class WordPressService : IWordPressService
{
    private readonly HttpClient _httpClient;
    private readonly ResiliencePipelineProvider<string> _pipelineProvider;

    public WordPressService(HttpClient httpClient, ResiliencePipelineProvider<string> pipelineProvider)
    {
        _httpClient = httpClient;
        _httpClient.BaseAddress = new Uri("https://api.example.com/");

        _pipelineProvider = pipelineProvider;
    }

    public async Task<string> GetBannersByLocationAsync(int locationId, CancellationToken cancellationToken)
    {
        var pipeline = _pipelineProvider.GetPipeline<HttpResponseMessage>("my-pipeline");

        var context = ResilienceContextPool.Shared.Get(cancellationToken);

        context.Properties.Set(ResilienceKeys.LocationId, locationId.ToString());

        var response = await pipeline.ExecuteAsync<HttpResponseMessage, ResilienceContext>(
            async (context, cancellationToken) =>
            {
                // Simulate an error for testing purposes
                await Task.Delay(100, cancellationToken);
                var response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
                return response;
            },
            context,
            cancellationToken);

        ResilienceContextPool.Shared.Return(context);

        return await response.Content.ReadAsStringAsync(cancellationToken);
    }
}

Program.cs中配置ResiliencePipeline的代码

// Program.cs

builder.Services.AddResiliencePipeline<string, HttpResponseMessage>("my-pipeline", (pipelineBuilder, context) =>
{
    var predicateBuilder = new PredicateBuilder<HttpResponseMessage>()
        .Handle<HttpRequestException>()
        .HandleResult(r => r.StatusCode >= HttpStatusCode.InternalServerError);

    pipelineBuilder
    .AddFallback(new FallbackStrategyOptions<HttpResponseMessage>()
    {
        ShouldHandle = predicateBuilder,
        FallbackAction = async (args) =>
        {
            if (args.Context.Properties.TryGetValue(ResilienceKeys.LocationId, out var data))
            {
                Console.WriteLine("FallbackAction, Location Id: {0}", data);
            }

            var fallbackResponse = new HttpResponseMessage()
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent("Fallback content")
            };

            return Outcome.FromResult(fallbackResponse);
        }
    })
    .AddRetry(new RetryStrategyOptions<HttpResponseMessage>()
    {
        ShouldHandle = predicateBuilder,
        Delay = TimeSpan.FromSeconds(2),
        MaxRetryAttempts = 2,
        BackoffType = DelayBackoffType.Exponential,
        UseJitter = true,
    });
});

原因分析

问题出在ExecuteAsync的调用方式上:你使用的ExecuteAsync<HttpResponseMessage, ResilienceContext>重载中,第二个参数是状态对象(State),而非传递ResilienceContext的正确参数。这个重载会自动创建新的默认ResilienceContext,不会复用你设置了Properties的实例,导致策略(Fallback/Retry)无法获取到你传入的值。


解决方案

修改WordPressService中的ExecuteAsync调用,使用专门接受ResilienceContext的重载:

public async Task<string> GetBannersByLocationAsync(int locationId, CancellationToken cancellationToken)
{
    var pipeline = _pipelineProvider.GetPipeline<HttpResponseMessage>("my-pipeline");

    var context = ResilienceContextPool.Shared.Get(cancellationToken);
    try
    {
        context.Properties.Set(ResilienceKeys.LocationId, locationId.ToString());

        // 使用接受ResilienceContext的ExecuteAsync重载
        var response = await pipeline.ExecuteAsync(async (ctx) =>
        {
            await Task.Delay(100, ctx.CancellationToken);
            return new HttpResponseMessage(HttpStatusCode.InternalServerError);
        }, context);

        return await response.Content.ReadAsStringAsync(cancellationToken);
    }
    finally
    {
        ResilienceContextPool.Shared.Return(context);
    }
}

如果需要保留CancellationToken的显式传递,也可以使用这个重载:

var response = await pipeline.ExecuteAsync(async (cancellationToken) =>
{
    await Task.Delay(100, cancellationToken);
    return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}, context);

这两个重载都会将你传入的context作为策略执行时的上下文,FallbackAction中的args.Context.Properties就能正确获取到你设置的LocationId值。


内容的提问来源于stack exchange,提问作者Alonso Ureña

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 14:47:36