Note
This documentation describes the previous Polly v7 API. If you are using the new v8 API, please refer to pollydocs.org.
An execution-scoped instance of the Polly.Context
class travels with every execution through a Polly policy.
The role of this class is to provide execution context and to allow the exchange of information between the pre-execution, mid-execution, and post-execution phases.
What information canContext
carry?
Context
carries two kinds of information:
Context
, using Dictionary<string, object> semantics.Context
available?
The context
instance travelling with the execution is available:
.Execute/Async(...)
-d through the policy (when you use an overload taking Context
)onRetry
, onBreak
etc)It can therefore be used to pass information into executions, get information out of executions, and exchange information among any part of a policy execution.
Pre-defined keys onContext
The following four pre-defined keys are available as properties directly on Context
:
PolicyWrapKey
PolicyWrap
instance
(in nested wrap, takes outermost specified value)
policyWrap
.WithPolicyKey("someValue")
PolicyWrap-partguid;
ornull
if no PolicyWrap in use
PolicyWrap
in use PolicyKey
Policy
instance policy
.WithPolicyKey("someValue")
PolicyType-partguid Indicate the Policy
in use OperationKey
call site
new
Context("someOperationKey")
into the Execute call null
Indicate where the policy in use
Used by CachePolicy
as the cache key for the execution
CorrelationId
an individual execution not user-settable a unique guid Uniquely correlate logs/ metrics for a single execution Changes between Polly <v6 and >=v6
OperationKey
was named ExecutionKey
prior to v6.
CorrelationId
was named ExecutionGuid
prior to v6.
// Identify policies with a PolicyKey, using the WithPolicyKey() extension method // (for example, for correlation in logs or metrics) var policy = Policy .Handle<DataAccessException>() .Retry(3, onRetry: (exception, retryCount, context) => { logger.Error($"Retry {retryCount} of {context.PolicyKey} at {context.OperationKey}, due to: {exception}."); }) .WithPolicyKey("MyDataAccessPolicy"); // Identify call sites with an OperationKey, by passing in a Context var customerDetails = policy.Execute(myDelegate, new Context("GetCustomerDetails")); // "MyDataAccessPolicy" -> context.PolicyKey // "GetCustomerDetails" -> context.OperationKeyUsing
Context
for custom data
Context
has full Dictionary<string, object>
semantics. You can set data on an instance of Context
var context = new Polly.Context(); context["MyCustomData"] = foo;
and get it back later anywhere context
is available - within an onRetry
hook, within the delegate executed through the policy, after policy execution:
var myFooFromElsewhere = context["MyCustomData"];Example: Pass information into an execution to enrich logging
Given a policy (abbreviated):
// Policy which logs information available from both pre-defined and custom user context data var policy = Policy .Handle<FooException>() .RetryAsync(3, onRetry: (exception, retryCount, context) => { logger.Error($"Retry {retryCount} of {context.PolicyKey} at {context.OperationKey}, getting {context["Type"]} of id {context["Id"]}, due to: {exception}. CorrelationId: {context.CorrelationId}"); }) .WithPolicyKey("GetEntitiesPolicy");
An example call site could pass in extra information (abbreviated example code):
public class EntityDataAccess<T> { private IAsyncPolicy Policy { get; } public EntityDataAccess(IAsyncPolicy policy) { Policy = policy; } public async T GetAsync(int id) { var context = new Context($"GetAsync-{typeof(T).Name}-{Id}", new Dictionary<string, object>() { { "Type" , typeof(T).Name }, { "Id" , id } }); return await Policy.ExecuteAsync(ctx => database.GetAsync(id), context); } }Example: Varying the
ILogger
used within an onRetry
delegate
A common use of Context
is to bridge the fact that policies are often defined (including policy callback delegates) on startup; but some information you want to use within the delegate hooks (such as an ILogger
) might only be available via dependency injection at the call site.
The policy can be pre-defined in StartUp
to retrieve the information from context
:
var policy = Policy.Handle<FooException>() .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (exception, timeSpan, retryCount, context) => { var msg = $"Retrying request for {retryCount} " + $"at {context.OperationKey}, " + $"due to: {exception}."; var logger = context.GetLogger(); logger?.LogWarning(msg); });
And the call site can configure the ILogger
onto Context
prior to execution:
// Meanwhile at the call site, given some ILogger logger available by dependency injection Context context = new Context().WithLogger(logger); return await policy.ExecuteAsync(ctx => FooAsync(), context);
In the above code, we used some helper methods to make the storage and retrieval of the ILogger
more concise and robust:
public static class ContextExtensions { private static readonly string LoggerKey = "LoggerKey"; public static Context WithLogger(this Context context, ILogger logger) { context[LoggerKey] = logger; return context; } public static ILogger GetLogger(this Context context) { if (context.TryGetValue(LoggerKey, out object logger)) { return logger as ILogger; } return null; } }Using
Context
with policies configured via HttpClientFactory
See the Polly with HttpClientFactory page.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4