Monday, April 5, 2010

Using castle dynamicProxy

I was looking for an easy dynamic proxy to work with (since it is missing from the .net framework for some unknown reason). I found castle dynamic proxy to be very elegant and useful.

Here is how to use it.

First create the class that will be accessible via the proxy:

    public  class MsgRouter
    {
        public virtual string SendMail(string from, string to, string title, string body)
        {
            return "from: " + from + Environment.NewLine + "to: " + to + Environment.NewLine + "title:" + title + Environment.NewLine + "body:" + body;
        }
 
        public virtual string SendEncryptedMail(string from, string to, string title, string body, string key)
        {
            return "from: " + from + Environment.NewLine + "to: " + to + Environment.NewLine + "title:" + title + Environment.NewLine + "body:" + body;
 
        }
 
        public virtual string SendFax(string PhoneNumber, string body)
        {
            return "phone number: " + PhoneNumber + " ,fax body:" + body;
        }
    }


Create an interceptor:

    /// <summary>
    ///     Counts the number of calls for each function.
    /// </summary>
    public class CallCounterInterceptor : IInterceptor
    {
        private Dictionary<string, int> _callsToMethods = new Dictionary<string, int>();
 
        #region IInterceptor Members
 
        public void Intercept(IInvocation invocation)
        {
            var name = invocation.MethodInvocationTarget.Name;
            if (!_callsToMethods.ContainsKey(name))
            {
                _callsToMethods.Add(name, 1);
            }
            else
            {
                int current = _callsToMethods[name];
                _callsToMethods[name] = current + 1;
            }
        }
 
        #endregion
    }


Create another interceptor:

   /// <summary>
    ///     Clears sensitive data
    /// </summary>
    public class ClearSensitiveDataInterceptor : IInterceptor
    {
        #region IInterceptor Members
 
        public void Intercept(IInvocation invocation)
        {
            for(int i=0; i < invocation.Arguments.Length ; i++)
            {
                invocation.SetArgumentValue(i, ClearSensitiveData(invocation.Arguments[i].ToString()));
            }
            invocation.Proceed();
            // change the return value
            invocation.ReturnValue = "gone!";
        }
 
        #endregion
 
        #region Utility
 
        /// <summary>
        ///     Just for the example....
        /// </summary>
        /// <param name="sInput"></param>
        /// <returns></returns>
        public string ClearSensitiveData(string sInput)
        {
            string res =  sInput.ToLower().Replace("[secret]", "---");
            return res;
        }
 
        #endregion
    }


Create a factory method for the MsgRouter class. The factory will be in-charge of creating the proxy object and binding it to one or more interceptors:

        private static MsgRouter MailerFactory()
        {
            IInterceptor[] arrInterceptors = new IInterceptor[2];
            arrInterceptors[0] = new ClearSensitiveDataInterceptor();
            arrInterceptors[1] = new CallCounterInterceptor();
 
            ProxyGenerator oProxyGenerator = new ProxyGenerator();
            var oProxy = oProxyGenerator.CreateClassProxy<MsgRouter>(new ProxyGenerationOptions(), arrInterceptors);
            return oProxy;
        }


Finally write some code to use the MsgRouter object:

            var oProxy = MailerFactory();
 
            string res = oProxy.SendEncryptedMail("me", "you", "title", "hello! this is the [secret]", "#$#%##$%^$#^$#^#^#^^$#%$#%$#^&^%&&*^&*($^%#&%#^$^");
            res = oProxy.SendMail("me", "you", "title", "hello! this is the [secret] I have told you about");
            res = oProxy.SendMail("me2", "you3", "a new title", "You get the point right?");
            res = oProxy.SendFax("6726721", "This is the body of the fax to be sent...");


Interception will be done for each virtual function call in a nested way as demonstrated:

1. call the method on the proxy object
2. first interceptor is invoked
3. second interceptor is invoked
4. original method on the original object is invoked
5. second interceptor has a chance to continue (and change for example the return value)
6. first interceptor has a chance to continue (and change for example the return value)

This is fine for what I was looking for. For more advanced implementations one should consider using a more comprehensive AOP framework.