常见场景
July 19, 2026 · View on GitHub
本页汇集几个常见的横切逻辑拦截器:日志、缓存、参数校验、重试、性能监控。它们都基于 AbstractInterceptorAttribute,可以作为特性标注,也可以作为全局拦截器注册(见拦截器配置)。示例中读取异步返回值统一用 context.UnwrapAsyncReturnValue(),原因见异步拦截。
日志记录
记录方法进入、耗时与结果,异常时记录错误并重新抛出:
using System;
using System.Linq;
using System.Threading.Tasks;
using AspectCore.DynamicProxy;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
public class LoggingInterceptorAttribute : AbstractInterceptorAttribute
{
public override async Task Invoke(AspectContext context, AspectDelegate next)
{
var logger = context.ServiceProvider.GetService<ILogger<LoggingInterceptorAttribute>>();
var methodName = $"{context.ServiceMethod.DeclaringType.Name}.{context.ServiceMethod.Name}";
var parameters = string.Join(", ", context.Parameters.Select(p => p?.ToString() ?? "null"));
logger.LogInformation("[START] {Method}({Params})", methodName, parameters);
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
try
{
await next(context);
stopwatch.Stop();
var result = await context.UnwrapAsyncReturnValue();
logger.LogInformation("[END] {Method} in {Elapsed}ms. Result: {Result}",
methodName, stopwatch.ElapsedMilliseconds, result);
}
catch (Exception ex)
{
stopwatch.Stop();
logger.LogError(ex, "[ERROR] {Method} failed after {Elapsed}ms",
methodName, stopwatch.ElapsedMilliseconds);
throw;
}
}
}
ILogger通过服务定位器(context.ServiceProvider)获取。也可以改用属性注入([FromServiceContext]),前提是拦截器用AddTyped<T>()注册,见拦截器配置。
缓存
命中缓存时短路原方法,未命中则执行并回填。注意异步方法要按声明的返回类型重新包装返回值:
using System;
using System.Linq;
using System.Threading.Tasks;
using AspectCore.DynamicProxy;
using Microsoft.Extensions.Caching.Memory;
public class CacheInterceptorAttribute : AbstractInterceptorAttribute
{
private readonly int _durationSeconds;
public CacheInterceptorAttribute(int durationSeconds = 60) => _durationSeconds = durationSeconds;
public override async Task Invoke(AspectContext context, AspectDelegate next)
{
var cache = context.ServiceProvider.GetService(typeof(IMemoryCache)) as IMemoryCache;
var cacheKey = GetCacheKey(context);
if (cache.TryGetValue(cacheKey, out var cached))
{
// 命中缓存,短路原方法;异步方法需重新包装成 Task<T>
context.ReturnValue = Task.FromResult(cached);
return;
}
await next(context);
var result = await context.UnwrapAsyncReturnValue();
cache.Set(cacheKey, result, TimeSpan.FromSeconds(_durationSeconds));
}
private static string GetCacheKey(AspectContext context)
=> $"{context.ServiceMethod.DeclaringType.Name}.{context.ServiceMethod.Name}_{string.Join("_", context.Parameters)}";
}
短路和重新包装返回值的规则(
Task.FromResult/ValueTask等)见异步拦截。上例假设被缓存的方法返回Task<T>;同步方法直接赋context.ReturnValue = cached即可。
参数校验
在方法执行前检查参数,不满足条件直接抛异常:
using System;
using System.Threading.Tasks;
using AspectCore.DynamicProxy;
public class ValidationInterceptorAttribute : AbstractInterceptorAttribute
{
public override async Task Invoke(AspectContext context, AspectDelegate next)
{
foreach (var param in context.Parameters)
{
if (param == null)
{
throw new ArgumentException("Parameters cannot be null");
}
}
await next(context);
}
}
需要基于
DataAnnotations特性的声明式校验时,用AspectCore.Extensions.DataAnnotations,见数据校验。
重试
对可能瞬时失败的方法自动重试,超过上限后抛出最后一次异常:
using System;
using System.Threading.Tasks;
using AspectCore.DynamicProxy;
public class RetryInterceptorAttribute : AbstractInterceptorAttribute
{
private readonly int _maxRetries;
private readonly int _delayMs;
public RetryInterceptorAttribute(int maxRetries = 3, int delayMs = 1000)
{
_maxRetries = maxRetries;
_delayMs = delayMs;
}
public override async Task Invoke(AspectContext context, AspectDelegate next)
{
var retryCount = 0;
while (true)
{
try
{
await next(context);
return;
}
catch (Exception ex) when (retryCount < _maxRetries)
{
retryCount++;
Console.WriteLine($"Retry {retryCount}/{_maxRetries} after: {ex.Message}");
await Task.Delay(_delayMs);
}
}
}
}
性能监控
统计方法耗时,无论成功失败都记录(放在 finally):
using System;
using System.Threading.Tasks;
using AspectCore.DynamicProxy;
public class PerformanceInterceptorAttribute : AbstractInterceptorAttribute
{
public override async Task Invoke(AspectContext context, AspectDelegate next)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
try
{
await next(context);
}
finally
{
stopwatch.Stop();
var methodName = $"{context.ServiceMethod.DeclaringType.Name}.{context.ServiceMethod.Name}";
Console.WriteLine($"[PERF] {methodName} took {stopwatch.ElapsedMilliseconds}ms");
// 也可上报到监控系统:Metrics.Record(methodName, stopwatch.ElapsedMilliseconds);
}
}
}
组合多个拦截器
多个拦截器命中同一方法会组成拦截器链,用 Order 控制顺序(越小越靠外)。既可以叠加特性,也可以混用全局拦截器:
public interface IOrderService
{
[Logging]
[Performance]
Task<Order> GetOrderAsync(int id);
}
Order、AllowMultiple 的语义见拦截器基础。