在构建期间是否可以通过PostSharp访问引入的/编织的接口和成员? [英] Is It Possible to Access Introduced/Weaved Interfaces and Members by PostSharp During Build Time?

查看:65
本文介绍了在构建期间是否可以通过PostSharp访问引入的/编织的接口和成员?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在设计一个方案,其中两个PostSharp方面相互配合.我有一个方面(下面的代码中的FirstAspect)打算引入一个接口,然后另一个方面(下面的代码中的SecondAspect)应该与第一个方面引入的接口一起工作./p>

但是,似乎第一个方面引入的接口从未可用于第二个方面.

这是我目前正在使用的代码:

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 {}
}

在构建时,会抛出SecondAspect.ProvideAspects中的InvalidOperationException,因为在调用时SecondAspect不能使用FirstAspect引入的接口.也就是说,即使接口已被编织为MyClass类型,在当前AppDomain中处于加载状态的类型也不会被标记为已实现接口.

我要寻找的是在构建期间可以访问和定位目标元素上所有已知和编织的接口和成员的功能.

我调查了 ReflectionSearch ,它与我要查找的内容很接近,但是它在对该API进行调用时,似乎并未考虑到编织的元素.例如,调用ReflectionSearch.GetMembersOfType不会在MyClass上产生预期的IInterface.HelloWorld(在上例中由FirstAspect引入).

在构建期间,是否应该使用另一个API来访问PostSharp引入的/编织的元素?这有可能吗?

解决方案

所以这个问题看起来有点老了,但是我有一个类似的问题,我仍然需要一个答案(即:如何将属性引入到引入的方法,而不将属性应用于实现并复制它).就是说,我可能不得不问自己的问题,因为针对您要问的模式,有一些通用步骤可以解决您的难题,但不能解决我的难题.看起来您已经在尝试其中的一些功能,但是为了以后出现的其他功能,我将对其进行详细说明.

简而言之,不要使用反射"类型来指定方面依赖性. PostSharp提供的属性可用于要求应用方面或要求特定顺序(请参阅:应对多个问题概述),以及其他方面已经提供的导入成员的方法(此StackOverflow答案(未标记)是对该用户问题的正确答案,并且还显示了将ImportMemberAttribute与方面相关性一起使用的方法. ImportMemberAttribute能够从其他方面导入成员,只要顺序正确即可.如果成员不存在且不是由方面引入的,则此属性的IsRequired属性将导致生成错误.

现在,如果希望您的第二个方面能够应用于实现接口的所有类,则无论该接口是否是您的第一个方面都已应用,都可以将AspectTypeDependencyAttribute设置为AspectDependencyAction.Order,但不能设置用AspectDependencyAction.Required设置AspectTypeDependencyAttribute;如果您只希望将第二个方面应用于第一个方面建议的目标,则可以应用多个依赖项属性来指定需求和顺序,并且不需要提供方面的提供者(上面的答案还显示了将Advice应用于多个方面的替代实现单个方面中的切入点).同样,如果您希望您的第一个方面总是需要您的第二个方面,则可以应用一个附加的AspectTypeDependencyAttribute来在另一个方向上指定要求(即,如果两个都要求另一个,则您希望在两个方向上都指定要求).

方面优先级"还可以用于确定应用订单方面,尽管在任何可能的情况下,您都应该使用依赖项,因为它们也可以作为合同文档.

这全部都是假设您实际上不需要使用Aspect Provider(因为您的注释暗示已完成订购).您不希望一个Aspect Provider依赖于另一个Aspect Provider的结果(这会违反关注点分离),而是每个对象都有一个Aspect Provider yield多个方面.但是,您也可以在Aspect Providers上使用AspectTypeDependencyAttribute,例如,您可以拥有一个Type Level Aspect Provider,该Type Provider在引入接口的Type Level Aspect之后进行排序,然后在该provider中,您可以循环访问键入和注入依赖于第一方面的方面(例如,提供者进行的接口介绍关注者,该方法将方法拦截建议应用于现在可以调用第一方面介绍的方法的成员).

希望可以为您解决所有问题(或者,自提出问题以来的时间,是遇到此问题的其他任何人).其中一些信息可能也已过时或不准确(据我所知,在某些或任何条件下,现在有可能在传递给方面提供者的目标类型上检测注入的接口),但我相信所表达的模式仍然是PostSharp提出的首选做法.

I am designing a scenario where two PostSharp aspects are working with each other. I have one aspect (FirstAspect in the code below) that is meant to introduce an interface, and then another aspect (SecondAspect in the code below) is supposed to work with the interface that was introduced by the first aspect.

However, it does not seem that the interface that is introduced by the first aspect is ever available to the second aspect.

Here is the code that I am currently working with:

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 {}
}

When I build, the InvalidOperationException in the SecondAspect.ProvideAspects is thrown, as the interface that was introduced by FirstAspect is not available to SecondAspect at the time the call is made. That is, even though the interface has been weaved into the MyClass type, the type as it stands within the current AppDomain as loaded is not marked as having the interface implemented.

What I am looking for is the ability to access and locate all known and weaved interfaces and members on a target element during build time.

I looked into ReflectionSearch, and this is close to what I am looking for, but it does not appear to account for weaved elements at the time calls into this API are made. For instance, making a call to ReflectionSearch.GetMembersOfType does not yield the expected IInterface.HelloWorld on MyClass (which is introduced by FirstAspect in the example above).

Is there another API I should be using to access introduced/weaved elements by PostSharp during build-time? Is this even possible?

解决方案

So this question looks a little old, but I have a similar issue which I still need an answer to (which is: how do I introduce an attribute to an introduced method without applying the attribute to the implementation and copying it). That said, I may have to ask my own question, as there are some common steps for the pattern you're asking about which may solve your dilemma, but do not solve mine. It looks like you've already experimented with some of this, but for the sake of others that come along, I'll detail it out.

In short, don't use "reflection" types to specify aspect dependency. PostSharp provides attributes which you can use to require aspects to be applied or to require specific order (See: Coping with Several Aspects on the Same Target for an overview), as well as a method for importing members already provided by other aspects (This StackOverflow Answer, while not marked, is the correct answer to that user's question and also shows a way to use the ImportMemberAttribute together with aspect dependency). The ImportMemberAttribute is capable of importing members from other aspects as long as the order is correct; the IsRequired property on this attribute will cause a build error if the member does not exist and was not introduced by an aspect.

Now, if you want your second aspect to be able to apply to all classes that implement an interface, whether the interface was applied by your first aspect or not, you would set the AspectTypeDependencyAttribute with AspectDependencyAction.Order, but not set an AspectTypeDependencyAttribute with AspectDependencyAction.Required; if you only want the second aspect to apply to targets advised by the first then you can apply multiple dependency attributes to specify both the requirement and the order and no Aspect Provider would be required (the above answer also shows an alternative implementation applying Advice to multiple Pointcuts in a single Aspect). Similarly, if you want your first aspect to always require your second aspect you can apply an additional AspectTypeDependencyAttribute to specify requirement in the other direction (i.e. if both require the other you want the requirement specified on both).

Aspect "Priority" can also be used to determine the order aspects are applied, although whenever possible you should use the dependencies instead because they also server as contract documentation.

This is all assuming you don't actually need to use an Aspect Provider (since your comments imply it was done for ordering). You would not want one Aspect Provider to depend on the results of another Aspect Provider (this would violate separation of concerns), you would instead have a single Aspect Provider yield multiple aspects for each target. You can, however, use AspectTypeDependencyAttribute on Aspect Providers as well so, for instance, you can have a Type Level Aspect Provider that orders after a type level aspect that introduces an interface and then in the provider you can loop through methods on the type and inject aspects that depend on the first aspect (e.g. an interface introduction follower by a provider that applies method interception advice to members that can now call methods introduced by the first aspect).

Hope that clears things up for you (or, given the time since the question was asked, anyone else that runs into this issue). Some of this information may also be outdated or inaccurate (for all I know, it may now be possible to detect injected interfaces on the target types passed to aspect providers, under some or any condition), but I believe the patterns expressed are still the preferred practice proposed by PostSharp.

这篇关于在构建期间是否可以通过PostSharp访问引入的/编织的接口和成员?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆