Before you read any further please see these much more tested and performant proxy solutions:
Be warned: What you are about to see is untested and most likely unperformant, unstable, unfriendly, and unsupported. Don't say I didn't warn you...
However, if you are like me and you just want a tiny solution to call your own, or even just wanting to learn something about dynamic proxy generation then please proceed!
Today I decided to try my hand at making a very simple, single-class proxy. The purpose for something like this would be to be able to implement an interface at run time to either wrap some kind of communication / IO layer or proxy an existing instance of that interface allowing me to intercept method and property interactions.
For example: Let's say I have an application that has a bunch of services that it interacts with. After the application is already written I decide I need to start logging information about each call to my services. I really don't want to go into every method on every service and add calls to a logger. Nor do I want to change the way my application interacts with my services. I'm way too lazy for that!
One annoying way to solve this problem would be to write a whole new class for each service interface that keeps an instance of the actual service and forwards calls to it after logging. Not the most desirable solution since I now have to write even more code than I was when I was just adding logging to the actual service methods. On top of this I would now have 2 classes to maintain if the service's interface is ever added to or changed.
What I really want to do is automate or generate this wrapper dynamically, yet still be able to function in a strongly typed environment. As I've stated before there are a number of existing solutions that will allow me to do this, but for this post I wanted to dive a little deeper and see how this stuff works for myself.
What it does:
Below I have an abstract Proxy<T> base class. This doesn't need to be abstract, but isn't terribly useful by itself as I have written it. The point of this implementation would be to extend this to a concrete class that is more useful for a specific use-case (of which I have several).
(This is a huge blob of code I'll explain and break down later)
Let's focus on the example at hand. I want to use this class now to generate proxies for services on the fly. To keep this dynamic I will want to extend the Proxy<T> base class into something like this:
Now wherever I am instantiating a service I will simply wrap it with a ServiceLogger<T> and use the ServiceLogger<T>.Service in place of my service. Any code with a dependency on a service of type T will not know the difference between the proxy and the real service and so will not have to be altered.
The "willBeLogged" and the "wontBeLogged" objects both have the same dependency satisfied. One with a proxy, and the other with the direct service. The one with the proxy will have all of its calls to the proxy logged without having to change anything in the "DependentCode" class.
How it works:All the code you write in .NET eventually becomes IL (Intermediate Language) when it is compiled. Even though you've written your application in C#, your application is "thinking" in IL (well, not really). Therefore you can't directly tell your application to just run a block of dynamically generated C# code at run-time without some kind of other service to compile your code down into IL. Fortunately for you there is no need to seek the services of a compiler because the .NET framework provides you with the ability to write your own IL.
The first thing you'll need to do in order to start building your own dynamic types is create a dynamic assembly in which to store it. As it is, all of the assemblies in your application are loaded into memory from DLLs and the like. Once an assembly is loaded into your AppDomain you will no longer be able to edit it. So you need to expect that each dynamic Type that you want to create will need to be housed in its own dynamic assembly.
Because of this, if you are not careful, you will have a memory leak as you create more and more dynamic types and will need to make sure that you are only creating new types when necessary. For this I have added a static field to the base Proxy<T> class that holds the dynamic type which is then generated in the static constructor. Normally I avoid adding static fields to generic classes because a new value is generated for each "T" used, however in this case it is exactly what I want.
Now in the instance constructor I simply construct an instance of the type referenced by the static InstanceType field. In a single-threaded environment this will be enough to prevent any leaks. (maybe also in multi-threaded environments?)
The "Generate()" method will create a dynamic type that inherits the "T" interface. The dynamic type will be given a private _proxy field that will reference the owning "Proxy<T>" instance. Each method on the dynamic class will then call the "_proxy.Trigger(...)" method with the appropriate information. The "Trigger" method will call the "BeforeCall", "OnCall", and "AfterCall" virtual methods which can then be overridden by your own subclass to perform whatever interception actions you want.
When generation is complete the dynamically created type resembles a class that would look something like this:
The rest is pretty straight-forward even though it doesn't seem so. If you take a peek into the "Generate" method you will see the following steps taken:
- Create a dynamic Assembly in the current AppDomain
- Create a dynamic Module in the dynamic Assembly
- Create a dynamic Type in the dynamic Module
- Create Fields, Properties, and Methods in the dynamic Type
- "Build" the new System.Type
Defining a member on a dynamic type is as easy as ".DefineField", ".DefineMethod", or ".DefineProperty". Obviously underlying methods won't magically know what to do when they are invoked. This is where all the IL encoding comes in.
I'm not, however, going to dive into how to write functionality with IL in this post. My own understanding of how to write IL is very basic and most of that has come from compiling assemblies and looking at what the compiler generates. Nevertheless, I have made sure to leave comments on each Emit statement to help explain what's happening. You can see these in the "ConfigureCall" method.
Did you like this post?
Let me know: http://markonthenet.com/contact.htm