能否在Azure Function中用Autofac和DynamicProxy实现AOP日志?
根据你提供的代码和场景,拦截器Intercept()方法未触发通常是代理生成失败、注册配置错误或者类/方法不符合动态代理要求导致的。下面是一步步的排查和解决思路:
1. 修正被拦截方法的访问修饰符(最可能的原因)
Castle DynamicProxy的类拦截(通过EnableClassInterceptors())要求被拦截的方法必须是virtual的——因为动态代理是通过生成子类并重写父类方法来实现拦截的。你的TestIt类中CallMe()和CallMeWithParameter()都没有标记为virtual,这会直接导致代理无法拦截这些方法:
[Intercept(typeof(CallLogger))] // 这里建议直接指定拦截器实现类,而非自定义接口 public class TestIt : ITestIt { public string Name { get; set; } // 改为virtual public virtual string CallMe() { return "Dependency Injection Test method had been called..."; } // 改为virtual public virtual string CallMeWithParameter(string parameter) { return "Dependency Injection Test method had been called with [" + parameter + "]"; } }
如果你不想修改方法的virtual修饰符,可以改用接口拦截:在注册时使用EnableInterfaceInterceptors(),这种方式不需要方法是虚的,因为代理是基于接口生成的:
// 在ServicesModule的Load方法中替换注册代码 builder.RegisterType<TestIt>().As<ITestIt>().EnableInterfaceInterceptors();
2. 修正拦截器特性的目标类型
你的代码中使用了[Intercept(typeof(ICallLogger))],但Autofac的动态代理需要拦截器是Castle.DynamicProxy.IInterceptor的实现类。虽然你的CallLogger实现了IInterceptor,但ICallLogger是自定义接口,Autofac无法识别它作为拦截器类型。建议直接指定拦截器的实现类:
[Intercept(typeof(CallLogger))] public class TestIt : ITestIt { ... }
或者,你也可以在注册时通过代码指定拦截器,而不用特性:
builder.RegisterType<TestIt>().As<ITestIt>() .EnableClassInterceptors() .InterceptedBy(typeof(CallLogger));
3. 确认Autofac与DynamicProxy的版本兼容性
你提到将Autofac降级到了4.0.1,需要确保Autofac.Extras.DynamicProxy和Castle.Core的版本与Autofac 4.x兼容:
- Autofac 4.0.1 对应
Autofac.Extras.DynamicProxy4.0.0左右版本 Castle.Core版本建议在4.0.x到4.4.x之间(避免版本过高导致兼容性问题)
4. 验证Azure Function的Autofac容器配置是否正确
Azure Function的DI配置和普通.NET Core应用不同,需要确保你正确替换了默认的DI容器:
- 创建
Startup类并继承FunctionsStartup:
using Microsoft.Azure.Functions.Extensions.DependencyInjection; using Autofac; [assembly: FunctionsStartup(typeof(YourNamespace.Startup))] namespace YourNamespace { public class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { var containerBuilder = new ContainerBuilder(); containerBuilder.RegisterModule<ServicesModule>(); // 替换为Autofac的服务提供工厂 builder.UseAutofacServiceProviderFactory(() => containerBuilder.Build()); } } }
- 确保你的Function类通过构造函数注入
ITestIt,而不是直接实例化:
public class YourFunction { private readonly ITestIt _testIt; // 构造函数注入 public YourFunction(ITestIt testIt) { _testIt = testIt; } [FunctionName("YourFunction")] public async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); // 调用方法,此时应该触发拦截器 var result = _testIt.CallMe(); return new OkObjectResult(result); } }
5. 检查拦截器的构造函数注入是否正常
你的CallLogger有两个构造函数,确保Autofac能正确解析依赖(Azure Function中更推荐使用ILogger而非TraceWriter)。你可以修改CallLogger适配更现代的日志注入:
public class CallLogger : IInterceptor { private readonly ILogger _logger; public CallLogger(ILogger<CallLogger> logger) { _logger = logger; } public void Intercept(IInvocation invocation) { _logger.LogInformation($"Calling method: {invocation.Method.Name}"); invocation.Proceed(); _logger.LogInformation($"Method {invocation.Method.Name} returned: {invocation.ReturnValue}"); } }
6. 验证是否获取到了代理实例
在Function中,你可以打印注入的ITestIt实例的类型名称,确认是否是动态代理类:
public YourFunction(ITestIt testIt) { _testIt = testIt; // 打印类型名称,如果是代理类,会包含"Proxy"后缀 Console.WriteLine(_testIt.GetType().FullName); }
如果输出的是YourNamespace.TestIt而非代理类名称,说明代理没有生成,需要回到前面的步骤检查注册配置。
内容的提问来源于stack exchange,提问作者Adrian Warbeck

