Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ var pipelineProvider = serviceProvider.GetRequiredService<ResiliencePipelineProv
// Retrieve your resilience pipeline using the name it was registered with
ResiliencePipeline pipeline = pipelineProvider.GetPipeline("my-pipeline");

// Alternatively, you can use keyed services to retrieve the resilience pipeline
pipeline = serviceProvider.GetRequiredKeyedService<ResiliencePipeline>("my-pipeline");

// Execute the pipeline
await pipeline.ExecuteAsync(static async token =>
{
Expand Down
53 changes: 53 additions & 0 deletions docs/advanced/dependency-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,56 @@ await pipeline.ExecuteAsync(
```
<!-- endSnippet -->

## Keyed services

.NET 8 introduced support for [keyed services](https://learn.microsoft.com/dotnet/core/extensions/dependency-injection#keyed-services).
Starting from version 8.3.0, Polly supports the retrieval of `ResiliencePipeline` or `ResiliencePipeline<T>` using keyed services.

To begin, define your resilience pipeline:

<!-- snippet: di-keyed-services-define -->
```cs
// Define a resilience pipeline
services.AddResiliencePipeline<string, HttpResponseMessage>("my-pipeline", builder =>
{
// Configure the pipeline
});

// Define a generic resilience pipeline
services.AddResiliencePipeline("my-pipeline", builder =>
{
// Configure the pipeline
});
```
<!-- endSnippet -->

Following the definition above, you can resolve the resilience pipelines using keyed services as shown in the example below:

<!-- snippet: di-keyed-services-use -->
```cs
public class MyApi
{
private readonly ResiliencePipeline _pipeline;
private readonly ResiliencePipeline<HttpResponseMessage> _genericPipeline;

public MyApi(
[FromKeyedServices("my-pipeline")]
ResiliencePipeline pipeline,
[FromKeyedServices("my-pipeline")]
ResiliencePipeline<HttpResponseMessage> genericPipeline)
{
// Although the pipelines are registered with the same key, they are distinct instances.
// One is generic, the other is not.
_pipeline = pipeline;
_genericPipeline = genericPipeline;
}
}
```
<!-- endSnippet -->

> [!NOTE]
> The resilience pipelines are registered in the DI container as transient services. This enables the resolution of multiple instances of `ResiliencePipeline` when [complex pipeline keys](#complex-pipeline-keys) are used. Resilience pipeline is retrieved and registered using `ResiliencePipelineProvider` that is responsible for lifetime management of resilience pipelines.
Comment thread
martintmk marked this conversation as resolved.
Outdated

## Deferred addition of pipelines

If you want to use a key for a resilience pipeline that may not be available
Expand Down Expand Up @@ -133,6 +183,9 @@ services
```
<!-- endSnippet -->

> [!NOTE]
> The `AddResiliencePipelines` method does not support keyed services. To enable the resolution of a resilience pipeline using keyed services, you should use the `AddResiliencePipeline` extension, which adds a single resilience pipeline and registers it into the keyed services.
Comment thread
martintmk marked this conversation as resolved.
Outdated

## Dynamic reloads

Dynamic reloading is a feature of the pipeline registry that is also surfaced when
Expand Down
3 changes: 3 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ var pipelineProvider = serviceProvider.GetRequiredService<ResiliencePipelineProv
// Retrieve your resilience pipeline using the name it was registered with
ResiliencePipeline pipeline = pipelineProvider.GetPipeline("my-pipeline");

// Alternatively, you can use keyed services to retrieve the resilience pipeline
pipeline = serviceProvider.GetRequiredKeyedService<ResiliencePipeline>("my-pipeline");

// Execute the pipeline
await pipeline.ExecuteAsync(static async token =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ public static IServiceCollection AddResiliencePipeline<TKey, TResult>(
Guard.NotNull(services);
Guard.NotNull(configure);

services.TryAddKeyedTransient(
key,
(serviceProvider, key) =>
{
var pipelineProvider = serviceProvider.GetRequiredService<ResiliencePipelineProvider<TKey>>();

return pipelineProvider.GetPipeline<TResult>((TKey)key!);
});

return services.AddResiliencePipelines<TKey>((context) =>
{
context.AddResiliencePipeline(key, configure);
Expand Down Expand Up @@ -125,6 +134,14 @@ public static IServiceCollection AddResiliencePipeline<TKey>(
Guard.NotNull(services);
Guard.NotNull(configure);

services.TryAddKeyedTransient(
key,
(serviceProvider, key) =>
{
var pipelineProvider = serviceProvider.GetRequiredService<ResiliencePipelineProvider<TKey>>();
return pipelineProvider.GetPipeline((TKey)key!);
});

return services.AddResiliencePipelines<TKey>((context) =>
{
context.AddResiliencePipeline(key, configure);
Expand Down
43 changes: 43 additions & 0 deletions src/Snippets/Docs/DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

namespace Snippets.Docs;

#pragma warning disable IDE0052 // Remove unread private members

internal static class DependencyInjection
{
public static async Task AddResiliencePipeline()
Expand Down Expand Up @@ -85,6 +87,47 @@ await pipeline.ExecuteAsync(
#endregion
}

public static async Task KeyedServicesDefine(IServiceCollection services)
{
#region di-keyed-services-define

// Define a resilience pipeline
services.AddResiliencePipeline<string, HttpResponseMessage>("my-pipeline", builder =>
{
// Configure the pipeline
});

// Define a generic resilience pipeline
services.AddResiliencePipeline("my-pipeline", builder =>
{
// Configure the pipeline
});

#endregion
}

#region di-keyed-services-use

public class MyApi
{
private readonly ResiliencePipeline _pipeline;
private readonly ResiliencePipeline<HttpResponseMessage> _genericPipeline;

public MyApi(
[FromKeyedServices("my-pipeline")]
ResiliencePipeline pipeline,
[FromKeyedServices("my-pipeline")]
ResiliencePipeline<HttpResponseMessage> genericPipeline)
{
// Although the pipelines are registered with the same key, they are distinct instances.
// One is generic, the other is not.
_pipeline = pipeline;
_genericPipeline = genericPipeline;
}
}

#endregion

public static async Task DeferredAddition(IServiceCollection services)
{
#region di-deferred-addition
Expand Down
3 changes: 3 additions & 0 deletions src/Snippets/Docs/Readme.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public static async Task QuickStartDi()
// Retrieve your resilience pipeline using the name it was registered with
ResiliencePipeline pipeline = pipelineProvider.GetPipeline("my-pipeline");

// Alternatively, you can use keyed services to retrieve the resilience pipeline
pipeline = serviceProvider.GetRequiredKeyedService<ResiliencePipeline>("my-pipeline");

// Execute the pipeline
await pipeline.ExecuteAsync(static async token =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,56 @@ public void AddResiliencePipeline_Single_Ok()
provider.GetPipeline("my-pipeline").Should().BeSameAs(provider.GetPipeline("my-pipeline"));
}

[Fact]
public void AddResiliencePipeline_KeyedSingleton_Ok()
{
AddResiliencePipeline(Key);

var provider = _services.BuildServiceProvider();

var pipeline = provider.GetKeyedService<ResiliencePipeline>(Key);
provider.GetKeyedService<ResiliencePipeline>(Key).Should().BeSameAs(pipeline);

pipeline.Should().NotBeNull();
}

[Fact]
public void AddResiliencePipeline_GenericKeyedSingleton_Ok()
{
AddResiliencePipeline<string>(Key);

var provider = _services.BuildServiceProvider();

var pipeline = provider.GetKeyedService<ResiliencePipeline<string>>(Key);
provider.GetKeyedService<ResiliencePipeline<string>>(Key).Should().BeSameAs(pipeline);

pipeline.Should().NotBeNull();
}

[Fact]
public void AddResiliencePipeline_KeyedSingletonOverride_Ok()
{
var pipeline = new ResiliencePipelineBuilder().AddTimeout(TimeSpan.FromSeconds(1)).Build();
_services.AddKeyedSingleton(Key, pipeline);
AddResiliencePipeline(Key);

var provider = _services.BuildServiceProvider();

provider.GetKeyedService<ResiliencePipeline>(Key).Should().BeSameAs(pipeline);
}

[Fact]
public void AddResiliencePipeline_GenericKeyedSingletonOverride_Ok()
{
var pipeline = new ResiliencePipelineBuilder<string>().AddTimeout(TimeSpan.FromSeconds(1)).Build();
_services.AddKeyedSingleton(Key, pipeline);
AddResiliencePipeline(Key);

var provider = _services.BuildServiceProvider();

provider.GetKeyedService<ResiliencePipeline<string>>(Key).Should().BeSameAs(pipeline);
}

[InlineData(true)]
[InlineData(false)]
[Theory]
Expand Down