3

Мы имеем регистрацию компонентов в замок Виндзор контейнера, как такВиндзорский замок метод перехвата вызова из класса

void RegisterComponent<TInterface, TImplementation>() { 
    var component = Component.For<TInterface>().ImplementedBy<TImplementation>(); 
    component.Interceptors<SomeInterceptor>(); 
    container.Register(component); 
} 

Однако мы получили к проблеме, когда мы делаем вызов метода из класса он не получает перехвачены. Например, у нас есть компонент, как

ServiceA : IService { 

    public void MethodA1() { 
     // do some stuff 
    } 

    public void MethodA2() { 
     MethodA1(); 
    } 

} 

И если мы называем MethodA2 или MethodA1 методы из некоторого другого класса он перехватывается, но MethodA1 по-видимому, не перехватывается при вызове из MethodA2, так как вызов из класса.

Мы нашли подобный случай с решением Castle Dynamic Proxy not intercepting method calls when invoked from within the class Однако решение показывает создание компонента и прокси с помощью new оператора, который не подходит в нашем случае, так как мы используем контейнер. Можем ли мы использовать это решение с регистрацией компонентов, как указано выше? Или существуют другие подходы к решению проблемы?

ответ

3

Для перехвата работать на MethodA1 при вызове из MethodA2 вы должны использовать перехват на основе наследования (это потому, что вы используете this ссылку, чтобы сделать вызов).

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

Затем вы можете сделать регистрацию контейнера, как это:

container.Register(Component.For<ServiceA>().Interceptors<SomeInterceptor>()); 
container.Register(Component.For<IService>().UsingFactoryMethod(c => c.Resolve<ServiceA>())); 

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

+0

Есть ли способ зарегистрировать сервис как интерфейс, но использовать перехват на основе наследства без этого взлома? Почему он отключен по умолчанию? – xumix

+0

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

+0

@ krzysztof-kozmic Итак, нет простого способа использовать перехват на основе Inheritance? – xumix

1

Мы используем метод CreateClassProxy для создания прокси для службы, как это было предложено в ответе на вопрос Castle Dynamic Proxy not intercepting method calls when invoked from within the class. Затем мы регистрируем полученный прокси как реализацию для интерфейса. Таким образом, наш метод пользовательских RegisterComponent выглядит следующим образом

private void RegisterComponent<TInterface, TImplementation>() 
    where TInterface : class 
    where TImplementation : class, TInterface 
{ 
    var proxyType = new ProxyGenerator().CreateClassProxy<TImplementation>().GetType(); 
    Container.Register(Component.For<TInterface>().ImplementedBy(proxyType)); 
} 

Полная регистрация компонент

Container = new WindsorContainer(); 
Container.Kernel.Resolver.AddSubResolver(new CollectionResolver(Container.Kernel)); 

// Interceptor 
Container.Register(Component.For<IInterceptor>().ImplementedBy<SomeInterceptor>().LifestyleTransient()); 

// Component registrations 
RegisterComponent<ISomeService, SomeService>(); 

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

Однако недостатком этого решения является то, что вы не могли использовать инъекцию конструктора при создании прокси-объекта. Обратите внимание, что вы создаете «пронумерованный» прокси-объект с оператором new только для получения типа прокси-сервера. Поэтому вы не можете использовать инъекцию конструктора только при создании фиктивного прокси, но когда вы разрешаете свою службу через контейнер, инъекция будет работать нормально. Таким образом, этот недостаток имеет решающее значение только для компонентов с конструктивной логикой, которые сложнее, чем просто привязка зависимостей.Если вам нужно только assigments зависимостей вы можете попытаться решить все зависимости от контейнера вручную до создания манекена проксирования без проксирования

private object[] ResolveConstructorParameters<TType>() 
{ 
    return typeof(TType).GetConstructors() 
         .Single(c => c.IsPublic) 
         .GetParameters() 
         .Select(p => _container.Resolve(p.ParameterType)) 
         .ToArray(); 
} 

, а затем RegisterComponent станет

private void RegisterComponent<TInterface, TImplementation>() 
    where TInterface : class 
    where TImplementation : class, TInterface 
{ 
    var constructorParameters = ResolveConstructorParameters<TImplementation>(); 
    var proxyType = new ProxyGenerator().CreateClassProxy(typeof(TImplementation), constructorParameters).GetType(); 
    _container.Register(Component.For<TInterface>().ImplementedBy(proxyType)); 
} 

Вы также можете просто заполнить аргументы null.

+0

Почему вы не пытались переопределить DefaultProxyFactory и реализовать там прокси-сервер Inheritance? Неспособность использовать инъекцию конструктора - это шоу-стоппер для меня :( – xumix

+0

@xumix Я обновил свой ответ с большим количеством объяснений. Возможно, позже я попытаюсь переопределить 'DefaultProxyFactory', но вы также можете попробовать и добавить еще один ответ на этот вопрос:) – NikolayKondratyev

2

Измените свою регистрацию на следующую и Windsor должен переключиться на прокси классов - например, используя наследование для перехвата, а не композицию.

void RegisterComponent<TInterface, TImplementation>() { 
    container.Register(Component.For<TInterface,TImplementation>().ImplementedBy<TImplementation>().Interceptors<SomeInterceptor>()); 
} 
+0

Это не решение, так как вы не можете зарегистрировать все контроллеры, например. Или вы должны зарегистрировать его один за другим. – xumix

+0

? Это метод регистрации, предоставляемый айером с одним изменением для принудительного прокси-класса. –

1

@NikolayKondratyev Я посмотрел в https://github.com/castleproject/Windsor/blob/master/src/Castle.Windsor/Windsor/Proxy/DefaultProxyFactory.cs#L110 , и я сделал Регистрационной легкий путь: container.Register(Classes.FromThisAssembly().BasedOn(typeof(IRepositoryBase<,>)) .WithServiceAllInterfaces().WithServiceSelf() .LifestyleTransient());

Примечание .WithServiceSelf() вызов, это на самом деле коммутаторы класса на основе проксирование

+0

Я предполагаю, что это делает то же самое, что и регистрирующий компонент для реализации службы, такой как другие. – NikolayKondratyev

+0

@NikolayKondratyev nope, это добавляет не только сервис как сам, но и добавляет все его интерфейсы. Также вам не нужно регистрировать компоненты 1-на-1, используя этот метод – xumix

+0

Я говорил о 'WithServiceSelf'. Отличное решение! – NikolayKondratyev

Смежные вопросы