Перехват Unity 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 равна нулю. Мой вывод заключается в том, что прокси-класс перехвата проверяется на наличие специального атрибута, а не исходного конкретного экземпляра.

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

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);

3 ответа

Решение

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

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

Другой вариант - создать подкласс из HandlerAttribute. См. Этот SO пост для деталей, или прочитайте этот документ Unity. В вашей реализации 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

}

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

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

Другие вопросы по тегам