我正在升级插件加载引擎(.NET
),其执行以下操作:
所有插件都实现相同的接口:IPlugin
,每个插件都在单独的BackGroundWorker
中启动。所有BackgroundWorkers
都由名为Host
的模块管理。
我的问题是Errors/Exceptions Handling
。该引擎已经部署,我想找到一种优雅的方法来处理插件运行时可能抛出的Errors/Exceptions
。一些Exceptions
被插件插入,但不是全部。
我正在考虑一个可以捕获错误的单独层,并为所有插件处理它们。
我想象一种Context
附加到每个Plugin
,其中包含其进度级别(BackgroundWorker.ReportProgress
),其状态,抛出异常(使用RunWorkerCompletedEvent
)但是只有在BackgroundWorker
停止后才会抛出错误。我想在抛出异常时中断它。
我还认为面向方面编程可能是一个很好的方法。我在网上看了一下,发现了像 Spring.NET 这样的框架。但不确定它是否适合我的情况。
[UPDATE] 以下是更多设计细节:
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
是否是添加错误处理层的合适解决方案?
答案 0 :(得分: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进行错误处理在我看来有点矫枉过正。
您可以执行的其他操作是抛出自定义异常(例如PluginException
)并在应用程序中全局处理这些异常,这可以通过附加到:
Application.ThreadException
和AppDomain.CurrentDomain.UnhandledException
次活动
答案 1 :(得分:1)
Spring.net使用动态编织,这基本上意味着在运行时Spring.net aop可以围绕方法调用包装异常处理程序。但Spring.net aop需要一个接缝来定位它的拦截器。
如果您的插件应该加载到UI中,那么用户(可能)可以调用不通过主机或IPlugin
接口的方法,这使得弹簧很难(如果不是不可能) .net aop to intercept and wrap exception handler。
如果你的主机是一个调用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框架来进行异常处理。作为Sebastian mentions in his answer,使用Spring aop 仅进行异常包装可能会被认为是矫枉过正 - 我同意;将他的代码示例的简单性与我的复杂性进行比较。想象一下,向团队中的新开发人员解释任何一个。
当你将它与Spring IOC容器结合使用时,Spring aop开始“闪耀”。