Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Polly-Contrib/Simmy/llms.txt

Use this file to discover all available pages before exploring further.

InjectBehaviourOptions and InjectBehaviourAsyncOptions are the configuration objects you pass to MonkeyPolicy.InjectBehaviour and MonkeyPolicy.InjectBehaviourAsync. Unlike the outcome and latency policies, behaviour injection lets you run any arbitrary Action — or async Func<Task> — before the wrapped delegate executes. This is useful for chaos scenarios that don’t fit neatly into “throw an exception” or “add a delay”: for example, writing to a corrupted cache, publishing a spurious event, exhausting a connection pool, or logging diagnostic information during a chaos run. The behaviour is always injected before the wrapped delegate executes, and the wrapped call proceeds normally afterwards.

Namespaces

using Polly.Contrib.Simmy.Behavior; // options types
using Polly.Contrib.Simmy;          // base extension methods

InjectBehaviourOptions (sync)

public class InjectBehaviourOptions : InjectOptionsBase
Synchronous behaviour options. Inherits InjectionRate and Enabled delegates from InjectOptionsBase. The internal BehaviourInternal property holds the Action<Context, CancellationToken> delegate and is populated by the .Behaviour(...) extension methods below.

InjectBehaviourAsyncOptions (async)

public class InjectBehaviourAsyncOptions : InjectOptionsAsyncBase
Asynchronous behaviour options. Inherits async InjectionRate and Enabled delegates (Task<double> / Task<bool>) from InjectOptionsAsyncBase. The internal BehaviourInternal property is Func<Context, CancellationToken, Task>.

Behaviour extension methods

.Behaviour(Action) — sync, no context

Configures a simple parameterless action to execute before the wrapped delegate.
public static InjectBehaviourOptions Behaviour(
    this InjectBehaviourOptions options,
    Action behaviour)
options
InjectBehaviourOptions
required
The sync options object being configured (supplied automatically by the fluent chain).
behaviour
Action
required
A parameterless action invoked at injection-time, before the wrapped call. The Polly context and cancellation token are not exposed; use the context-aware overload if you need them.
MonkeyPolicy.InjectBehaviour(with =>
    with.Behaviour(() => Console.WriteLine("[chaos] behaviour triggered"))
        .InjectionRate(0.1)
        .Enabled());

.Behaviour(Action<Context, CancellationToken>) — sync with context

Configures an action that receives the current Polly Context and CancellationToken at injection-time.
public static InjectBehaviourOptions Behaviour(
    this InjectBehaviourOptions options,
    Action<Context, CancellationToken> behaviour)
options
InjectBehaviourOptions
required
The sync options object being configured.
behaviour
Action<Context, CancellationToken>
required
A synchronous action invoked with the current Context and CancellationToken. Use ctx.OperationKey or custom context data to tailor the behaviour per execution, and honour the cancellation token if the action performs any blocking work.
MonkeyPolicy.InjectBehaviour(with =>
    with.Behaviour((ctx, ct) =>
        {
            ct.ThrowIfCancellationRequested();
            logger.LogWarning(
                "[chaos] injecting behaviour for operation '{Op}'",
                ctx.OperationKey);
            corruptCache.Invalidate(ctx.OperationKey);
        })
        .InjectionRate(0.05)
        .EnabledWhen((ctx, ct) =>
            Environment.GetEnvironmentVariable("CHAOS_ENABLED") == "true"));

.Behaviour(Func<Task>) — async, no context

Configures an async action with no context access. Internally promoted to Func<Context, CancellationToken, Task>.
public static InjectBehaviourAsyncOptions Behaviour(
    this InjectBehaviourAsyncOptions options,
    Func<Task> behaviour)
options
InjectBehaviourAsyncOptions
required
The async options object being configured.
behaviour
Func<Task>
required
A parameterless async function returning Task. Use this for straightforward async side-effects that do not need access to the Polly context or cancellation token.
MonkeyPolicy.InjectBehaviourAsync(with =>
    with.Behaviour(async () =>
        {
            await Task.Delay(50); // brief async side-effect
            await eventBus.PublishChaosEventAsync();
        })
        .InjectionRate(0.1)
        .Enabled());

.Behaviour(Func<Context, CancellationToken, Task>) — async with context

Configures an async action that receives the Polly Context and CancellationToken.
public static InjectBehaviourAsyncOptions Behaviour(
    this InjectBehaviourAsyncOptions options,
    Func<Context, CancellationToken, Task> behaviour)
options
InjectBehaviourAsyncOptions
required
The async options object being configured.
behaviour
Func<Context, CancellationToken, Task>
required
An async delegate returning Task. Receives the current Polly Context and CancellationToken, making it the most flexible overload. Suitable for async chaos actions that depend on execution context or need to respect cancellation, such as writing to a test database or calling an external chaos-coordination API.
MonkeyPolicy.InjectBehaviourAsync(with =>
    with.Behaviour(async (ctx, ct) =>
        {
            ct.ThrowIfCancellationRequested();
            await chaosCoordinator.RecordInjectionAsync(ctx.OperationKey, ct);
        })
        .InjectionRate(0.05)
        .Enabled());

Inherited base options

InjectBehaviourOptions inherits from InjectOptionsBase and InjectBehaviourAsyncOptions from InjectOptionsAsyncBase. The following methods control when and how often the behaviour is triggered. InjectionRate and Enabled are both required — the factory throws ArgumentNullException if either is missing.

Sync base (InjectOptionsBaseExtensions)

.Enabled() — always on

public static InjectOptionsBase Enabled(this InjectOptionsBase options)
Unconditionally enables the policy on every call. Equivalent to .Enabled(true).

.Enabled(bool)

public static InjectOptionsBase Enabled(this InjectOptionsBase options, bool enabled)
enabled
bool
required
true to activate behaviour injection; false to deactivate it for all calls regardless of injection rate. Useful for toggling chaos off at startup when reading configuration.

.EnabledWhen(Func<Context, CancellationToken, bool>)

public static InjectOptionsBase EnabledWhen(
    this InjectOptionsBase options,
    Func<Context, CancellationToken, bool> enabledWhen)
enabledWhen
Func<Context, CancellationToken, bool>
required
A synchronous delegate evaluated on every execution. Return true to allow chaos injection for that call; false to skip it. Common patterns include checking an environment variable, reading an in-memory feature flag, or inspecting ctx.OperationKey.

.InjectionRate(double)

public static InjectOptionsBase InjectionRate(this InjectOptionsBase options, double injectionRate)
injectionRate
double
required
A constant rate in [0, 1]. 0.0 means the behaviour is never injected; 1.0 means it runs before every call. Values outside this range throw ArgumentOutOfRangeException.

.InjectionRate(Func<Context, CancellationToken, double>)

public static InjectOptionsBase InjectionRate(
    this InjectOptionsBase options,
    Func<Context, CancellationToken, double> injectionRateProvider)
injectionRateProvider
Func<Context, CancellationToken, double>
required
A delegate evaluated on each execution to supply a dynamic injection rate. Enables graduated chaos rollouts — for example, increasing the rate from 1% to 10% over time without redeploying.

Async base (InjectOptionsAsyncBaseExtensions)

The async base provides the same .Enabled(), .Enabled(bool), and .InjectionRate(double) overloads as the sync base, plus async-delegate variants:

.EnabledWhen(Func<Context, CancellationToken, Task<bool>>)

public static InjectOptionsAsyncBase EnabledWhen(
    this InjectOptionsAsyncBase options,
    Func<Context, CancellationToken, Task<bool>> enabledWhen)
enabledWhen
Func<Context, CancellationToken, Task<bool>>
required
An async delegate returning Task<bool>. Use this when the enabled check must call an async service such as a remote feature-flag or chaos-orchestration API.

.InjectionRate(Func<Context, CancellationToken, Task<double>>)

public static InjectOptionsAsyncBase InjectionRate(
    this InjectOptionsAsyncBase options,
    Func<Context, CancellationToken, Task<double>> injectionRateProvider)
injectionRateProvider
Func<Context, CancellationToken, Task<double>>
required
An async delegate returning Task<double> in [0, 1]. Allows the injection rate to be sourced from a live configuration service on every execution.

Complete examples

Sync behaviour injection — cache invalidation chaos

using Polly;
using Polly.Contrib.Simmy;
using Polly.Contrib.Simmy.Behavior;

// Invalidate the cache before 10% of calls to simulate a cold-cache scenario
var behaviourPolicy = MonkeyPolicy.InjectBehaviour(with =>
    with.Behaviour((ctx, ct) =>
        {
            cache.Remove(ctx.OperationKey);
            logger.LogWarning("[chaos] cache invalidated for '{Op}'", ctx.OperationKey);
        })
        .InjectionRate(0.1)
        .EnabledWhen((ctx, ct) =>
            Environment.GetEnvironmentVariable("CHAOS_ENABLED") == "true"));

behaviourPolicy.Execute(
    () => FetchFromServiceWithCache("product-42"),
    new Context("product-42"));

Async behaviour injection — spurious event publishing

using Polly;
using Polly.Contrib.Simmy;
using Polly.Contrib.Simmy.Behavior;

var behaviourPolicy = MonkeyPolicy.InjectBehaviourAsync(with =>
    with.Behaviour(async (ctx, ct) =>
        {
            ct.ThrowIfCancellationRequested();
            // Publish a spurious duplicate event to test idempotency handling
            await eventBus.PublishAsync(
                new OrderCreatedEvent { OrderId = Guid.NewGuid() }, ct);
        })
        .InjectionRate(async (ctx, ct) =>
            await chaosSettings.GetBehaviourRateAsync(ct))
        .EnabledWhen(async (ctx, ct) =>
            await featureFlags.IsChaosEnabledAsync(ct)));

await behaviourPolicy.ExecuteAsync(
    async ct => await placeOrderAsync(order, ct),
    CancellationToken.None);

Combining behaviour injection with retry

using Polly;
using Polly.Contrib.Simmy;
using Polly.Contrib.Simmy.Behavior;

// Chaos policy that corrupts a downstream resource
var chaosPolicy = MonkeyPolicy.InjectBehaviourAsync(with =>
    with.Behaviour(async (ctx, ct) =>
        {
            await resourcePool.ForceCloseConnectionAsync(ct);
        })
        .InjectionRate(0.05)
        .Enabled());

// Retry policy that recovers from the disruption
var retryPolicy = Policy
    .Handle<Exception>()
    .RetryAsync(3);

// Wrap: chaos is innermost so retry can handle what chaos throws
var resilient = Policy.WrapAsync(retryPolicy, chaosPolicy);

await resilient.ExecuteAsync(
    async ct => await queryDatabaseAsync(ct),
    CancellationToken.None);

Build docs developers (and LLMs) love