АОП Разделение сквозных проблем - PullRequest
1 голос
/ 08 апреля 2011

Я пытаюсь начать использовать преимущества Аспектно-ориентированного программирования для повторяющихся задач. Я не уверен, как идти о разделении проблем. Я использую C # и для AOP я использую Castle.DynamicProxy (используя функцию InterofptedBy от Autofac), но я надеюсь, что ответ на этот вопрос может быть достаточно общим советом, чтобы применить его и к другим решениям AOP (возможно, вы можете убедить мне перейти на другое решение АОП).

Например, у меня есть что-то вроде следующего перехватчика, который перехватывает все вызовы методов класса. В настоящее время у него есть две проблемы: когда вызывается метод, (1) измеряют, сколько времени занял вызов, и (2) записывают имя метода до и после его вызова.

public class TimeLoggingInterceptor : IInterceptor
{
    private ILog m_Log;
    public TimeLoggingInterceptor(ILog log)
    {
        m_Log = log;
    }
    public void Intercept(IInvocation invocation)
    {
        // Logging concerns
        string fullMethodName = invocation.TargetType.Name + "." + invocation.MethodInvocationTarget.Name;
        m_Log.Debug(fullMethodName + " started.");

        // Timing concerns
        DateTime beforeStamp = DateTime.UtcNow;

        // Call method
        invocation.Proceed();

        // Timing concerns
        DateTime afterStamp = DateTime.UtcNow;
        TimeSpan callTime = afterStamp - beforeStamp;

        // Logging concerns
        m_Log.Debug(fullMethodName + " finished. Took " + callTime.TotalMilliseconds + "ms.");
    }
}

У меня огромное чувство, что вопросы синхронизации здесь (измерение того, сколько времени потребовался вызов метода) должны быть отделены от проблем регистрации (запись в файл журнала), потому что ... ну, это отдельные проблемы. Я думаю сделать что-то подобное, но я не уверен, как приблизиться к порядку или куда поместить переменную callTime :

public class TimingInterceptor : IInterceptor
{
    private static ThreadLocal<TimeSpan> callTime = new ThreadLocal<TimeSpan>();
    public static TimeSpan CallTime
    {
        get
        {
            if (!callTime.IsValueCreated) throw new InvalidOperationException("callTime was never set");
            return callTime.Value;
        }
    }

    public void Intercept(IInvocation invocation)
    {
        // Timing concerns
        DateTime beforeStamp = DateTime.UtcNow;

        // Call method
        invocation.Proceed();

        // Timing concerns
        DateTime afterStamp = DateTime.UtcNow;
        callTime.Value = afterStamp - beforeStamp;
    }
}
public class LoggingInterceptor : IInterceptor
{
    private ILog m_Log;
    public LoggingInterceptor(ILog log)
    {
        m_Log = log;
    }

    public void Intercept(IInvocation invocation)
    {
        // Logging concerns
        string fullMethodName = invocation.TargetType.Name + "." + invocation.MethodInvocationTarget.Name;
        m_Log.Debug(fullMethodName + " started.");

        // Call method
        invocation.Proceed();

        // Logging concerns
        m_Log.Debug(fullMethodName + " finished. Took " + TimingInterceptor.CallTime.TotalMilliseconds + "ms.");
    }
}

По сути, я думаю, что здесь должно произойти следующее: 1011 * TimingInterceptor должен непосредственно перехватить методы, а затем LoggingInterceptor необходимо обернуть это.

Какие подходы используют люди, чтобы убедиться, что эти проблемы произойдут в правильном порядке? Должен ли я перехватывать цепочки перехватчиков, используя метод перехвата LogingInterceptor методом перехвата TimingInterceptor? Или я могу поставить какой-то атрибут [InterceptOrder(1|2|3|...)] на классы перехватчиков? Или я могу поставить что-то вроде [InterceptAfter(typeof(TimingInterceptor))] на LoggingInterceptor?

И есть ли лучшая альтернатива использованию локальной переменной потока для отслеживания времени вызова? Да, я бы хотел, чтобы это было безопасно для потоков . Я думаю, что сохранение этой переменной в стеке может быть предпочтительным, но я не уверен, как LoggingInterceptor может получить дескриптор TimingInterceptor без слишком большого количества соединений (было бы неплохо иметь возможность отключить реализацию TimingInterceptor без перекомпиляции LoggingInterceptor) .

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018

Хорошо бы попробовать отдельные проблемы в ваших приложениях.Но проблемы не могут зависеть от другого.

Это может привести к путанице в терминологии, используемой в IOC / AOP и в области глобальной связи.Действительно, проблемы в парадигме AOP означают независимый код / ​​обработку.

В вашем случае я могу определить «проблему / аспект ведения журнала» и зависимость между измерением времени / калькулятором (Start + Stop) и регистратором.

Ваша инфраструктура AOP должна внедрять только аспект / совет ведения журнала, который может зависеть от работы регистратора + калькулятора времени (например, бизнес / домен / функциональный секундомер).В идеале, регистратор и калькулятор времени находятся за интерфейсами, и в аспекте ведения журнала следует использовать контейнер IOC для создания экземпляра рекомендации по ведению журнала с помощью введенного (пожалуйста, по конструктору) средства ведения журнала и калькулятора времени.

Таким образом, рекомендации по ведению журнала, аспект ведения журнала, средство ведения журналаМодуль и калькулятор времени Модуль может быть модульным тестированием и разрабатываться различными группами в разных проектах.

Использование threadlocal / threadstatic или callcontext часто является плохой идеей и может отражать проблему проектирования.

not: если вы используете threadstatic / threadlocal, позаботьтесь об объекте утечки памяти / длительного хранения, управлении пулом потоков, асинхронном вызове (Task), трудно обнаруживаемой ошибке из-за последовательных и случайных результатов стиля.

0 голосов
/ 08 апреля 2011

Вы пробовали просто добавить оба Interceptors в ваш прокси и запустить ваш код? Насколько я понимаю, если прокси-сервер имеет несколько перехватчиков, вызов Proceed () в первом перехватчике в цепочке будет фактически вызывать следующий перехватчик, а не фактически выполнять вызов.

...