3

我正在升级一个插件加载引擎.NET),它执行以下操作:

  1. 加载插件
  2. 将它们连接到适当的数据源
  3. 启动插件
  4. 显示结果

所有插件都实现相同的接口:IPlugin每个插件都在单独的BackGroundWorker. 所有这些BackgroundWorkers都由一个名为Host.

我的问题是Errors/Exceptions Handling. 引擎已经部署,我想找到一种优雅的方式来处理Errors/Exceptions插件运行时可能引发的问题。有些Exceptions被插件捕获,但不是全部。

我正在考虑一个单独的层,它可以捕获错误并为所有插件处理它们。

我想象了一种Context附加到每个Plugin包含它的进度级别(BackgroundWorker.ReportProgress)、它的状态、抛出的异常(使用RunWorkerCompletedEvent)但只有在BackgroundWorker停止之后才会抛出错误。我想在抛出异常时中断它。

我还认为面向方面的编程可能是一个好方法。我上网查了一下,找到了一些类似Spring.NET的框架。但不确定它是否适合我的情况。

[更新] 这里有更多的设计细节要求:

  • IPlugin 接口:调用AbstractEnvChecker

抽象环境检查器

该应用程序是一个富客户端应用程序。编译插件后,DLL加载生成的插件,并在一个简单的 Windows 窗体中向用户显示一个列表。然后用户选择要启动的插件并Plugin.DoWork()调用该方法。

以下是主机启动所选插件的方式:

void LaunchPlugin(AbstractEnvChecker p_Plugin)
{
    if (p_Plugin != null)
    {
        BackgroundWorker l_BackgroundWorker = new BackgroundWorker();
        l_BackgroundWorker.WorkerReportsProgress = true;
        l_BackgroundWorker.WorkerSupportsCancellation = true;

        l_BackgroundWorker.DoWork +=
            new DoWorkEventHandler(bw_DoWork);
        l_BackgroundWorker.ProgressChanged +=
            new ProgressChangedEventHandler(bw_ProgressChanged);
        l_BackgroundWorker.RunWorkerCompleted +=
            new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

        m_PluginByThreadMap.Add(l_BackgroundWorker, p_Plugin);
        l_BackgroundWorker.DoWork += p_Plugin.DoWork;
        l_BackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(l_BackgroundWorker_RunWorkerCompleted);
        l_BackgroundWorker.RunWorkerAsync(p_Plugin);
    }
}

AOP添加错误处理层是否合适?

4

2 回答 2

3

最简单的方法是将IPlugin.DoWork()方法包装在一个try/catch子句中。像这样的东西:

l_BackgroundWorker.DoWork += (o, e) => ExecutePlugin(o, e, p_plugin);

private void ExecutePlugin(object sender, DoWorkEventArgs e, IPlugin plugin)
{   
   try 
   { 
      plugin.DoWork(o, e);
   }
   catch (Exception e)
   {
      //do something with the error. disable the plugin maybe?
   }
}

如果这项工作,那么在我看来,仅将 Spring 用于错误处理有点矫枉过正。

您可以做的其他事情是抛出自定义异常(例如 a PluginException)并在您的应用程序中全局处理这些异常,这可以通过附加到: Application.ThreadExceptionAppDomain.CurrentDomain.UnhandledException事件来实现

于 2012-06-28T18:43:40.850 回答
1

Spring.net 使用动态编织,这基本上意味着在运行时 Spring.net aop 可以围绕方法调用包装异常处理程序。但是 Spring.net aop 需要一个接缝来定位它的拦截器。

如果您的插件应该加载到 UI 中,那么用户(可能)可以调用根本不通过主机或IPlugin接口的方法,这使得 spring.net aop 很难(如果不是不可能)拦截和包装异常处理程序。

如果您的主机是调用 的控制台应用程序或服务myPlugin.DoWork(),那么绝对可以使用 Spring.net aop 拦截插件抛出的任何异常。如果您可以提供更多详细信息(请参阅对您问题的评论),那么我可以向您展示如何做到这一点。

下面是一个使用 Spring.net AOP 代理插件实例并用拦截器包装它的示例,它捕获抛出的异常并将其委托回主机。请注意,您也可以在没有 AOP 的情况下执行此操作……这取决于您。

using System;
using AopAlliance.Intercept;
using NUnit.Framework;
using Spring.Aop.Framework;

namespace Aop
{

    [TestFixture]
    public class SimpleProxyFactoryTests
    {
        [Test]
        public void Main()
        {
            var host = new Host();

            var mp = new SimplePlugin(); 
            var pf = new ProxyFactory(mp);
            pf.AddAdvice(new DelegateToHostExceptionHandlingAdvice(host));

            var proxy = (IPlugin)pf.GetProxy();

            proxy.DoWork();
        }
    }

    public interface IPlugin
    {
        void DoWork();
    }

    public class Host
    {
        public void HandleExceptionFromPlugin(Exception ex)
        {
            Console.WriteLine("Handling exception: {0}", ex.Message);
        }
    }

    public class SimplePlugin : IPlugin
    {
        public void DoWork()
        {
            Console.WriteLine("Doing it and throwing an exception ... ");

            throw new ApplicationException("Oops!");
        }
    }

    public class DelegateToHostExceptionHandlingAdvice : IMethodInterceptor 
    {
        private readonly Host _host;

        public DelegateToHostExceptionHandlingAdvice(Host host)
        {
            _host = host;
        }

        public object Invoke(IMethodInvocation invocation)
        {
            try
            {
                return invocation.Proceed();
            }
            catch (Exception ex)
            {
                 _host.HandleExceptionFromPlugin(ex);
                return null; 
            }
        }
    }
}

讨论

我希望我已经向您展示了如何利用 aop 框架进行异常处理。正如塞巴斯蒂安在他的回答中提到的那样,将 Spring aop用于异常包装可能被认为是矫枉过正——我同意;将他的代码示例的简单性与我的复杂性进行比较。想象一下,向团队中的新开发人员解释其中之一。

当您将 Spring aop 与 Spring IOC 容器结合使用时,它开始“发光”。

于 2012-06-28T14:11:58.933 回答