Wow, what I had planned as few parts tutorial has turned to nothing less than full examination of Dynamic Proxy capabilities. Of all most important features we' have basically just one left – proxy persistence, which is what we’re going to talk about today.
Although Dynamic Proxy’s name suggests that it’s useful for… well creating proxies on the fly at runtime, there are other scenarios where the framework can be useful. We’ve seen one such scenario last time, when we created mixins, not using proxying at all.
Also the dynamic aspect of proxies is not always what we want. This is not so apparent in server application where application starts once and runs (hopefully) for a long time without restart. In the desktop applications however, we might find ourselves creating lots of identical proxies over and over again each and every time user starts the application.
This may degrade the perception of the application from users perspective, because it will take a lot of time to start. When you have many proxy types, things may get quite bad, due to bug in BCL. I call it a bug but it manifests itself by nonlinearly increasing time that creating of each subsequent proxy type takes (Mono apparently does not have this issue. I created a Connect ticket for it here. Please vote on the issue).
In some scenarios using static weaver like PostSharp will be sufficient. However it is not an option if the shape of your proxies depends on some dynamic aspects of user environment, configuration etc. In this case you may not know which proxies and how shaped until your software is on the users machine.
Let’s say you created a WPF application that allows its GUI to be extended with third party extensions, and that you use Dynamic Proxy to dynamically create ViewModels and INotifyPropertyChange on top of your POCO models. Clearly using static weaver is not an option here, because if would defy the whole point of dynamic extensibility.
However once an extension is installed, it’s really no point in recreating proxies for models in it over and over again. Ideally you would want to create them once, the first time you load the extension, and then reuse them… at least until user installs updated version of the extension.
Good news is, (as you probably suspected anyway) it is possible using Dynamic Proxy. There’s no support for determining whether or not you should reuse existing proxies, but this is very scenario specific so having any code for this in the framework itself wouldn’t make much sense anyway. Let’s look at the code.
Let’s see some code
So far we’ve been creating ProxyGenerator instances using default constructor.
var generator = new ProxyGenerator();
If we want to persist our proxies, we have to write more code:
var savePhysicalAssembly = true;
var strongAssemblyName = ModuleScope.DEFAULT_ASSEMBLY_NAME;
var strongModulePath = ModuleScope.DEFAULT_FILE_NAME;
var weakAssemblyName = "Foo.Bar.Proxies";
var weakModulePath = "Foo.Bar.Proxies.dll";
var scope = new ModuleScope(savePhysicalAssembly, strongAssemblyName, strongModulePath, weakAssemblyName, weakModulePath);
var builder = new DefaultProxyBuilder(scope);
var generator = new ProxyGenerator(builder);
We have to create ModuleScope ourselves (which we’ll later use to save the assembly to disk) passing true, as first argument, to tell DynamicProxy that we want to persist the assembly with generated types. If we didn’t do it, we would get an exception if we tried to save the assembly. We then pass two pairs of assemblyname/filename - strongly named and weakly named version. If you intend to use just one (like in this example I’m going to only save weakly named assembly) you can pass the default values for strongly typed version defined as constants on ModuleScope type.
Save me, Save me
That was the first part of the task. We then use the generator to create the proxy types we need, and when we’re done (most likely upon closing the program) we use scope to save the assembly.
The argument value of false means that we want to save the assembly without strong name.
And after the call the assembly lands safely in the designated folder under the name provided in the constructor. You can now fire up Reflector and see the inner workings of proxy types if that’s what you want.
One small catch – remember that you only can call save once on a module scope. If you don’t Dynamic Proxy will remind you:
Let’s get it back
We now have just one element missing, that is loading saved types. As I said finding the assembly itself is your responsibility. When you do, and decide to use it, you do it like this:
Assembly proxyAssembly = GetProxyAssembly();
The LoadAssemblyIntoCache method will look for proxy types in the assembly and add all it finds to the cache, so that the next time proxy generator asks for type matching one of cached one, that type will be used, instead of creating a new one.
Two things to note here:
You can load more than one assembly into the module scope’ cache (duplicates will be overriden).
You can save the dynamic assembly even after you’ve loaded types from other assemblies to its scope. Note that only types generated in this new assembly will be saved, not the ones loaded from other assemblies (which is pretty logical).
If you want to check before saving if any new types were generated, you can use the following piece of code:
Type types = scope.ObtainDynamicModuleWithWeakName().GetTypes();
//there are new types.
That would basically be it. We pretty much covered every aspect of the framework, and hopefully by now you have pretty good understanding of what and how you can achieve with it. If you think there’s something missing, or you’d like me to talk more about certain topics I already touched let me know in the comments.
08-24-2009 10:22 AM