AOP With Unity 2.0 - 2

by Daniel Lins Leite 2. April 2010 20:07

In the first post i showed a framework for Aspect-Oriented Programming ( AOP ) using only a proxy generator and a Unity 2.0. In this post i want to show the advances of the framework.

part 1 - http://www.machinaaurum.com.br/blog/post/AOP-With-Unity-20.aspx
part 3 - http://www.machinaaurum.com.br/blog/post/AOP-With-Unity-20-3.aspx

Before and After methods

The framework now can intercept the method either before and after the execution and have some information of the original called method and its parameters.

        public void Before(MethodInfo methodInfo, object[] parameters)
        {
            Log.TraceInformation("Before " + methodInfo.Name);
        }

        public void After(MethodInfo methodInfo, object[] parameters)
        {
            Log.TraceInformation("After " + methodInfo.Name);
        }

And running i have the following log: 

Complex Aspects

Not every aspects are simple classes. Now the aspect class is being resolved by the Unity so it can have its own dependencies.

    interface ILog
    {
        TraceSource Source { get; set; }
    }

    class Log : ILog
    {
        public TraceSource Source { get; set; }

        public Log()
        {
            Source = new TraceSource("Log");
            Source.Switch.Level = SourceLevels.Verbose;
        }
    }

    class LogMethodInjection : IMethodInjection
    {
        ILog Log { get; set; }

        public LogMethodInjection(ILog log)
        {
            Log = log;
        }

        #region IMethodInjection Members

        public void Before(MethodInfo methodInfo, object[] parameters)
        {
            Log.Source.TraceInformation("Before " + methodInfo.Name);
        }

        public void After(MethodInfo methodInfo, object[] parameters)
        {
            Log.Source.TraceInformation("After " + methodInfo.Name);
        }

        #endregion
    }

Thus, the ILog interface, in the constructor, will be injected. For this the ILog must be registered in the Unity.

container.RegisterType<ILog, Log>();

Besides dependencies, the aspect constructor can have parameters that are not dependencies. For example:

    class LogMethodInjection : IMethodInjection
    {
        ILog Log { get; set; }
        string Prefix { get; set; }

        public LogMethodInjection(ILog log, string prefix)
        {
            Log = log;
            Prefix = prefix;
        }

        #region IMethodInjection Members

        public void Before(MethodInfo methodInfo, object[] parameters)
        {
            Log.Source.TraceInformation( Prefix + " Before " + methodInfo.Name);
        }

        public void After(MethodInfo methodInfo, object[] parameters)
        {
            Log.Source.TraceInformation( Prefix + " After " + methodInfo.Name);
        }

        #endregion
    }

The problem is that now, the Unity will not resolve the "prefix" parameter. For this to happen a specific injection attribute must be created. The attribute will fill the a dictionary with the name and the value of the constructor parameter.

    class LogAttribute : InjectAttribute
    {
        public LogAttribute(string prefix)
        {
            Injection = typeof(LogMethodInjection);
            Parameters.Add("prefix", prefix);
        }
    }

And the injected method must be decorated.

        [LogAttribute("Test1", Order = 1)]
        public void Move(Account sourceAccount, Account destinationAccount, float quantity)
        {
            sourceAccount.Quantity -= quantity;
            destinationAccount.Quantity += quantity;
        }

The result: 

Aspect Lifetime

The aspect class is beaing created for each instance of the target class. To see it in action, let us create this aspect.

    class CallCount : IMethodInjection
    {
        int Count = 0;

        [Dependency]
        public ILog Log { get; set; }

        #region IMethodInjection Members

        public void Before(MethodInfo methodInfo, object[] parameters)
        {
            Count++;
            Log.Source.TraceInformation(Count.ToString());
        }

        public void After(MethodInfo methodInfo, object[] parameters)
        {
            Count++;
            Log.Source.TraceInformation(Count.ToString());
        }

        #endregion
    }

Next step: decorate the injected method.

        [Inject(Order = 1, Injection = typeof(CallCount) )]
        public void Move(Account sourceAccount, Account destinationAccount, float quantity)
        {

The running code is...

            {
                IMoveOperation operation = container.Resolve<IMoveOperation>();
                operation.Move(a, b, 10.0f);
                operation.Move(a, b, 10.0f);
                operation.Move(a, b, 10.0f);
            }
            container.Resolve<ILog>().Source.TraceInformation("New Instance");
            {
                IMoveOperation operation = container.Resolve<IMoveOperation>();
                operation.Move(a, b, 20.0f);
                operation.Move(a, b, 10.0f);
                operation.Move(a, b, 10.0f);
            }

The result must be that the counter will be 6 then will come back to zero and reach 6 again... 

 Assembly and Class Aspect

The InjectAttribute can also be applied in assemblies:

[assembly: Unity2AndAop.LogAttribute("Assembly", Order = 0,  Rules = new string[]
    {
        "Unity2AndAop.(.)*"
    })]

The "Rules" property is a list of rules that will be applied to each method in the classes registration ( only registred classes by the Unity Extension will be injected ). In this example i am saying that i want that every method of every class in the Unity2Aop namespace must be injected by the Log aspect.

If both, a method and an assembly, has a aspect, both will be merged respecting the order property of both attributes. For example:

[assembly: Unity2AndAop.LogAttribute("Assembly", Order = 0,  Rules = new string[]
    {
        "Unity2AndAop.(.)*"
    })]

    [Log("Class", Order = 1, Rules = new string[]{"(.)*"})]
    class MoveOperation : IMoveOperation
    {
        #region IAccountManager Members

        [Log ( "Method", Order = 2 )]
        public void Move(Account sourceAccount, Account destinationAccount, float quantity)
        {
            sourceAccount.Quantity -= quantity;
            destinationAccount.Quantity += quantity;
        }

        #endregion   

 

And the result is... 

Download the Unity Extension and the Proxy Generator here.

kick it on DotNetKicks.com

Tags: , , ,

Development

Comments (1) -

Denis Gladkikh Russia
4/3/2010 6:24:59 PM #

Thanks Daniel, very interesting. After you first post I tried to implement some AOP features in out project and found a bug in Unity 2 (unity.codeplex.com/.../View.aspx?WorkItemId=7511), so I set aside this methodology for now.

Pingbacks and trackbacks (5)+


RecentComments

Comment RSS