2013-06-20 5 views
3

Заранее благодарим за вашу помощь! (Да, есть вопрос внизу)Unity Interception GetCustomAttribute

Я использую Unity 3.x Interception для выполнения AOP предварительной и последующей связи базы данных и транзакционных операций. Перехватчик базы данных всегда создается, а транзакционный перехватчик - на основе CustomAttributeMatchingRule, как через InterfaceInterceptor. У меня есть свойства, которые в настоящее время установлены в моем TransactionAttribute:

[Transaction(IsolationLevel.ReadUncommitted, NoRollbackFor = new[] { typeof(TestException) })] 

В качестве примера я использую в моем тестовом модуле. Я хотел бы получить к ним доступ в моем методе вызова класса TransactionCallHandler. Я видел примеры:

var transactionAttribute = input.MethodBase.GetCustomAttribute<TransactionAttribute>(false); 

это способ получить к нему доступ, но моя транзакция var равна нулю. Мое заключение - прокси-класс перехвата проверяется для настраиваемого атрибута, а не для конкретного конкретного экземпляра.

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

var methods = input 
    .Target 
    .GetType() 
    .GetMethods() 
    .Where(m => m.Name == input.MethodBase.Name) 
    .Where(m => m.GetCustomAttribute<TransactionAttribute>(false) != null); 

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

Итак, после всего этого, мой вопрос: Я не выполняю рефлексию правильно? Есть ли ошибка в Unity, о которой я должен сообщить?

Вот мои определения контейнера:

Container = new UnityContainer(); 
Container.AddNewExtension<Interception>(); 

Container.RegisterType<IMockUseDefaultConnectionString, MockUseDefaultConnectionString>(
    new InterceptionBehavior<PolicyInjectionBehavior>(), 
    new Interceptor<InterfaceInterceptor>(), 
    new InjectionConstructor(new DatabaseSettings())); 

Container.RegisterType<IMockUseHardcodedConnectionString, MockUseHardCodedConnectionString>(
    new InterceptionBehavior<PolicyInjectionBehavior>(), 
    new Interceptor<InterfaceInterceptor>(), 
    new InjectionConstructor(new DatabaseSettings 
    { 
     ConnectionString = MockUseHardCodedConnectionString.ConnectionString 
    })); 
/* IDatabaseSettings is not registered to manually control the settings being used */ 

var first = new InjectionProperty("Order", 1); 
var second = new InjectionProperty("Order", 2); 

Container 
    .Configure<Interception>() 
    .AddPolicy("DatabaseConnectionPolicy") 
    .AddMatchingRule<NamespaceMatchingRule>(new InjectionConstructor("MyNamespace.*", true)) 
    .AddCallHandler<DatabaseConnectionCallHandler>(first); 

Container 
    .Configure<Interception>() 
    .AddPolicy("TransactionPolicy") 
    .AddMatchingRule(new CustomAttributeMatchingRule(typeof(TransactionAttribute), inherited: false)) 
    .AddCallHandler<TransactionCallHandler>(second); 

ответ

2

Я считаю, что поведение, которое вы видите, является следствием конструкции методов перехвата. При использовании InterfaceInterceptor создается прокси-объект, который реализует целевой интерфейс, однако прокси-объект является совершенно другим типом от исходного типа.

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

+1

Я согласен с @Tuzo с вашим уменьшением виртуального перехвата.Я не хочу вводить конвенцию в регулярную базу кода, которую я должен учить каждому новому разработчику; не говоря уже о надеждах после того, как я оставлю этот контракт, что они помнят, почему это делается в первую очередь. Я добавлю запрос в проект перехвата Unity и посмотрю, смогу ли я получить его в дорожной карте. – Werewolf

+0

Моя просьба к проекту закрыта: Пользователь gmelnik обновил Вопрос: Unity Interception GetCustomAttribute. Статус изменился с «Предложен до закрытого» следующим комментарием: «Ответ предоставлен на StackOverflow». – Werewolf

1

Другие варианты - создать подкласс из HandlerAttribute. См. Это SO post для получения дополнительной информации или прочтите this Unity doc. В своей реализации CreateHandler() HandlerAttribute передайте экземпляр вашего экземпляра в экземпляр обработчика вызова, чтобы вам не приходилось вызывать GetCustomAttributes(). Или, как реализовать HandlerAttribute и ICallHandler в одном классе и просто вернуть это:

public sealed class AuditAttribute : HandlerAttribute, ICallHandler 
{ 
    #region attribute properties 
    public string Key { get; set; } // your own attribute properties 

    public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container) 
    { 
     return this; 
    } 

    #endregion 

    #region ICallHandler 
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) 
    { 
     // do stuff ... 

     // then 
     return getNext()(input, getNext); 
    } 

    public int Order { get; set; } 
    #endregion 

} 
+0

Ницца. Я не думал об этом, и это прекрасно решает проблему. Тем не менее, если я хочу, чтобы несколько атрибутов работали вместе, например, для кеша (cacheResult, InvalidateCacheResult, CacheTimeout и т. Д.), Вам нужно еще сделать неприятную проверку на отражение – Keith

0

Я просматривал какой-то пример кода, и я заметил, что-то очень простое, что я не подумал.

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

+0

интересной идеей. Сможете ли вы разместить небольшой код для поддержки этого? Благодаря! – Werewolf