Introduction
Recently, I got a chance to play with Castle DynamicProxy a bit and did some comparisons between it and Dynamic Decorator. I found some interesting points and would like to share in this blog.
They are similar in that both use proxy concept. Both can be used to extend object functionality and add aspects to objects without modifying existing classes. The difference is that Dynamic Decorator overrides .NET's RealProxy and reuses its transparent proxy technology while Castle DynamicProxy uses a customized proxy technology. Your can find more information about them in the following links:
The CreateProxy
method of the ObjectProxyFactory
class in the Dynamic Decorator is mostly close to CreateInterfaceProxyWithTarget
method of the ProxyGenerator
class in the Castle DynamicProxy. The test is limited to use them to add some preprocessing functionality to an existing object.
Test Code
In this test, we try to add some logging functionality to an existing object. The following is the code for this test.
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.Write("Calling: " + invocation.Method.Name + "\n");
invocation.Proceed();
}
}
class Program
{
static void Main(string[] args)
{
string path = Path.GetDirectoryName
(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
FileStream fileStream = null;
if (!File.Exists(path + "\\hrlog.txt"))
{
fileStream = new FileStream(path + "\\hrlog.txt", FileMode.Create);
}
else
fileStream = new FileStream(path + "\\hrlog.txt", FileMode.Truncate);
TextWriter tmp = Console.Out;
StreamWriter sw1 = new StreamWriter(fileStream);
Console.SetOut(sw1);
IEmployee emp = new Employee(1, "John", "Smith", new DateTime(1990, 4, 1), 1);
IEmployee iemp = null;
System.Int32? id = null;
System.String detail = "";
DateTime? dtStart = null;
TimeSpan? ts = null;
dtStart = DateTime.Now;
for (int i = 0; i < 1000; i++)
{
ProxyGenerator generator = new ProxyGenerator();
iemp = generator.CreateInterfaceProxyWithTarget<IEmployee>
(emp, new LoggingInterceptor());
id = iemp.EmployeeID;
detail = iemp.DetailsByLevel(2);
}
ts = DateTime.Now - dtStart.Value;
Console.Write("Using Castle CreateInterfaceProxyWithTarget: " +
ts.Value.ToString() + "\n");
dtStart = DateTime.Now;
for (int i = 0; i < 1000; i++)
{
iemp = (IEmployee)ObjectProxyFactory.CreateProxy(
emp,
new String[] { "DetailsByLevel" },
new Decoration((x, y) =>
{
Console.Write("Calling: " + x.CallCtx.MethodName + "\n");
}, null),
null);
id = iemp.DepartmentID;
detail = iemp.DetailsByLevel(2);
}
ts = DateTime.Now - dtStart.Value;
Console.Write("Using Dynamic Decorator: " + ts.Value.ToString() + "\n");
emp = (IEmployee)ObjectProxyFactory.CreateProxy(
emp,
new String[] { "DetailsByLevel" },
new Decoration((x, y) =>
{
IMethodCallMessage method = x.CallCtx;
string str = "Entering " + x.Target.GetType().ToString() +
"." + method.MethodName +
"(";
int i = 0;
foreach (object o in method.Args)
{
if (i > 0)
str = str + ", ";
str = str + o.ToString();
}
str = str + ")";
Console.WriteLine(str);
Console.Out.Flush();
}, null),
null);
id = emp.DepartmentID;
detail = emp.DetailsByLevel(2);
Console.SetOut(tmp);
sw1.Close();
fileStream.Close();
}
}
Performance
The first part of the code compares Castle DynamicProxy's performance with Dynamic Decorator's. In the above code, an object emp
of class Employee
is created. The first loop simply calls CreateInterfaceProxyWithTarget
method of the ProxyGenerator
class of the Castle DynamicProxy to create a proxy, then, uses the proxy to call the methods of the object emp
for 1000 times. The second loop simply calls CreateProxy
method of the ObjectProxyFactory
class of the Dynamic Decorator, then, uses the proxy to call the methods of the object emp
for 1000 times. The execution time of each loop is presented in the following table.
Castle DynamicProxy - First Loop |
Dynamic Decorator - Second Loop |
00:01:13.4976480 |
00:00:00.0781225 |
The Dynamic Decorator is orders of magnitude faster than the Castle DynamicProxy for doing exactly the same thing. Actually, the execution time for Dynamic Decorator is negligible. The Dynamic Decorator is clearly a winner in terms of performance.
Features
The following paragraphs discuss the feature differences between Dynamic Decorator and Castle DynamicProxy.
With Castle DynamicProxy, the preprocessing functionality is added to all methods of the object. There is no easy way to specify a particular method to decorate. It is either all methods or no methods to get the additional functionality. With Dynamic Decorator, however, individual methods can be specified. For instance, in the above code, we specify only the method DetailsByLevel
when calling ObjectProxyFactory.CreateProxy
of the Dynamic Decorator.
With Dynamic Decorator, you can access the method call context from within additional functionality. For example, in the above code, we are able to get the value of the parameter of the method DetailsByLevel
. I am not aware of a way to do it in the Castle DynamicProxy.
From the above code, you see the proxy of the Dynamic Decorator can be chained. The variable emp
originally references to the target object. And later, it references to the proxy of the target. You can keep doing this to create a chain of proxies each time adding some extra functionality.
There are other features of Dynamic Decorator which can be used to enhance the preprocessing and postprocessing functionality. Please see the article Add Aspects to Object Using Dynamic Decorator for details.
The Dynamic Decorator has more features which can be used to enhance the preprocessing and postprocessing code.
Conclusion
The Dynamic Decorator is specifically designed to add additional functionality to objects. It is a lightweight and feature-rich tool to extend object functionality and to add aspects to objects. On the other hand, although the Castle DynamicProxy can be used to add aspects to objects, it suffers performance issue and has limited features to enhance the aspects.