Castle.Windsor: изменения для фабрик и перехватчиков в 3.1?
Следующий код проходит как есть с Castle.Windsor 2.5.3, но не проходит после обновления до 3.1.0
Исключением является InvalidProxyConstructorArgumentsException, в котором говорится: "Не удалось создать экземпляр прокси-сервера класса: Test. Не удалось найти конструктор без параметров".
static void Main(string[] args)
{
var container = new WindsorContainer();
container.Register(Component.For<Interceptor>(),
Component.For<Test>().UsingFactoryMethod(() => new Test(""))
.Interceptors<Interceptor>());
var test = container.Resolve<Test>(); //THROWS IN 3.1.0
}
}
public class Test
{
public readonly string S;
public Test(string s)
{
S = s;
}
}
public class Interceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
}
}
В моем реальном коде Test - это MongoDatabase, который создается с использованием фабричного метода и внедряется в репозиторий.
В моем реальном коде я также использую наследник AbstractFacility для регистрации перехватчика. Таким образом, мне не нужно регистрировать перехватчик для каждого компонента. Кажется, что обе формы использования перехватчика работают / не работают (в 2.5.3/3.1.0) одинаково при последующем разрешении. Для справки вот сокращенная версия объекта:
public class Facility : AbstractFacility
{
protected override void Init() { Kernel.ComponentRegistered += KernelComponentRegistered; }
static void KernelComponentRegistered(string key, IHandler handler)
{
if (typeof(IInterceptor).IsAssignableFrom(handler.ComponentModel.Implementation)) return;
handler.ComponentModel.Interceptors.AddIfNotInCollection(InterceptorReference.ForKey("SomeInterceptor"));
}
}
Я посмотрел на исходный код Castle.Windsor, и ожидающий код должен обернуть прокси вокруг заданного им класса, поэтому он ищет конструктор без параметров. Однако в 2.5.3 я думаю, что код генерации прокси никогда не выполнялся, и контейнер преобразуется (правильно, на мой взгляд) в непрокси-версию Test/MongoDatabase
Итак, два вопроса, я думаю: 1) Что изменилось? 2) Как сохранить регистрацию перехватчика, не создавая прокси для объекта, разрешенного заводским методом? Или я думаю, как прокси генерируется с использованием фабричного метода для компонента...
2 ответа
В 2.5.3 Виндзор, похоже, молча не может применить перехватчик. В 3.1.0 Windsor генерирует исключение, когда не может применить перехватчик к типу, который вы зарегистрировали. Windsor использует свою библиотеку Dynamic Proxy для поддержки перехватчиков (AOP), генерируя прокси экземпляров, которые вы запрашиваете. Таким образом, проблема в обеих версиях заключается в том, что передаваемый им класс не может быть превращен в динамический прокси, поскольку у него нет конструктора без аргументов. Я бы посчитал поведение в 3.1.0 более правильным, так как, если бы вы ожидали, что перехватчик будет применен, было бы намного сложнее выяснить, в чем проблема.
Если вы хотите сохранить поведение, начиная с 2.5.3, когда оно молча завершается неудачей, просто добавьте проверку, чтобы проверить, можно ли прокси-тип перед тем, как зарегистрировать перехватчик на вашем объекте. Наверное, лучше это сделать, но вот что я придумал:
try
{
ProxyGenerator generator = new ProxyGenerator();
generator.CreateClassProxy(handler.ComponentModel.Implementation);
handler.ComponentModel.Interceptors.AddIfNotInCollection(InterceptorReference.ForType<MyInterceptor>());
}
catch {}
Это ужасный код во многих отношениях, но он воссоздает поведение, к которому вы привыкли. Просто будьте осторожны, чтобы не укусить вас в будущем, когда есть другой класс, на котором вы хотите включить перехватчик, и пытаетесь понять, почему он не вызывается.
Стефан поставил меня на правильный путь. Тот факт, что перехватчик не был назначен для тестирования в 2.5.3, является ошибкой / функцией, замаскированной под ошибку. Поведение 3.1.0 более корректно и заставляет вас четко указывать, на что вы хотите перехватить. Поскольку мой настоящий код включал в себя Facility, вот решение, которое решает проблему:
public class Facility : AbstractFacility
{
protected override void Init() { Kernel.ComponentRegistered += KernelComponentRegistered; }
static readonly List<Type> TypesNotToIntercept = new List<Type>
{
typeof(IInterceptor), //Don't intercept Interceptors
typeof(MulticastDelegate), //Func<> and the like
typeof(LateBoundComponent), //Resolved with a factory, such as MongoDatabase
};
static void KernelComponentRegistered(string key, IHandler handler)
{
if (TypesNotToIntercept.Any(type => type.IsAssignableFrom(implementation));
return;
handler.ComponentModel.Interceptors.AddIfNotInCollection(InterceptorReference.ForKey("SomeInterceptor"));
}
}