Brushing up old APIs with Castle DynamicProxy
Old APIs?
Recently I had the pleasure of rebuilding a B2B ecom site for a local butchery. It's never been properly integrated with their ERP, but the new site is. The front-end is built using Umbraco 9 and Vendr and hosted on Azure, while the integration side is an ASPNET Core WebAPI solution running closer to the ERP-system. They are using an ERP system that's been around since the nineties. Of course it's been upgraded and modernized throughout the years, but the core APIs are still the same as they've been since then. The maturity of the software says enough about the architecture. We can't say that it didn't work, wasn't maintainable or not durable.
However, programming styles have changed over the years. Back then and earlier it was perfectly sensible to inform function callers about runtime errors and exceptions using integer return codes. Executable binaries still do, though most just exit with a 0. Usually a 0 means all is well while any non-zero value means something didn't go well.
Another thing with such unmanaged APIs is that we have to go through interop. That means we have to mind releasing absolutely all resources we instantiate.
Hence every managed example for this ancient (forgive me) API goes something like this:
Server server = new Server();
int result = server.LogOn();
if (result != 0) {
goto Fail;
}
ComponentA component = (ComponentA)server.Components[(int)ComponentTypesEnum.ComponentA];
result = component.SetupData();
if (result != 1) {
goto Fail;
}
result = component.BindData();
if (result != -1) {
goto Fail;
}
result = component.DoFirstThing();
if (result != 0) {
goto Fail;
}
result = component.DoSecoondThing();
if (result != 0) {
goto Fail;
}
goto Cleanup;
Fail:
component.Rollback();
Cleanup:
Marshal.ReleaseComComponent(component);
Marshal.ReleaseComComponent(server);
Did you notice any duplication in there? The examples sport a comment that will stay in there forever, but I found it amusing to add a goto statement. Most would likely have bundled a bunch of nested ifs?
We could maybe throw in each if, and have an outer try...
The awake reader will also notice that the first two ifs for the component's common SetupData
and BindData
methods test for other values than zero.
Said API is like that, and the reason for this is still a mystery to me. One day I might get to know.
Note that both Server
and ComponentA
are interfaces. They don't follow the .net convention of prefixing an I
.
Then finally of course we have to do this fancy static call to release each of the unmanaged instances.
For orders sake, I intentionally wrote the example in C# 1.0 style to drive the nail in.
How should it be?
In the .NET world we're used to things throwing exceptions. Things we have to clean up are all "disposable". Wouldn't it be nice if the code above was as such too? (To save some bytes and indenting we're now doing C# 8.0)
using var server = ComponentFactory.CreateServer();
using var component = server.CreateComponent<IComponentA>();
try
{
server.LogOn();
component.Initialize();
component.DoFirstThing();
component.DoSecoondThing();
}
catch(CustomAPIException ex)
{
component.Rollback();
SomeLogger.LogError(ex);
throw;
}
I'm feeling confident I don't have to argue the benefit of the latter example.
So how do we make the API work like this instead? Hopefully not by wrapping absolutely all the components
with hand crafted code. Not generated classes either, if all they contain are the duplicated four lines of code per call.
Enter Castle.DynamicProxy!
Dynamic proxies
If you've ever used ORMs like NHibernate and Entity Framework, mocking libraries like Moq and RhinoMocks,
webservice proxies and whatnot, you've probably been using them without even knowing. Dependency Injection Frameworks (Especially Castle Windsor) may also provide fancy mechanisms to decorate without adding more implementations.
Dynamic Proxies are a mechanism to generate code at runtime. If you have a non-sealed class, you can have a derived class built at runtime, and then instantiate that one.
The wonderful thing about them is that they can be used to wrap existing implementations. (not only yors) Take the example of the return code in the ancient API. Wouldn't it be nice if all the methods did that zero-test automagically, and then threw an exception if they failed?
Creating proxies
A first requirement is to take control over the instantiation. In the example we're using the
Server
instance is the factory for all components. All of those are already proxies,
although they are "made" using Microsofts proprietary .NET Framework interop technology and DCOM. (Proprietary Windows distributed transaction handling)
That makes the system able to handle transactional business logic on a central server.
We have no control over those proxies, though, so we have to wrap and swap out that Components[ComponentTypesEnum type]
indexer with something we control.
We want to use Castle DynamicProxy to add an IInterceptor
that can intercept calls to the component.
In my solution I came up with this extension that just makes it look like we have a generic overload on Server
. One could investigate ways of preventing calls to the "old" one if convension is not enough.
Code to create a "component" becomes server.CreateComponent<T>(ComponentTypeEnum type)
, which seems a bit more "factoryish". We still have the enum in there, but I'll leave my suggested fix for later. (Writing the blogpost, I cringe at the missing opportunity to remove it...)
To be able to add an interceptor to all calls to a type we use a ProxyGenerator
instance, and call it's CreateInterfaceProxyWithTarget
method. To which we pass the component itself, and one or more IInterceptor
implementations.
public static class ComponentFactory
{
private static readonly ProxyGenerator Generator = new ProxyGenerator();
public static T Create<T>(this Server server, ComponentTypeEnum componentType)
{
var component = server.Components[(int)componentType];
return Decorate<T>(component);
}
public static T Decorate<T>(object component)
{
var proxy = (T)Generator.CreateInterfaceProxyWithTarget(
component,
new ErrorWhenResultNotZeroInterceptor()
);
return proxy;
}
// ...
}
As you can see we still use the Components[index]
"factory" indexer to create the requested component, but we use Castle DynamicProxy to add our ErrorWhenResultNotZeroInterceptor
.
The decorating error handler
An IInterceptor
implementation requires one single method to be implemented: Intercept(IInvocation invocation)
. It will be called for any extendable or implementable member on the decorated instance.
In order for the decorated instance to be called at all, we must call invocation.Proceed()
.
Otherwise, we've basically create a "no-op" override.
The IInvocation
instance contains information about the member intercepted, the return value after invocation etc. We're certainly interested in the return value.
But remember we have those two rogue methods that don't return zero for success? That's where the reflection info comes in handy. Here's the entire code for the "convert non-zero to exception" mechanism:
public class ErrorWhenResultNotZeroInterceptor : IInterceptor
{
private static readonly Dictionary<string, int> okValues = new Dictionary<string, int>
{
{ "SetupData", 1 },
{ "BindData", -1 }
};
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
var result = (int)invocation.ReturnValue;
var methodName = invocation.Method.Name;
var expectedSuccess = okValues.ContainsKey(methodName) ? okValues[methodName] : 0;
if (result != expectedSuccess)
{
throw new CustomAPIException(result, methodName);
}
}
}
Et voilá! Now any time we call a component's methods and it doesn't return a zero, an exception is thrown instead.
We've handled the biggest annoyance, and can now write code as such:
try
{
server.LogOn();
component.SetupData();
component.BindData();
component.DoFirstThing();
component.DoSecoondThing();
}
catch(CustomAPIException ex)
{
component.Rollback();
throw;
}
As you might notice, my ideal code had component.Initialize()
instead of the Setup/Bind
pair. I leave it as an excercise for the reader to create that extension.
Disposability
To intercept calls to IDisposable.Dispose
and replace them with calls to Marshal.ReleaseComObject
requires a fairly simple IInterceptor
implementation:
public class DisposableInterceptor : IInterceptor
{
private const int MaxReleases = 100;
private readonly object real;
private readonly Guid id = Guid.NewGuid();
public DisposableInterceptor(object real)
{
this.real = real;
}
public void Intercept(IInvocation invocation)
{
if (invocation.Method.Name != "Dispose") return;
var i = 0;
var result = -1;
while (result != 0 && i++ < MaxReleases)
{
result = Marshal.ReleaseComObject(real);
}
}
}
But the whole endeavour is about to get quite a bit more painful. There might be easier ways, but I actually ended up generating some code pre-compile time for this trick. However, there's only one line of code per component in the API. (Two, really, but hey...)
Thing is that neither the core Component
interface, nor the derived IndividualComponent
interfaces implement the IDisposable
interface. So we have to create our own IDisposableIndividualComponent
interfaces that inherit both the individual one and IDisposable
.
For me the most natural step was to create an explicitly ran unit test I can run at will. It's really a one off, but the tests were great for getting there:
[TestFixture]
[Explicit("Run this to generate pastable code with disposable versions of API components")]
public class CodeGeneration
{
[Test]
public void GenerateDisposableCustomComponents()
{
var types = GetComponentTypes();
foreach (var type in types)
{
Console.WriteLine($"public interface I{type.Name} : {type.Name}, IDisposable {{}}");
}
}
[Test]
public void GenerateTypeMapForFactory()
{
var types = GetComponentTypes();
foreach (var type in types)
{
Console.WriteLine($"{{ typeof(I{type.Name}), typeof({type.Name}) }},");
}
}
private static IOrderedEnumerable<Type> GetComponentTypes()
{
var baseType = typeof(IComponent);
var allTypes = baseType.Assembly.GetModules().SelectMany(m => ModuleTypes(m));
var types = allTypes
.Where(t => t.IsInterface && baseType.IsAssignableFrom(t) && !t.Name.StartsWith("I"))
.OrderBy(t => t.Name);
return types;
}
private static Type[] ModuleTypes(Module m)
{
try
{
return m.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
return e.Types.Where(t => t != null).ToArray();
}
}
}
The first test generates one line per component as follows. We're somewhat in luck as well. The API in question doesn't follow the .net convension of prefixing interface names with I
, so we don't have to invent any ingenious pre- or suffix to our interface names.
public interface IServer : Server, IDisposable { }
public interface IComponentB : ComponentB, IDisposable { }
public interface IComponentC : ComponentC, IDisposable { }
// ...
(Yes, the API sports better names that ComponentA, B etc. I'm just obfuscating it.)
The second test generates a map for the factory to find the right interface to decorate the actual one with:
private static readonly Dictionary<Type, Type> TypeMap = new()
{
{ typeof(IServer), typeof(Server) },
{ typeof(IComponentB), typeof(ComponentB) },
{ typeof(IComponentC), typeof(ComponentC) },
// ...
}
Likely both groups of "one-liners" per component could've been generated at runtime as well, but I'm sure this'll save some CPU cycles.
This is the place to suggest that the enum parameter could also get its own map and we could ditch that last factory argument.
The factory can now be updated to create instances of our disposable interfaces instead of the original interfaces. Castle DynamicProxy's ProxyGenerator
has another factory method called CreateInterfaceProxyWithTargetInterface
. It takes a bunch more arguments than the one we've used till now. It needs the new disposable type, the original implementation type, the actual component and the decorators.
Additionally, we need to discriminate between calls to the Dispose
method, calls to methods returning int
and other methods when we intercept. We don't want either interceptor to run on incompatible methods.
The factory method also accepts an options instance where we can pass something called an IInterceptorSelector
.
Here's the complete factory code looking up the types and sewing it all together:
public static class ComponentFactory
{
private static readonly ProxyGenerator Generator = new ProxyGenerator();
private static readonly ProxyGenerationOptions Options = new ProxyGenerationOptions
{
Selector = new InterceptorSelector()
};
public static T Create<T>(this Server server, ComponentTypeEnum componentType)
{
var component = server.Components[(int)componentType];
return DecorateWithDisposable<T>(component);
}
public static T DecorateWithDisposable<T>(object component)
{
var targetType = TypeMap.ContainsKey(typeof(T)) ? TypeMap[typeof(T)] : typeof(T);
var implemented = TypeMap.ContainsKey(typeof(T))
? new[] { typeof(IDisposable), typeof(T) }
: new[] { typeof(IDisposable) };
var proxy = (T)Generator.CreateInterfaceProxyWithTargetInterface(
targetType,
implemented,
component,
Options,
new DisposableInterceptor(component),
new ErrorWhenResultNotZeroInterceptor()
);
return proxy;
}
public static Server CreateServer()
{
return DecorateWithDisposable<Server>(new ServerClass());
}
private static readonly Dictionary<Type, Type> TypeMap = new() {
// ...
}
}
The interceptor selector then does some "simple" filtering (and a few nice modern C# pattern matches) to select the relevant interceptor:
public class InterceptorSelector : IInterceptorSelector
{
public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors)
{
if (method.ReturnType != typeof(int))
interceptors = interceptors.Where(x => x is not ErrorWhenResultNotZeroInterceptor).ToArray();
if (method.DeclaringType == typeof(IDisposable))
return interceptors;
return interceptors.Where(x => x is not DisposableInterceptor).ToArray();
}
}
And that's it! We can now write code like such:
using var server = ComponentFactory.CreateServer();
using var component = server.CreateComponent<IComponentA>();
try
{
server.LogOn();
component.Initialize();
component.DoFirstThing();
component.DoSecoondThing();
}
catch(CustomAPIException ex)
{
component.Rollback();
SomeLogger.LogError(ex);
throw;
}
Summary
So we've gone on a deep dive through both "easy" extensions and more complex ones like adding disposability. I really hope it sparked some inspiration.
I'm sure there are tons of ways to utilize this, but make sure to use it as the right tool for the right job, and not for the sake of using it.
For one, the decorator pattern is way easier to apply to large interfaces than generating tons of wrapper code to decorate only a few of the methods on a type. I usually write individual classes for each use case (read command or query), so generic decorators can be as simple as IInterceptor
implementations anyway.
But for APIs not built like this, Castle DynamicProxy makes it a breeze!
For debugging purposes, turning on and off a console logger that prints each method called and its result is also quite nice. Especially within unit-tests.
Got any other great ideas? Drop them in the comments.
Thanks for reading!