Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple aspects on one method

Tags:

c#

aop

postsharp

In my application I previously used regular C# attributes to "annotate" a method. E.g.:


[Foo(SomeKey="A", SomeValue="3")]
[Foo(SomeKey="B", SomeValue="4")]
public void TheMethod()
{
   SpecialAttributeLogicHere();
}


What SpecialAttributeLogicHere() did, was to reflectively look at all the Foo-attributes that annotated this particular method. It would then (on its own), create its own dictionary for all the keys and values.

I'm now trying to move to PostSharp, because the SpecialAttributeLogic could be put into an aspect (and removed from the method body which is much cleaner!), within OnEntry. Foo will be replaced by an aspect that extends OnMethodBoundaryAspect.

I would still like to use it the following way:


[Foo(SomeKey="A", SomeValue="3")]
[Foo(SomeKey="B", SomeValue="4")]

But if Foo has an OnEntry, that means that the "SpecialAttributeLogic" will be executed twice. I basically need to "gather" all the keys and values from each Foo(), into a dictionary, which I then apply some logic to.

How to do this (or best practices) with PostSharp? Thanks!

like image 782
Torbjørn Kristoffersen Avatar asked Aug 23 '11 15:08

Torbjørn Kristoffersen


1 Answers

It looks like you want to build a namevaluepair inside of your method. You cannot do this with an aspect. What I suggest is you use a MethodInterceptionAspect and reflect the attributes on the method then build your collection and pass it in the the method via a parameter (maybe using an overloaded method) or setting it as a class member.

You can reflect the values at compile time as to keep performance optimal.

Here is a quicky solution to your problem. It's a bit ugly (you will need to make modifications to fit). There are other ways but they aren't as "generic".

namespace ConsoleApplication12
{
    class Program
    {
        static void Main(string[] args)
        {
            MyExampleClass ec = new MyExampleClass();
            ec.MyMethod();
        }
    }

    public class MyExampleClass
    {
        [Special(Key = "test1", Value = "1234")]
        [Special(Key = "test2", Value = "4567")]
        [MyAspect]
        public void MyMethod()
        {
            MyMethod(new Dictionary<string, string>());
        }

        public void MyMethod(Dictionary<string, string> values)
        {
            //Do work
        }

    }

    [Serializable]
    public class MyAspect : MethodInterceptionAspect
    {
        Dictionary<string, string> values = new Dictionary<string, string>();
        MethodInfo target;

        public override void CompileTimeInitialize(System.Reflection.MethodBase method, AspectInfo aspectInfo)
        {
            target = method.DeclaringType.GetMethod(method.Name, new Type[] { typeof(Dictionary<string, string>) });

            foreach (Attribute a in method.GetCustomAttributes(false))
            {
                if (a is SpecialAttribute)
                {
                    values.Add(((SpecialAttribute)a).Key, ((SpecialAttribute)a).Value);
                }
            }
        }

        public override void OnInvoke(MethodInterceptionArgs args)
        {
            if (values == null || values.Count < 1)
            {
                args.Proceed();
            }
            else
            {
                target.Invoke(args.Instance, new object[] { values });
            }

        }
    }
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true) ]
    public class SpecialAttribute : Attribute
    {
        public string Key { get; set; }
        public string Value { get; set; }
    }
}

target and values are both initialized at compiletime (not runtime) for consumption at runtime. They get serialized with the aspect at compiletime. This way you save on the reflection hit at runtime.

like image 77
Dustin Davis Avatar answered Oct 01 '22 18:10

Dustin Davis