2016-09-25 2 views
1

Я разрабатываю сценарий, в котором два аспекта PostSharp работают друг с другом. У меня есть один аспект (FirstAspect в коде ниже), который предназначен для введения интерфейса, а затем другой аспект (SecondAspect в коде ниже) должен работать с интерфейсом, который был введен первым аспектом.Возможно ли получить доступ к введенным/сотканным интерфейсам и членам PostSharp во время сборки?

Однако не кажется, что интерфейс, который представлен первым аспектом, всегда доступен для второго аспекта.

Вот код, который я в настоящее время работает с:

public class Tests 
{ 
    [Fact] 
    public void Verify() 
    { 
     // Not really all that significant as the current code does not compile correctly: 
     var sut = new MyClass(); 
     Assert.True(sut is IInterface); 
    } 

    public interface IInterface 
    { 
     void HelloWorld(); 
    } 

    [IntroduceInterface(typeof(IInterface))] 
    public class FirstAspect : InstanceLevelAspect, IInterface, IAspectProvider 
    { 
     public void HelloWorld() {} 

     public IEnumerable<AspectInstance> ProvideAspects(object targetElement) 
     { 
      // Implementing IAspectProvider appears to ensure this aspect is processed first. 
      // This may be a bug. 
      // Please see: http://support.sharpcrafters.com/discussions/problems/3365-runtimeinitialize-does-not-follow-ordering-rules#comment_40824072 
      // for more information. 
      yield break; 
     } 
    } 

    [AspectTypeDependency(AspectDependencyAction.Order, AspectDependencyPosition.After, typeof(FirstAspect))] 
    public class SecondAspect : InstanceLevelAspect, IAspectProvider 
    { 
     public IEnumerable<AspectInstance> ProvideAspects(object targetElement) 
     { 
      var type = (Type)targetElement; 
      if (!typeof(IInterface).GetTypeInfo().IsAssignableFrom(type)) 
      { 
       // This is currently being thrown, as MyClass does not implement 
       // IInterface when the AppDomain is first loaded and initialized: 
       throw new InvalidOperationException($"Does not implement {typeof(IInterface)}"); 
      } 

      // How to access the weaved elements from FirstAspect? ... 

      yield break; 
     } 
    } 

    [FirstAspect, SecondAspect] 
    class MyClass {} 
} 

Когда я строю, то InvalidOperationException в SecondAspect.ProvideAspects выбрасывается, как интерфейс, который был введен FirstAspect не доступен для SecondAspect в то время вызов выполнен. То есть, хотя интерфейс был спрессован в тип MyClass, тип, поскольку он находится в текущем AppDomain, как загруженный, не отмечен как имеющий реализованный интерфейс.

Что я ищу - это возможность доступа и определения всех известных и переплетенных интерфейсов и элементов на целевом элементе во время сборки.

Я просмотрел ReflectionSearch, и это близко к тому, что я ищу, но оно не похоже на учет тканных элементов во время вызова этого API. Например, вызов ReflectionSearch.GetMembersOfType не дает ожидаемого IInterface.HelloWorld по адресу MyClass (который представлен в приведенном выше примере FirstAspect).

Есть ли другой API, который я должен использовать для доступа к введенным/переплетенным элементам PostSharp во время сборки? Возможно ли это?

+0

аспекты PostSharp видят только оригинальные типы и методы, нет способа увидеть сотканные интерфейсы во время сборки. Что вы хотите сделать с введенным интерфейсом в SecondAspect? –

+0

Спасибо за ваш ответ @JakubLinhart. Я хочу убедиться, что определенный интерфейс применяется к моему классу, а затем, как только этот интерфейс применяется, примените к нему какое-либо количество аспектов, как обычно. Похоже, мне нужно выяснить другой способ сделать это? –

+0

Как насчет проверки наличия пользовательских атрибутов 'FirstAspect' для проверки на IInterface? Или вы хотите применить какой-либо аспект к введенным методам? –

ответ

1

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

Короче говоря, не используйте типы «отражения», чтобы указать зависимость от аспект. PostSharp предоставляет атрибуты, которые вы можете использовать, чтобы требовать применения аспектов или требовать определенного порядка (см. Ниже: Coping with Several Aspects on the Same Target), а также способ импорта участников, уже предоставленных другими аспектами (This StackOverflow Answer, хотя и не отмечен, является правильным ответьте на вопрос этого пользователя, а также покажите способ использования ImportMemberAttribute вместе с зависимостью от аспект). ImportMemberAttribute способен импортировать участников из других аспектов, если порядок верен; свойство IsRequired в этом атрибуте вызовет ошибку сборки, если член не существует и не был введен аспект.

Теперь, если вы хотите, чтобы ваш второй аспект, чтобы иметь возможность применять ко всем классам, которые реализуют интерфейс, был ли интерфейс применяется вашим первым аспектом или нет, вы должны установить AspectTypeDependencyAttribute с AspectDependencyAction.Order, но не установили AspectTypeDependencyAttribute с AspectDependencyAction.Required; если вы хотите, чтобы второй аспект применялся к целевым объектам, рекомендованным первым, вы можете применить несколько атрибутов зависимостей, чтобы указать как требование, так и порядок, и не требуется Аспектный провайдер (приведенный выше ответ также показывает альтернативную реализацию, применяющую Совет к нескольким Pointcuts в одном аспекте). Аналогичным образом, если вы хотите, чтобы ваш первый аспект всегда требовал вашего второго аспекта, вы можете применить дополнительный AspectTypeDependencyAttribute, чтобы указать требование в другом направлении (то есть, если оба требуют другого, то вы должны указать требование, указанное на обоих).

Аспект «Приоритет» также может использоваться для определения аспектов заказа, хотя по возможности вам следует использовать зависимости, потому что они также являются серверными документами.

Это все предполагается, что на самом деле вам не нужно использовать провайдера Aspect (так как ваши комментарии подразумевают, что это было сделано для заказа). Вы не хотели бы, чтобы один поставщик Aspect зависел от результатов другого поставщика Aspect (это нарушало бы разделение проблем), вместо этого у вас должен быть один аспектный провайдер yield для каждой цели. Однако вы можете использовать AspectTypeDependencyAttribute на Aspect Providers, так, например, у вас может быть провайдер уровня уровня, который заказывает после аспекта уровня уровня, который вводит интерфейс, а затем в провайдере вы можете прокручивать методы типа и впрыскивать аспекты, которые зависят от первого аспекта (например, последователь введения интерфейса поставщиком, который применяет рекомендации по перехвату метода для членов, которые теперь могут вызывать методы, введенные в первом аспекте).

Надеюсь, что это очистит вас (или, учитывая то время, когда был задан вопрос, кто-нибудь еще сталкивается с этой проблемой). Некоторая часть этой информации также может быть устаревшей или неточной (для всего, что я знаю, теперь возможно обнаружить инъецированные интерфейсы на типы целей, переданные поставщикам аспектов, при некоторых или любых условиях), но я считаю, что выраженные шаблоны по-прежнему являются предпочтительная практика, предложенная PostSharp.

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