Почему перехватчик Castle Windsor нарушает привязку метода во время выполнения к динамическому объекту C #? - PullRequest
4 голосов
/ 20 апреля 2019

Я заранее прошу прощения за терминологическую перегрузку, связанную с этим вопросом: слово dynamic в дальнейшем будет использоваться как для функции динамической типизации C #, так и для функции динамического прокси Castle Windsor.

У нас в основном есть сценарий, в котором во время выполнения мы должны выбрать правильный обработчик события для объекта события. Обработчики событий предоставляются фабричным объектом, который внутренне использует контейнер инъекции зависимостей Виндзора замка для предоставления экземпляров объекта, а входные данные, предоставляемые фабрике, являются экземпляром интерфейса маркера IEvent.

Просто чтобы исправить идею, это участвующие классы (это упрощенный сценарий, но суть проблемы сохраняется):

public interface IEvent {}

public class CustomerCreated : IEvent 
{
  public string CustomerName { get; set; }
}

public interface IEventHandler {}

public interface IEventHandler<T> : IEventHandler where T: IEvent
{
  void Handle(T event);
}

public class CustomerService : IEventHandler<CustomerCreated>
{
  public void Handle(CutomerCreated @event)
  {
    // handle the event in some way...
  }
}

public interface IEventHandlerFactory 
{
  IEventHandler[] GetHandlers(IEvent event);
}

Вот код потребления, который получает событие, просит фабрику предоставить обработчики и выполняет все предоставленные обработчики (опять-таки это упрощенная версия, но суть остается):

public class EventDispatcher 
{
  private readonly IEventHandlerFactory factory;

  public EventDispatcher(IEventHandlerFactory factory)
  {
    this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
  }

  public void Dispatch(IEvent @event)
  {
    foreach(var handler in this.factory.GetHandlers(@event))
    {
      ((dynamic)handler).Handle((dynamic)@event);
    }
  }
}

Этот код работал хорошо в течение многих лет, так как мы решили создать Castle Windsor Interceptor, чтобы перехватывать все вызовы метода Handle класса CustomerService, чтобы мы могли записывать некоторые журналы каждый раз, когда метод называется.

Теперь плохая часть всей истории ...

Недавно добавленный замок-перехватчик Виндзора нарушил привязку времени выполнения метода Handle к динамическому объекту ((dynamic)handler), показанному внутри класса EventDispatcher.

Сообщается об ошибке RuntimeBinderException , в которой говорится, что вызов наилучшей перегрузки для метода CastleDynamicProxy_14.Handle содержит некоторые недопустимые аргументы (метод, который фактически выбран и сообщен в сообщении об исключении, является неправильным , потому что он принимает другой тип события в качестве параметра).

Мы тщательно исследовали исключение, и это в основном означает, что привязка во время выполнения выбирает неправильный метод Handle класса CustomerService для привязки вызова (CustomerService обрабатывает несколько событий в нашем реальном коде, поэтому имеет много методов, называемых Handle, и каждый из них принимает отдельный тип события в качестве единственного параметра в соответствии с определением интерфейса IEventHandler<T>.

Странно то, что введение динамического прокси-объекта Castle Windsor, который оборачивает реальный объект (CustomerService перехватывается Castle), нарушило привязку времени выполнения C # только для некоторых событий в то время как для остальных класс EventDispatcher, показанный выше, работает отлично, как и раньше. Между сломанным и рабочим событиями нет существенной разницы: все они являются классами POCO, реализующими интерфейс маркера IEvent.

У кого-нибудь была похожая проблема с динамическим кодом? Есть ли способ получить какую-то многословную запись о процессе, выполняемом CLR, когда он выполняет процесс привязки во время выполнения для динамических объектов?

Мы не можем воспроизвести ту же проблему в минимальном примере вне нашего приложения, поэтому я бы исключил ошибки на уровне Castle Dynamic Proxy или C #.

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

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

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

1 Ответ

0 голосов
/ 20 мая 2019

Это не относится к проблеме с перехватчиками / динамическими замками, но я много раз писал похожий код, не требуя вообще никакой динамики, используя общую версию Dispatch.

например:

public interface IEventHandlerFactory 
{
  IEventHandler<TEvent>[] GetHandlers<TEvent>(TEvent event) where TEvent : IEvent;
}   

public class EventDispatcher 
{
  private readonly IEventHandlerFactory factory;

  public EventDispatcher(IEventHandlerFactory factory)
  {
    this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
  }

  public void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent
  {
    foreach(var handler in this.factory.GetHandlers(@event))
    {
      handler.Handle(@event);
    }
  }
}

вызывая его:

eventDispatcher.Dispatch(new MyCustomEvent());

Если вам нужно вызвать его динамически, вы все равно можете использовать рефлексию для вызова eventDispatcher.Dispatch, но вы не столкнетесь с проблемами при приведении кдинамические и замковые перехватчики - это возможное решение проблемы, которая кажется мне потенциальной ошибкой

...