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.