PoliNorError.Extensions.DependencyInjection
0.0.8
dotnet add package PoliNorError.Extensions.DependencyInjection --version 0.0.8
NuGet\Install-Package PoliNorError.Extensions.DependencyInjection -Version 0.0.8
<PackageReference Include="PoliNorError.Extensions.DependencyInjection" Version="0.0.8" />
<PackageVersion Include="PoliNorError.Extensions.DependencyInjection" Version="0.0.8" />
<PackageReference Include="PoliNorError.Extensions.DependencyInjection" />
paket add PoliNorError.Extensions.DependencyInjection --version 0.0.8
#r "nuget: PoliNorError.Extensions.DependencyInjection, 0.0.8"
#:package PoliNorError.Extensions.DependencyInjection@0.0.8
#addin nuget:?package=PoliNorError.Extensions.DependencyInjection&version=0.0.8
#tool nuget:?package=PoliNorError.Extensions.DependencyInjection&version=0.0.8
The PoliNorError.Extensions.DependencyInjection package extends PoliNorError library to provide integration with Microsoft Dependency Injection.
⚡ Quick Start
Get up and running in 3 simple steps:
1. Register policies in DI
// Program.cs
services.AddPoliNorError(
Assembly.GetExecutingAssembly());
This scans your assembly for all IPolicyBuilder<> implementations and wires up IPolicy<T> automatically.
2. Define your policy builders
public class SomePolicyBuilder : IPolicyBuilder<SomePolicyBuilder>
{
private readonly ILogger<SomePolicyBuilder> _logger;
public SomePolicyBuilder(ILogger<SomePolicyBuilder> logger)
{
_logger = logger;
}
public IPolicyBase Build()
{
return new RetryPolicy(3)
.WithPolicyName("SomeRetryPolicy")
.WithErrorProcessor(new RetryLoggingErrorProcessor(_logger))
.WithWait(new TimeSpan(0, 0, 3))
.AddPolicyResultHandler(pr =>
{
Log.PolicyFailedToHandleException(
_logger,
pr.UnprocessedError,
pr.PolicyName);
});
}
}
Another example:
public class AnotherPolicyBuilder : IPolicyBuilder<AnotherPolicyBuilder>
{
private readonly ILogger<AnotherPolicyBuilder> _logger;
public AnotherPolicyBuilder(ILogger<AnotherPolicyBuilder> logger)
{
_logger = logger;
}
public IPolicyBase Build()
{
return new RetryPolicy(2)
.WithPolicyName("AnotherRetryPolicy")
.WithErrorProcessor(new RetryLoggingErrorProcessor(_logger))
.WithWait(new TimeSpan(0, 0, 1))
.AddPolicyResultHandler(pr =>...);
}
}
, where RetryLoggingErrorProcessor:
public class RetryLoggingErrorProcessor : ErrorProcessor
{
private readonly ILogger _logger;
public RetryLoggingErrorProcessor(ILogger logger)
{
_logger = logger;
}
public override void Execute(Exception error,
ProcessingErrorInfo? catchBlockProcessErrorInfo = null,
CancellationToken token = default)
{
_logger.LogError(error,
"An error occurred while doing work on {Attempt} attempt.",
catchBlockProcessErrorInfo.GetAttemptCount());
}
}
3. Consume policies in your services
public class Worker
{
private readonly IPolicy<SomePolicyBuilder> _somePolicy;
private readonly IPolicy<AnotherPolicyBuilder> _anotherPolicy;
public Worker(IPolicy<SomePolicyBuilder> somePolicy,
IPolicy<AnotherPolicyBuilder> anotherPolicy)
{
_somePolicy = somePolicy;
_anotherPolicy = anotherPolicy;
}
public async Task DoWorkAsync(CancellationToken token)
{
await _somePolicy.HandleAsync(MightThrowAsync, token);
await _anotherPolicy.HandleAsync(MightThrowAsync, token);
}
private async Task MightThrowAsync(CancellationToken token)
{
await Task.Delay(100, token);
throw new SomeException("Something went wrong.");
}
}
✅ That’s it!
- Builders encapsulate configuration.
- Consumers inject
IPolicy<T>and just use it. - DI takes care of wiring everything together.
✨ Key Features
IPolicyBuilder<TBuilder>
- Implemented only in your builders.
- A builder abstraction for creating policies.
- Encapsulates configuration (retry count, wait strategy, error processors, etc.).
- Registered automatically into DI via assembly scanning.
IPolicy<T>
- Consumed only in your services.
- A closed generic wrapper that represents a policy built by a specific builder.
- Resolved directly from DI, giving consumers a type-safe handle to the correct policy.
- Internally backed by
ProxyPolicy<T>which delegates to the builder’sBuild()result.
Automatic DI Registration
AddPoliNorError()scans assemblies for allIPolicyBuilder<>implementations.- Registers them and wires up
IPolicy<T>→ProxyPolicy<T>automatically.
Keyed Services Support
AddKeyedPolicyregisters policies under string keys (builder-based or lambda-based).- Resolved via
[FromKeyedServices]orGetRequiredKeyedService<IPolicy>.
Policy Registry
AddPolicyRegistryopt-in registration for resolution viaIPolicyRegistry.GetPolicy<TBuilder>().- Keeps constructors clean when a consumer needs many policies.
🧩 How It Works
- You create builder classes that implement
IPolicyBuilder<TBuilder>. AddPoliNorErrorregisters the open generic mappingIPolicy<> -> ProxyPolicy<>.- When a consumer requests
IPolicy<TBuilder>, DI resolvesProxyPolicy<TBuilder>. - The proxy calls the builder’s
Build()method to produce the actual policy. - All calls (
Handle,HandleAsync, etc.) are delegated to the built policy.
✅ Benefits
- Type-safe DI: No string keys or manual lookups.
- Separation of concerns: Builders configure, consumers execute.
- Discoverable: Constructor injection makes dependencies explicit.
- Testable: Swap out builders or inject fake policies in tests.
- Extensible: Add new PoliNorError policies by just adding new builders.
🔑 Keyed Services Support (AddKeyedPolicy)
Register policies under string keys using the standard .NET keyed services API.
Builder-based overload — reuses the existing IPolicy<TBuilder> pipeline:
services
.AddPoliNorError(Assembly.GetExecutingAssembly())
// The same builder under two keys, with independent lifetimes
.AddKeyedPolicy<SomePolicyBuilder>("retry-primary")
.AddKeyedPolicy<SomePolicyBuilder>("retry-relaxed", ServiceLifetime.Singleton);
Lambda-based overload — ad-hoc policy without a builder class:
services.AddKeyedPolicy("orders-policy",
sp => new RetryPolicy(2)
.WithPolicyName("OrdersPolicy")
.WithErrorProcessor(new RetryLoggingErrorProcessor(sp.GetRequiredService<ILogger<Program>>())),
ServiceLifetime.Singleton);
Consume keyed policies via [FromKeyedServices] or GetRequiredKeyedService:
public class Worker
{
public Worker([FromKeyedServices("retry-primary")] IPolicy primaryPolicy) { }
public async Task DoWorkAsync(IServiceProvider sp, CancellationToken token)
{
var ordersPolicy = sp.GetRequiredKeyedService<IPolicy>("orders-policy");
// ...
}
}
✅ Use when: you need multiple named variants of the same builder, config-driven policy selection, or ad-hoc policies.
🗂 Policy Registry (AddPolicyRegistry)
Register policies for resolution through IPolicyRegistry — useful when a consumer needs many policies (e.g., a resilience orchestrator) and constructor bloat becomes a problem.
services
.AddPoliNorError(Assembly.GetExecutingAssembly())
.AddPolicyRegistry(registry =>
{
registry.AddPolicy<SomePolicyBuilder>();
registry.AddPolicy<AnotherPolicyBuilder>();
registry.AddPolicy<ThirdPolicyBuilder>();
});
Resolve policies by builder type:
public class Worker
{
private readonly IPolicyBase _somePolicy;
private readonly IPolicyBase _anotherPolicy;
private readonly IPolicyBase _thirdPolicy;
public Worker(IPolicyRegistry policyRegistry)
{
_somePolicy = policyRegistry.GetPolicy<SomePolicyBuilder>();
_anotherPolicy = policyRegistry.GetPolicy<AnotherPolicyBuilder>();
_thirdPolicy = policyRegistry.GetPolicy<ThirdPolicyBuilder>();
}
// ...
}
✅ Use when: a consumer needs 5+ policies, or you need dynamic policy selection at runtime.
🔥 Advanced Usage: Separation of Concerns with Configurators and Builders
For more complex scenarios, PoliNorError.Extensions.DependencyInjection supports an advanced pattern that separates policy creation from policy configuration.
Key Concepts:
PolicyConfigurator<TPolicy>— an abstract base class for encapsulating cross‑cutting configuration logic (logging, enrichment, etc.).PolicyBuilder<TPolicy, TConfigurator, TBuilder>— an abstract base class that encapsulates policy creation and optional configurator wiring.
✅ Inheriting from PolicyConfigurator<TPolicy>
Create a subclass of PolicyConfigurator<TPolicy> and override the Configure method, where TPolicy is a policy from PoliNorError library.
Inheritors of PolicyConfigurator are automatically resolved from DI.
✅ Inheriting from PolicyBuilder<TPolicy, TConfigurator, TBuilder>
Create a subclass of PolicyBuilder<TPolicy, TConfigurator, TBuilder> and override the CreatePolicy method, where TPolicy is a policy from PoliNorError library, and TConfigurator inherits from PolicyConfigurator<TPolicy>. TBuilder is the concrete builder type itself (self-referential CRTP parameter).
✅ When to Use This Pattern
Use custom builders + configurators when:
- You want policy creation and policy configuration to be separate concerns.
- You want to reuse the same configurator across multiple builders.
- You want to keep your builder classes minimal and declarative.
🧱 Example: Retry Policy With a Custom Configurator
public class RetryPolicyConfigurator : PolicyConfigurator<RetryPolicy>
{
private readonly ILoggerFactory _loggerFactory;
public RetryPolicyConfigurator(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}
public override void Configure(RetryPolicy policy)
{
var logger = _loggerFactory.CreateLogger(policy.PolicyName);
policy.WithErrorProcessor(new RetryLoggingErrorProcessor(logger))
.AddPolicyResultHandler(pr =>
{
Log.PolicyFailedToHandleException(
logger,
pr.UnprocessedError,
pr.PolicyName);
});
}
}
This configurator:
- Receives dependencies via DI (here:
ILoggerFactory) - Adds a logging error processor to the policy
- Uses the policy name to create a dedicated logger
public class SomePolicyBuilder : PolicyBuilder<RetryPolicy, RetryPolicyConfigurator, SomePolicyBuilder>
{
protected override RetryPolicy CreatePolicy() =>
new RetryPolicy(3, retryDelay: ConstantRetryDelay.Create(new TimeSpan(0, 0, 3)))
.WithPolicyName("SomeRetryPolicy");
}
This builder:
- Creates a
RetryPolicywith a fixed delay. - Assigns a policy name (used later by the configurator).
- Delegates configuration to
RetryPolicyConfigurator.
Once created, the configurator (a subclass of PolicyConfigurator) can be shared across multiple builders:
public class AnotherPolicyBuilder : PolicyBuilder<RetryPolicy, RetryPolicyConfigurator, AnotherPolicyBuilder>
{
protected override RetryPolicy CreatePolicy() =>
new RetryPolicy(2, retryDelay: ConstantRetryDelay.Create(new TimeSpan(0, 0, 1)))
.WithPolicyName("AnotherRetryPolicy");
}
✅ Benefits of this approach
- Single Responsibility Principle: Each class has one clear responsibility
- Reusability: Configurators can be shared across multiple policy builders
- Testability: Configurators and builders can be tested independently
- Maintainability: Changes to configuration logic don't affect creation logic and vice versa
🏆 Samples
See samples folder for concrete example.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.DependencyInjection (>= 10.0.13)
- PoliNorError (>= 2.24.30)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.0.8 | 132 | 10/15/2026 |
| 0.0.5 | 162 | 6/11/2026 |
| 0.0.3.2 | 157 | 2/13/2026 |
| 0.0.3 | 492 | 12/11/2025 |
| 0.0.3-preview | 300 | 11/14/2025 |
| 0.0.2-preview | 229 | 10/16/2025 |
| 0.0.1-preview | 228 | 10/16/2025 |