Castle Windsor включает или отключает перехватчики во время выполнения

Возможно ли, пока приложение (любого типа, например, консоль, веб-роль, веб-роль в Azure и т. д.) включать или выключать перехватчики Castle Windsor, чтобы избежать перезапуска приложения?

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

public class ExampleInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Debug.WriteLine(string.Format("Before method: {0}", invocation.Method.Name));
        invocation.Proceed();
        Debug.WriteLine(string.Format("After method: {0}", invocation.Method.Name));
    }
}

Мой первоначальный подход состоял в том, чтобы сделать что-то вроде этого:

public class InterceptorSelector : IModelInterceptorsSelector
{
    public static bool InterceptorOn = false;

    public bool HasInterceptors(ComponentModel model)
    {
        return
            typeof(ExampleInterceptor) != model.Implementation
            && InterceptorOn
            && model.Implementation.Namespace.StartsWith("MvcApplication1");
    }

    public InterceptorReference[] SelectInterceptors(ComponentModel model, InterceptorReference[] interceptors)
    {
        return new[]
        {
            InterceptorReference.ForType<ExampleInterceptor>()
        };
    }
}

Однако я обнаружил, что после того, как перехватчик обработал метод один раз, должно быть некоторое внутреннее кэширование (без сомнения, для повышения производительности), поскольку метод больше не попадает в InterceptorSelector.

Основной вариант использования, который у меня есть для этого, — это WebApi, размещенный в Azure, обслуживающий сотни (если не тысячи) запросов в секунду на нескольких экземплярах виртуальной машины. В случае необходимости отладки живой среды я хочу включить подробное ведение журнала для одного из этих экземпляров, но в то же время не хочу выводить экземпляр виртуальной машины из строя при включении этого ведения журнала.


person Danjuro    schedule 30.09.2014    source источник
comment
Вы проверили, что оба метода HasInerceptors, а также SelectInterceptors не вызываются? Я ожидаю, что последний вызывается при каждом создании компонента. Я думал, что первый метод просто вызывается один раз, так что Windsor не будет вызывать второй для компонентов, которые никогда не нуждаются в выборе перехватчиков.   -  person Marwijn    schedule 02.10.2014


Ответы (3)


Логика переключения во время выполнения может быть легко реализована с помощью переключателя конфигурации или вы можете сделать это, как К.Козмич, как показано в его руководстве по динамическому прокси здесь.

person Ognyan Dimitrov    schedule 02.10.2014

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

public class ChangeInterceptorSettingsEventArgs : EventArgs {}

public class MyInterceptorsController
{
    public delegate void ChangeInterceptorSettingsEvent(ChangeInterceptorSettingsEventArgs args);
    public static event ChangeInterceptorSettingsEvent ChangeInterceptorSettings;

    public void ChangeInterceptorsSettings(ChangeInterceptorSettingsEventArgs args)
    {
        if (ChangeInterceptorSettings != null)
        {
            ChangeInterceptorSettings(args);
        }
    }
}

Затем создайте перехватчик и зарегистрируйте обработчик событий.

public class ExampleInterceptor : IInterceptor
{
    public bool WriteThings { get; set; }
    public ExampleInterceptor()
    {
        MyInterceptorsController.ChangeInterceptorSettings += MyInterceptorsController_ChangeInterceptorSettings;
    }

    void MyInterceptorsController_ChangeInterceptorSettings(ChangeInterceptorSettingsEventArgs args)
    {
        WriteThings = !WriteThings; // this is not very smart, 
        // but I don't want to populate the args in this example
    }

    public void Intercept(IInvocation invocation)
    {
        if (WriteThings) {Debug.WriteLine(string.Format("Before method: {0}", invocation.Method.Name));}
        invocation.Proceed();
        if (WriteThings) {Debug.WriteLine(string.Format("After method: {0}", invocation.Method.Name));}
    }
}

Наконец, чтобы использовать его, просто вызовите событие из контроллера.

MyInterceptorsController.ChangeInterceptorsSettings(new ChangeInterceptorSetingsEventArgs())
// populate the args parameters with what you need

Таким образом, если вы хотите удалить некоторые перехватчики из объекта, который вы разрешили, вы всегда можете получить приватное поле, содержащее перехватчики, и изменить его во время выполнения. Я действительно не рекомендую это до проверки того, что существование перехватчика имеет такой удар по производительности:

resolvedObject
    .GetType()
    .GetField("__interceptors")
    .SetValue(resolvedObject, new IInterceptor[0]);
// or whatever interceptor array you want to set
person samy    schedule 30.09.2014
comment
Спасибо за ваш ответ @samy. Мне очень нравится эта идея, однако при таком подходе я считаю, что перехватчик должен быть включен постоянно, и поэтому я не уверен, на что будет похожа производительность. Я читал другой пост, в котором наблюдалось снижение производительности на 30% (более старая версия замка), поэтому я/надеялся, что есть способ удалить перехватчик из замка так же, как добавление -Container.Kernel.ProxyFactory.AddInterceptorSelector( новый InterceptorSelector()). Я предполагаю, что Кастлу нравится регистрировать все заранее, а то, что мне нужно, невозможно. - person Danjuro; 01.10.2014
comment
ps @samy, я оставлю это на пару дней, чтобы посмотреть, есть ли какие-либо другие предложения, и если нет, я отмечу ваш ответ как ответ. - person Danjuro; 01.10.2014
comment
После того, как вы создали компонент с перехватчиком, другие части вашего кода будут иметь ссылку на ваш перехватчик, а не на экземпляр вашего компонента. Таким образом, удаление перехватчика из работающего компонента невозможно. Если вы беспокоитесь о производительности, вы можете использовать прокси-сервер класса (по сути, это производный класс). Вы также можете принудительно зарегистрировать сам компонент в качестве его интерфейса. - person Marwijn; 01.10.2014
comment
Спасибо @Marwijn, так что в конечном итоге мы говорим здесь о том, что если перехватчик зарегистрирован, его нельзя отменить, и всегда будет проверка компонента, зарегистрированного в Windsor, чтобы увидеть, использовать ли перехватчик или нет. - person Danjuro; 01.10.2014
comment
@Danjuro, я собираюсь изучить это немного подробнее, но изначально я пытался ответить, как включать и выключать перехватчики, а не как управлять конвейером перехвата. Что касается выступления, вы засекали время? Это похоже на настоящий хит производительности, и, поскольку это не так просто измерить, интересно, как это было сделано. - person samy; 02.10.2014
comment
@samy Я считаю, что после создания экземпляра компонента (т.е. разрешения из контейнера) удалить перехватчик больше невозможно. Я не считаю невозможным избежать создания перехватчика для только что разрешенного компонента. Чтобы повысить производительность, вы можете попробовать создать прокси класса, который по сути является производным классом. Это можно сделать, зарегистрировав компонент как дополнительный сервис. - person Marwijn; 02.10.2014
comment
@Marwijn Ну, вы всегда можете продолжить и углубиться в разрешенный прокси-объект: resolvedObject.GetType().GetField("__interceptors").SetValue(resolvedObject, new IInterceptor[0]) но это выглядит не очень безопасно - person samy; 02.10.2014
comment
Добавление отражения для переопределения перехватчиков в мой ответ. Хотя очень хотелось бы услышать о производительности - person samy; 02.10.2014

Вы можете исключить/добавить перехватчики во время выполнения следующим образом:

public class MyController : Controller
{
    public IResolver iResolver {get;set;}
    public IService someService {get;set;}  

    private UsageMethod()
    {
        ExcludeInterceptors(someService, new [] {typeof(Interceptor1),typeof(Interceptor2)};
        AddInterceptor(someService, typeof(Interceptor1), 0};
    }

    private Castle.DynamicProxy.IInterceptor[] getInterceptorsField(object service)
    {
        var field = service.GetType().GetField("__interceptors");
        return field.GetValue(service) as Castle.DynamicProxy.IInterceptor[];
    }

    private void ExcudeInterceptors(object service, params Type[] interceptorTypes2exclude)
    {           
        var newInterceptors = getInterceptorsField(service).Where(x => !interceptorTypes2exclude.Contains(x.GetType())).ToArray();
        field.SetValue(service, newInterceptors);
    }

    private void AddInterceptor(object service, Castle.DynamicProxy.IInterceptor interceptorType2add, int position)
    {           
        var newInterceptors = getInterceptorsField(service).ToList();
        newInterceptors.Insert(position, iResolver.Resolve(interceptorType2add));
        field.SetValue(service, newInterceptors.ToArray());
    }
}

P.S. Тестировалось с Castle.Windsor 3.3.0

person Slava Utesinov    schedule 06.06.2017