@@ -35,25 +35,31 @@ Parameters are added as span tags. Exceptions set error status + add exception e
3535
3636### [ Timing] — lightweight metrics
3737
38- Records method execution time via a static event. No external dependencies .
38+ Records method execution time. Supports DI with ` ITimingRecorder ` or static event fallback .
3939
4040``` csharp
41- // Subscribe to timing events:
41+ // Option 1 — DI (recommended):
42+ builder .Services .AddSingleton <ITimingRecorder , MyMetricsRecorder >();
43+ builder .Services .AddTransient <TimingHandler >();
44+
45+ // Option 2 — static event (no DI needed):
4246TimingHandler .OnTimingRecorded += (className , methodName , elapsedMs ) =>
43- {
4447 Console .WriteLine ($" {className }.{methodName }: {elapsedMs }ms" );
45- // or: myMetrics.RecordHistogram("method_duration", elapsedMs, methodName);
46- };
4748
4849[Timing ]
4950public Order GetOrder (int id ) { .. . }
5051```
5152
5253### [ SuppressException] — exception observability
5354
54- Fires an event when a method throws. Useful for recording exceptions without changing control flow .
55+ Fires when a method throws. Supports DI with ` IExceptionObserver ` or static event fallback .
5556
5657``` csharp
58+ // Option 1 — DI:
59+ builder .Services .AddSingleton <IExceptionObserver , MyExceptionLogger >();
60+ builder .Services .AddTransient <SuppressExceptionHandler >();
61+
62+ // Option 2 — static event:
5763SuppressExceptionHandler .OnExceptionSuppressed += (ctx , ex ) =>
5864 logger .LogWarning (ex , " Exception in {Method}" , ctx .MethodName );
5965
@@ -72,23 +78,34 @@ public Order GetOrder(int id) { ... }
7278public async Task < Order > GetOrderAsync (int id ) { .. . }
7379```
7480
75- ### [ Cache] — in-memory caching
81+ ### [ Cache] — caching
82+
83+ Supports DI with ` IAspectCache ` (Redis, IMemoryCache, etc.) or built-in in-memory fallback.
7684
7785``` csharp
86+ // Option 1 — DI with custom cache:
87+ builder .Services .AddSingleton <IAspectCache , RedisAspectCache >();
88+ builder .Services .AddTransient <CacheHandler >();
89+
90+ // Option 2 — built-in in-memory (no DI needed):
7891 [Cache (DurationSeconds = 60 )]
7992public Order GetOrder (int id ) { .. . }
80- // First call: executes, caches result
81- // Same params again: returns cached (no execution)
8293
83- // Invalidate:
94+ // Invalidate (fallback cache only) :
8495CacheHandler .Invalidate (" GetOrder" );
8596CacheHandler .ClearAll ();
8697```
8798
8899### [ Authorize] — authorization check
89100
101+ Supports DI with ` IAuthorizationChecker ` or static delegate fallback.
102+
90103``` csharp
91- // Setup once at startup:
104+ // Option 1 — DI (recommended):
105+ builder .Services .AddScoped <IAuthorizationChecker , MyAuthChecker >();
106+ builder .Services .AddTransient <AuthorizeHandler >();
107+
108+ // Option 2 — static delegate:
92109AuthorizeHandler .AuthorizationCheck = (ctx , policy ) =>
93110 currentUser .HasPermission (policy ?? ctx .MethodName );
94111
@@ -99,6 +116,53 @@ public void DeleteOrder(int id) { ... } // checks "DeleteOrder" permission
99116public void PurgeAllData () { .. . } // checks "Admin" permission
100117```
101118
119+ ## Dependency Injection
120+
121+ All aspect handlers support DI. Register handlers and their dependencies in your DI container, then wire up ` AspectServiceProvider ` :
122+
123+ ``` csharp
124+ var builder = WebApplication .CreateBuilder (args );
125+
126+ // Register handler dependencies
127+ builder .Services .AddSingleton <ITimingRecorder , PrometheusTimingRecorder >();
128+ builder .Services .AddScoped <IAuthorizationChecker , JwtAuthChecker >();
129+ builder .Services .AddSingleton <IAspectCache , RedisAspectCache >();
130+
131+ // Register handlers themselves
132+ builder .Services .AddTransient <TimingHandler >();
133+ builder .Services .AddTransient <AuthorizeHandler >();
134+ builder .Services .AddTransient <CacheHandler >();
135+
136+ var app = builder .Build ();
137+
138+ // Enable DI for all aspect handlers (one line):
139+ AspectServiceProvider .ServiceProvider = app .Services ;
140+ ```
141+
142+ When ` AspectServiceProvider.ServiceProvider ` is set, generated code resolves handlers from DI. If DI is not configured or a handler is not registered, it falls back to ` new ` (backward compatible).
143+
144+ Custom handlers also benefit — just add constructor parameters:
145+
146+ ``` csharp
147+ public class MetricsHandler : IAspectHandler
148+ {
149+ private readonly ILogger <MetricsHandler > _logger ;
150+ private readonly IMetrics _metrics ;
151+
152+ public MetricsHandler (ILogger <MetricsHandler > logger , IMetrics metrics )
153+ {
154+ _logger = logger ;
155+ _metrics = metrics ;
156+ }
157+
158+ public void OnBefore (AspectContext ctx ) { }
159+ public void OnAfter (AspectContext ctx )
160+ => _metrics .RecordHistogram (" method_duration" , ctx .ElapsedMilliseconds , ctx .MethodName );
161+ public void OnException (AspectContext ctx , Exception ex )
162+ => _logger .LogError (ex , " Failed {Method}" , ctx .MethodName );
163+ }
164+ ```
165+
102166## Custom Aspects
103167
104168### Sync handler (works on sync + async methods)
0 commit comments