AOP与Autofac和DynamicProxy2异常处理

本文关键字:DynamicProxy2 异常处理 Autofac AOP | 更新日期: 2023-09-27 18:08:11

我正试图集中管理某个方法的异常处理,但我似乎无法到达那里。

public class ExceptionInterceptor : IInterceptor
{
    private readonly Logger _logger;
    public ExceptionInterceptor(Logger logger)
    {
        _logger = logger;
        Measure.Configure(new StatsdConfig());
    }
    public void Intercept(IInvocation invocation)
    {
        try
        {
            invocation.Proceed();
            //if ((Task<System.Threading.Tasks.VoidTaskReturn>) invocation.ReturnValue.Status == "Failed")
            //{
            //    throw new Exception(invocation.ReturnValue.Exception[0]);
            //}
        }
        catch (Exception e)
        {
            var errorMessage =
                String.Format(
                    "An error occurred while retrieving fund data. Error Message: {0} Inner Exception: {1}",
                    e.Message, e.InnerException != null ? e.InnerException.Message : "<None>");
            _logger.Log(errorMessage);
            Measure.Counter("Exception", 1);
            Measure.Event("Exception", errorMessage);
            throw;
        }
    }

我把它连接在一个模块中,像这样:

builder.RegisterType<DataConsumer>().
            As<IConsumer<DataRequest>>().
            EnableInterfaceInterceptors().
            InterceptedBy(typeof(ExceptionInterceptor));
builder.RegisterType<ExceptionInterceptor>().AsSelf();
var loggingInterceptor = new LoggingInterceptor(Logger);
builder.Register(c => loggingInterceptor);

然而,当我在方法调用中抛出异常时,它不会作为抛出的异常冒泡到拦截器中,因此它永远不会进入catch块。是否有任何方法可以在拦截器中捕获被拦截方法的异常?

由于某些原因,我也无法访问invoke . returnvalue . status,因此无法测试是否有抛出异常以便重新抛出。

有谁能告诉我我在这里可能没有做什么吗?

Ta

AOP与Autofac和DynamicProxy2异常处理

由于信息不完整,我很难再现您的问题。例如,您注意到IConsumer<T>接口是MassTransit接口,但MassTransit中的接口不是通用的。它还特别提到接口应该是IoC容器的标记,这可能对您的连接有一些影响。

首先,让我们发布一个工作异常处理示例。为了实现自包含,我将创建一个IWorker<T>接口来代替IConsumer<T>和一个简单的实现:

public interface IWorker<T>
{
    bool DoWork(T message);
}
public class StringWorker : IWorker<string>
{
    public bool DoWork(string message)
    {
        throw new DivideByZeroException();
    }
}

现在我将创建一个简单的异常日志记录器,它将信息传输到控制台。

public class ExceptionLogger : IInterceptor
{
    private readonly TextWriter _output;
    public ExceptionLogger(TextWriter output)
    {
        _output = output;
    }
    public void Intercept(IInvocation invocation)
    {
        try
        {
            invocation.Proceed();
        }
        catch(Exception ex)
        {
            _output.WriteLine("Logged Exception: {0}", ex.Message);
            throw;
        }
    }
}

我可以把它连接起来,看看它是如何工作的,像这样:

var builder = new ContainerBuilder();
builder.RegisterInstance(Console.Out).As<TextWriter>();
builder.RegisterType<ExceptionLogger>();
builder.RegisterType<StringWorker>()
       .As<IWorker<string>>()
       .EnableInterfaceInterceptors()
       .InterceptedBy(typeof(ExceptionLogger));
var container = builder.Build();
var worker = container.Resolve<IWorker<string>>();
worker.DoWork("Test!");

当我运行这个时,我在控制台中看到,就在程序结束之前(带有未处理的异常-注意我的处理程序没有吞下它,只是记录了它):

Logged Exception: Attempted to divide by zero.

运行正常

我认为你的环境中可能有更多的东西会在这里造成麻烦。可能是你认为不相关但实际上很重要的事情。

一般调查事项:

    临时更新您的DataConsumer以立即在其中一个接口方法中抛出异常。在构建容器之后,解析a、IConsumer<DataRequest>并调用该接口方法。它会被记录吗?
  • 查看您期望看到日志记录发生的地方。你是解决和工作与IConsumer<DataRequest>或其他东西?它包装了接口方法,而不是对象类型,所以不是所有的方法都被覆盖。
  • 在拦截器中设置一个断点,看看是否有任何调用通过它。如果没有命中,它不会捕获异常。:)
  • 检查是否有其他异常处理策略或代码在运行。例如,有些人使用Enterprise Library异常处理块来处理异常,这可能会干扰您在这里的工作。
  • 我没有使用MassTransit,但检查看看是否有任何其他对象代理正在进行。(值得怀疑,但我知道我在使用像Glimpse这样的产品时遇到过这种情况,所以你最终会用代理包裹代理,这变得很有挑战性。)
  • 异常真的发生在你认为的地方吗?它可能在某个地方发生并被处理,而不是被代理包装。

基本上,将工件减少到尽可能小的集合,直到你可以看到它工作,然后慢慢扩大,直到你找到它坏的地方。我不知道这些是否适用于你的情况,但如果我在排除故障,这些都是我开始关注的事情。

但是…使用拦截器以AOP方式处理异常确实可以工作,所以引起挑战的是其他事情。

似乎不可能将目标代理中抛出的异常推到拦截器,因此我试图做的事情不起作用。我最终在异常发生的类上处理异常。

很失望,我没能让它按我想要的方式工作。