具有Autofac和DynamicProxy2异常处理的AOP

时间:2015-08-17 10:03:20

标签: c# aop autofac

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

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块。 有没有办法在拦截器中捕获截获的方法的异常?

由于某些原因,我也无法访问invocation.ReturnValue.Status,因此无法测试是否存在抛出异常以便重新抛出。

任何人都可以对我可能做的事情有所了解吗?

TA

2 个答案:

答案 0 :(得分:4)

由于信息不完整,我很难重现您的问题。例如,您注意到IConsumer<T>接口是MassTransit接口,但是{{3 }}。它还特别提到接口应该只是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以立即在其中一个接口方法中引发异常。构建容器后,解析一个IConsumer<DataRequest>并调用该接口方法。它会被记录吗?
  • 查看您希望发生日志记录的地方。您是否正在解决并使用IConsumer<DataRequest>或其他内容?它包装了接口方法,而不是对象类型,因此不是所有方法都被覆盖。
  • 在拦截器中设置一个断点,看看是否有任何调用都通过它。如果它没有被击中,它将不会捕获异常。 :)
  • 检查是否存在任何其他异常处理策略或代码。例如,有些人使用企业库异常处理块来处理异常,这可能会干扰您的工作。
  • 我没有使用过MassTransit,但检查是否还有其他对象代理正在进行中。 (令人怀疑,但我知道我已经遇到像Glimpse这样的产品,所以你最终会遇到代理人的代理,这会变得很有挑战性。)
  • 异常是否真的发生在你认为的地方?它可能正在发生并在某个未被代理包装的地方处理。

基本上,将工件减少到可能的最小值,直到看到它工作,然后慢慢展开,直到找到它发生故障的地方。我不知道这些是否适用于您的情况,但如果我进行故障排除,这些是我开始考虑的事情。

但是...... 使用拦截器以AOP方式进行异常处理确实有效,所以这是导致挑战的其他因素。

答案 1 :(得分:-1)

似乎将目标代理中抛出的异常推送到拦截器是不可能的,因此我试图做的事情并没有起作用。我最终在他们发生的课程中处理了异常。

失望我没有设法以我想要的方式工作。