Я заранее прошу прощения за терминологическую перегрузку, связанную с этим вопросом: слово 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 #.
У меня такое ощущение, что проблема зависит только от того, как мы зарегистрировали службы и перехватчики в замке Виндзор.
Дело в том, что в нашем коде десятки перехватчиков, и все они работают нормально. Кроме того, почему прерывается только одно или два события, а остальные работают нормально с с тем же кодом диспетчера событий и теми же зарегистрированными перехватчиками ?
В данный момент я застрял в расследовании, у меня нет больше идеи, чтобы понять, что происходит не так.