EasyCore.Polly 8.3.4

dotnet add package EasyCore.Polly --version 8.3.4
                    
NuGet\Install-Package EasyCore.Polly -Version 8.3.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EasyCore.Polly" Version="8.3.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EasyCore.Polly" Version="8.3.4" />
                    
Directory.Packages.props
<PackageReference Include="EasyCore.Polly" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EasyCore.Polly --version 8.3.4
                    
#r "nuget: EasyCore.Polly, 8.3.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EasyCore.Polly@8.3.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EasyCore.Polly&version=8.3.4
                    
Install as a Cake Addin
#tool nuget:?package=EasyCore.Polly&version=8.3.4
                    
Install as a Cake Tool

πŸ›‘οΈ EasyCore.Polly

EasyCore.Polly is a production-ready Polly resilience wrapper for .NET 8. Apply retry, timeout, circuit breaker, bulkhead, and fallback with [PollyConfig] on interface / class / method / MVC API. Standalone NuGet (does not depend on EasyCore.Invocation); stacks with Redis / Invocation via DI IAsyncInterceptor.

<p align="center"> <img src="https://raw.githubusercontent.com/RockyWang0521/EasyCore.Polly/master/png/EasyCoreLogo.png" alt="EasyCore Logo" width="120" /> </p>

.NET C# Polly Proxy License Version


🌍 Language


πŸ“š Table of Contents

Part I β€” Overview & Architecture

Part II β€” Getting Started

Part III β€” Policies & Production


1. 🎯 Positioning

EasyCore.Polly solves β€œdeclare resilience with attributes instead of hand-written Polly wraps”:

Pain point EasyCore.Polly approach
Hand-written Polly wraps everywhere [PollyConfig] on interface / class / method / API
Manual proxy registration AddEasyCorePolly + discovery
Fragile filesystem scanning Prefer AddAssemblyFrom<T>()
Broken Task<T> fallback Generic policies return degradation values
Library forces console logging Host owns logging configuration
Coupling to other AOP packages Independent; stack via DI IAsyncInterceptor

1.1 Design Principles

Principle Meaning
Low friction One extension method + one attribute to get running
Multi-level placement Interface type / interface method / class / method / ControllerΒ·Action
Interface proxy Castle DynamicProxy; APIs use IFilterFactory
Explicit assemblies Use AddAssemblyFrom<T>() in production
Composable policies Retry / Timeout / CB / Bulkhead / Fallback in one attribute
Package independence No reference to EasyCore.Invocation / Redis; stack via DI

2. πŸ—οΈ Architecture & Call Chain

2.1 Component diagram

architecture-en

2.2 Call sequence

[IOrderClient] ──► Castle Interface Proxy
                         β”‚
                         β–Ό
              PollyStandardInterceptor
                         β”‚
                         β–Ό
         PolicyCache ──► PolicyFactory
                         β”‚
                         β–Ό
              Fallback β†’ Retry β†’ Timeout
              β†’ CircuitBreaker β†’ Bulkhead
                         β”‚
                         β–Ό
                 OrderClient.Method()

3. πŸ“ Repository Layout

EasyCore.Polly/
β”œβ”€β”€ src/EasyCore.Polly/
β”‚   β”œβ”€β”€ Abstractions/     # Options, Attribute
β”‚   β”œβ”€β”€ Discovery/       # Assembly discovery
β”‚   β”œβ”€β”€ Hosting/         # DI extension entry
β”‚   β”œβ”€β”€ Interception/    # Castle interceptor
β”‚   └── Policies/        # Policy factory and cache
β”œβ”€β”€ demo/
β”‚   β”œβ”€β”€ PollyTest/                  # Shared sample services
β”‚   β”œβ”€β”€ WebApp.EasyCorePolly/       # ASP.NET Core + Swagger
β”‚   └── WinFormsApp.EasyCorePolly/  # WinForms
β”œβ”€β”€ tests/EasyCore.Polly.Tests/
β”œβ”€β”€ docs/svg/                       # README diagrams
└── png/EasyCoreLogo.png

Folders are for organization only; the public namespace stays EasyCore.Polly.


4. πŸ“¦ Installation

dotnet add package EasyCore.Polly

Requires .NET 8.


5. ⚑ Quick Start

5.1 Define an interface and implementation

using EasyCore.Polly;

public interface IOrderClient
{
    [PollyConfig(
        MaxRetry = 3,
        RetryIntervalSeconds = 1,
        TimeOutSeconds = 5,
        IsEnableCircuitBreaker = true,
        ExceptionsAllowedBeforeBreaking = 5,
        SecondsOfBreak = 30,
        ServiceDegradation = nameof(GetOrderFallbackAsync))]
    Task<string> GetOrderAsync(string id);

    Task<string> GetOrderFallbackAsync();
}

public sealed class OrderClient : IOrderClient
{
    public Task<string> GetOrderAsync(string id)
        => throw new HttpRequestException("upstream down");

    public Task<string> GetOrderFallbackAsync()
        => Task.FromResult("cached-or-default");
}

Services need an interface proxy. You can also put [PollyConfig] on Controller / Action (no global filter).

5.2 Register services

Either style works:

// Convenient: auto-scan candidate assemblies (demo / local)
builder.Services.AddEasyCorePolly();

// Production: scan an explicit assembly
builder.Services.AddEasyCorePolly(options =>
{
    options.AddAssemblyFrom<OrderClient>();
});

Resolve IOrderClient from DI β€” you get a Castle proxy that applies policies.


6. βš™οΈ Attributes & Options

6.0 Placement

Placement Hits Path
Interface type All methods on that interface Castle
Interface method That method only Castle
Implementation class Outward methods of the class Castle
Implementation method That method only Castle
Controller / Action That type or action MVC IFilterFactory
EasyCoreAppService contract / method Dynamic API action Convention merges interface attrs β†’ IFilterFactory

Resolution: impl method β†’ interface method β†’ class β†’ interface type. Default Order is 50 (typically outside cache).

MVC / Dynamic API and retry: ActionExecutionDelegate may run only once, so the filter path forces MaxRetry = 0 (timeout / circuit breaker / bulkhead / fallback still apply). Use Castle DynamicProxy for retries on local services. AspNetCore.Mvc remote clients stay interface-only DispatchProxy (not Castle-wrapped).

6.1 [PollyConfig] property reference

Property Meaning Default Icon
RateLimit Bulkhead max parallelization (0 = off). Not a per-second rate limiter. 0 🧱
RateQueuing Bulkhead max queued actions 0 πŸ“₯
MaxRetry Retry count (0 = off) 0 πŸ”
RetryIntervalSeconds Delay between retries 1 ⏱️
IsEnableCircuitBreaker Enable circuit breaker false ⚑
ExceptionsAllowedBeforeBreaking Failures before open 3 πŸ’₯
SecondsOfBreak Open duration (seconds) 5 ⏸️
TimeOutSeconds Timeout (0 = off) 0 βŒ›
ServiceDegradation Parameterless fallback method name on the same type null 🩹

6.2 EasyCorePollyOptions

API Meaning
AddAssemblyFrom<T>() Scan the assembly that contains T (production recommended)
AddAssembly(Assembly) Scan an explicit assembly
Assemblies Configured assembly list (empty β‡’ candidate scan)

7. 🧩 Policy Wrap Order

7.1 Visual wrap

policy-wrap-en

7.2 Execution order

Outermost β†’ innermost:

Fallback β†’ Retry β†’ Timeout β†’ CircuitBreaker β†’ Bulkhead β†’ method

This matches Polly WrapAsync composition used by EasyCore.Polly.

Policy Role Property
🩹 Fallback Degrade on final failure ServiceDegradation
πŸ” Retry Transient fault retries MaxRetry / RetryIntervalSeconds
βŒ› Timeout Prevent long hangs TimeOutSeconds
⚑ CircuitBreaker Stop cascading failures IsEnableCircuitBreaker, etc.
🧱 Bulkhead Cap concurrency / isolate faults RateLimit / RateQueuing

8. πŸ§ͺ Demo Projects

Project Description Scenario
demo/WebApp.EasyCorePolly ASP.NET Core + Swagger Polly samples + cross-stack /api/combo
demo/WinFormsApp.EasyCorePolly WinForms Desktop sample
demo/PollyTest Shared [PollyConfig] services Referenced by demos above

Cross-stack (host references Invocation + Redis; packages stay independent):

GET /api/demo           # A–F placement guide
GET /api/users/unstable # A interface-type [PollyConfig]
GET /api/orders         # B class-type
GET /api/reports/generate
GET /api/notify/send
GET /api/catalog/publish
GET /api/apiplacement/ping
GET /api/combo           # H three-package stack
dotnet test tests/EasyCore.Polly.Tests
dotnet run --project demo/WebApp.EasyCorePolly

9. βœ… Production Checklist

  • Call AddAssemblyFrom<T>() (do not rely on base-directory DLL scanning)
  • Expose services through interfaces (I{ClassName})
  • Configure logging in the host (console/file/OTLP) β€” the library does not add sinks
  • Tune circuit-breaker and timeout values for your SLOs
  • Prefer returning values from degradation methods for Task<T> APIs
  • Invalid [PollyConfig] values throw at first execution

10. ❓ FAQ

Q: Why must I use an interface?
A: For services, policies apply via Castle interface proxies. You can also put [PollyConfig] on Controller / Action (IFilterFactory).

Q: Why does MaxRetry not apply on Controller / EasyCoreAppService?
A: MVC filter next can execute only once; retrying would break the pipeline. PollyConfigActionFilter therefore clears MaxRetry on the filter path. Hitting a dynamic API directly (e.g. /api/Xxx/GetUnstableAsync) will not retry; MaxRetry applies on Castle DynamicProxy for local services. AspNetCore.Mvc Consumer remotes remain plain DispatchProxy (not Castle).

Q: Is RateLimit a per-second throttle?
A: No. It maps to Polly Bulkhead (max concurrent executions + optional queue). See the wrap diagram above.

Q: What can a fallback method return?
A: Task, Task<T>, or a synchronous T for generic methods. It must be parameterless and on the same type.

Q: Can I use this with EasyCore.Redis / EasyCore.Invocation?
A: Yes. They are independent NuGet packages. Each registers IAsyncInterceptor via TryAddEnumerable; proxies stack with GetServices. Do not use single-slot TryAdd<IAsyncInterceptor> or only the first package wins.

Q: How does this relate to Ocelot / API gateways?
A: EasyCore.Polly is in-process resilience. Gateways handle edge traffic; use both when appropriate.

Q: Polly v8?
A: This package targets Polly 7.2.x so the attribute model stays stable. Polly v8 pipelines are a separate migration.


11. πŸ“„ License

MIT

🀝 Contributing

  1. Fork and create a feature branch
  2. Add tests under tests/EasyCore.Polly.Tests
  3. Run dotnet test and dotnet build EasyCore.Polly.sln
  4. Open a pull request

Issues / PRs welcome πŸš€

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.