Обработка исключений: AOP против классической обработки?
Я работаю над механизмом загрузки плагинов (.NET
), его основные роли:
- Загрузка плагинов
- Подключение их к соответствующему источнику данных
- Запуск плагинов
- Отображение результатов
Все плагины реализуют один и тот же интерфейс: IPlugin
и каждый плагин запускается в отдельном BackGroundWorker
... все BackgroundWorkers
управляются модулем под названием Host
,
Проблема заключается в Errors/Exceptions Handling
, решение уже развернуто, и я хочу найти подходящее решение для лечения Errors/Exceptions
это может произойти, когда плагины работают. Немного Exceptions
ловятся в плагинах но не все.
Мне нужна отдельная система, способная отлавливать ошибки и обрабатывать их для всех плагинов (я не могу угадать бизнес-обработку каждого плагина...).
Я думал сделать вид Context
прикреплен к каждому Plugin
которые содержат его уровень прогресса (BackgroundWorker.ReportProgress
), его статус, ошибки произошли при его запуске (используя RunWorkerCompletedEvent
) ... Но проблема в том, что, например, ошибки выбрасываются только после BackgroundWorker
останавливается, я хочу прервать его при возникновении ошибки и попытаться лечить...
Я подумал о другом решении: Аспектно-ориентированное программирование. Я посмотрел в сети, и я нашел некоторые рамки, такие как Spring.NET... Но я не уверен, может ли это быть полезным в моем случае..
Вот более подробная информация, касающаяся ваших комментариев:
- Интерфейс IPlugin: называется
AbstractEnvChecker
:
Приложение представляет собой Rich Client App, после разработки и копирования плагинов загружаются все библиотеки 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
хорошее решение добавить слой обработки ошибок? Или я должен сохранить упомянутое решение и разработать его (идеи приветствуются)? - Если
AOP
может быть хорошим решением, есть ли риск снижения производительности двигателя? - Как можно решить эту проблему
AOP
? Где он может перехватить двигатель?
2 ответа
Самый простой способ - просто обернуть 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
События
Spring.net использует динамическое переплетение, что в основном означает, что во время выполнения Spring.net aop может обернуть обработчики исключений вокруг вызовов методов. Но Spring.net aop нужен шов для позиционирования перехватчика.
Если ваши плагины должны быть загружены в пользовательский интерфейс, то пользователь (вероятно) может вызывать методы, которые не проходят через хост или IPlugin
интерфейс вообще, что затрудняет (если не невозможно) для Spring.net aop перехватывать и оборачивать обработчики исключений.
Если ваш хост - это консольное приложение или служба, которая вызывает myPlugin.DoWork()
тогда, безусловно, можно перехватывать любые исключения, генерируемые плагином, используя Spring.net aop. Если бы вы могли предоставить немного больше подробностей (см. Комментарии к вашему вопросу), то я могу показать вам, как это сделать.
Ниже приведен пример, в котором SpringOP 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.