Определение базового класса или базовой функциональности динамического прокси-сервера (например, Castle, LinFu)

#c# #proxy-classes

#c# #прокси-классы

Вопрос:

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

Что я хотел бы сделать, так это расширить созданный прокси, чтобы он реализовывал некоторые из моих собственных пользовательских действий (т. Е. средство сравнения). Мне это нужно, потому что следующее стандартное поведение .NET не дает правильных результатов:

 //object AC is a concrete class
collection.Contains(AC) = true

//object AP is a proxy with the SAME id and therefore represents the same instance as concrete AC
collection.Contains(AP) = false
  

Если мой компаратор был реализован AP (т. Е. соответствует идентификатору), то collection.Contains(AP) вернет true, как я и ожидал, если прокси были неявными. (ПРИМЕЧАНИЕ: Для тех, кто говорит, что NH наследуется от вашего базового класса, тогда да, это так, но NH также может наследовать от интерфейса — что мы и делаем)

Я совсем не уверен, что это возможно или с чего начать. Можно ли это сделать в любом из распространенных генераторов прокси, которые использует NH?

Ответ №1:

С Castle DynamicProxy у вас мало вариантов.

Во-первых, при создании прокси-сервера необходимо указать IComparer<T> в качестве одного из additionalInterfacesToProxy . Интерфейс не будет иметь реальной реализации для продолжения, поэтому вам нужно предоставить перехватчик, который вместо вызова Proceed предоставит фактическую логику для методов.

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

Третий вариант, доступный только для интерфейсных прокси, заключается в установке базового класса proxyGenerationOptions.BaseClassForInterfaceProxy = typeof(SomethingImplementingComparer);

Комментарии:

1. Кшиштоф, третий вариант — я полагаю, это настройка Castle? Я нигде не могу найти это в документации NH. Это, безусловно, кажется самым простым вариантом, если я смогу правильно его подключить. Пока не совсем уверен, как настроить Castle в NH!

2. да, это часть настроек Castle. Чтобы подключить NHibernate, я думаю, вам пришлось бы предоставить свой собственный ProxyFactoryFactory и ProxyFactory . Я бы начал с рассмотрения того, что находится в NHibernate.Bytecode.Castle.dll

3. Кшиштоф, выбрал третий вариант, но он работает не так, как я ожидал. Прокси, сгенерированный castle, действительно преобразуется в указанный мной базовый класс, но он не запускает средство сравнения базового класса, если я не приведу прокси к базовому классу. Похоже, что Castle также «проксирует» базовый класс, не используя базовый класс в качестве прокси для интерфейса. Правильно ли это?

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

5. Так это будет изменение Castle в какой-то момент в будущем? Хорошо, тогда все равно спасибо. Я буду следить за этим — для справки, я пробовал это с IComparer<T> и IComparable<T> — ни один из них не приводил к попаданию прокси в базовый класс, если я сначала не приведу прокси-сервер.

Ответ №2:

Такое поведение возможно с LinFu.DynamicProxy, но вам пришлось бы заменить перехватчик, предоставляемый NHibernate, вашим собственным пользовательским перехватчиком, который делегирует обратные вызовы исходному перехватчику:

 var yourProxiedInterfaceInstance = ...

// Get the Interceptor that NHibernate uses

var proxy = (IProxy)yourProxiedInterfaceInstance;

var interceptor = proxy.Interceptor;

// You'll need to create a decorator class around the IInterceptor interface
var yourInterceptor = new SomeCustomInterceptor(interceptor); 

// Replace the interceptor here
proxy.Interceptor = yourInterceptor;
  

Это довольно легко сделать с LinFu, поскольку для каждого экземпляра прокси, который он генерирует, требуется перехватчик. Если вы измените перехватчик, вы сможете автоматически изменить поведение прокси-сервера. Я надеюсь, что это поможет.

Комментарии:

1. Спасибо за совет. По крайней мере, теперь я знаю, что то, что я хочу, не совсем случайно. Я изучу LinFu.