Обработка исключений: AOP против классической обработки?

Я работаю над механизмом загрузки плагинов (.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:

Приложение представляет собой 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);
    }
}
  1. Является AOP хорошее решение добавить слой обработки ошибок? Или я должен сохранить упомянутое решение и разработать его (идеи приветствуются)?
  2. Если AOP может быть хорошим решением, есть ли риск снижения производительности двигателя?
  3. Как можно решить эту проблему 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.

Другие вопросы по тегам