On the Nature of Segmenting Contracts
Not long after wrapping up the publishing of some of the foundational aspects of my Extensibility framework to my Bad Echo Technologies repo, I needed to start working on another side of the framework: support for my IHostAdapter
interface. Implementations of this interface are responsible for establishing connections between a host and several routable plugin adapters that segment a common contract.
What does it mean to segment a contract? Let’s, for instance, say we have an interface named IContract
, with the methods IContract.MethodOne
and IContract.MethodTwo
. Then, let’s also assume we have two plugin assemblies, PluginA.dll and PluginB.dll, each with their own implementation of IContract
. If we create a proxy that will route IContract
method calls to PluginA.dll and PluginB.dll based on the particular method being called, then we have essentially segmented the IContract
contract.
Segmenting IContract: An Illustration
The inspiration behind this concept came from one of the more significant kinds of creative drivers: attempting to sell a business proposition. In my first job out of college, I was working with a client/server software solution that had a lot of market potential, but was plagued with horrible technological burdens. It was one of those pieces of software that indeed needed a complete rewrite, but of course I needed to convince the business owners to allow me to embark on such an endeavor.
I was able to convince the owner to allow me to rewrite it by telling him I would develop a shiny new frontend client first, and only after that would I spend time redeveloping the backend. This would allow the company to begin to market and sell the shiny new frontend after a much shorter period of time than what would be required if we went about it the traditional way (making the backend first and then the frontend). So, essentially, a quicker capturing of profits from the rewrite effort.
Because the new frontend would be operating against a brand new system service contract, I needed a way to reroute calls made to this new contract to the old backend. As the new backend was being developed, I would slowly shift responsibility for what ultimately processed service calls to it. In order to achieve all of this, I came up with this segmented contract approach.
On the Nature of Generating Proxy Objects
This article isn’t about the complete implementation of a contract segmentation solution, but rather how I went about generating the proxy objects that the solution requires while working on reorienting my treasure trove of code to run on top of .NET 5.0.
Typically when we’re talking about proxy objects, we’re talking about them in the context of mocks, stubs, and/or fakes. Fake objects adhering to some kind of interface that we configure at runtime to behave in a manner that aids in the process of testing a particular piece of code. What we’re doing here is very much a non-testing concern, but may benefit from the same bits of technology that testing-oriented frameworks that provide such objects use.
That’s how I approached the problem originally, and it led me to end up using Castle.Core for my original solution. This is a very well known third-party library, and it makes available a runtime proxy generator known as Castle DynamicProxy. Using this library I satisfied the proxy-related concerns of contract segmentation by using Castle’s ProxyGenerator
class and a custom IInterceptor
implementation in order to reroute an interface’s method calls.
It’s my goal to avoid excessive reliance on third-party libraries with the Bad Echo core frameworks library, the general-purpose library I’m building, but I decided to just swallow my pride a bit and add the Castle.Core NuGet package to the project. It was then that I saw that Castle.Core targets an older version of .NET Standard, and the amount of dependencies it wanted to bring along with it disturbed me slightly, making me uneasy enough to stop what I was doing.
Another stumbling stone encountered. I started to think (and hope) about whether or not something was built into Microsoft’s latest .NET installment, namely .NET 5.0, that could help me generate proxy objects. And, to my surprise, there was!
Making a Routable Proxy with DispatchProxy
The DispatchProxy
class is (from what I can tell) new to .NET Core and is a very nice way of going about generating proxy objects if we’re building on top of .NET 5.0. Looking at its source code, it is evident that it makes use of Reflection.Emit
, just like Castle.Core does, to create a dynamic proxy at runtime.
If a built-in solution satisfies requirements just as well as a third-party solution, I will 99% of the time gravitate towards adopting the built-in approach. That being said, I would very much be interested in measuring and analyzing performance differences between DispatchProxy
and Castle.Core’s own proxy generating classes. That can be done some other day, however. I will give DispatchProxy
the benefit of the doubt until then.
It wasn’t long until some annoyances popped out at me when attempting to use DispatchProxy
to create my routable proxy:
- When generating the proxy type (the type deriving from
DispatchProxy
), it’s not possible to pass any arguments via the constructor. Since we need to provide it with aIHostAdapter
to do the actual routing, we would need to instead feed it to the object by setting an exposed field. - A public parameterless constructor must be exposed on the proxy type in order for it to be instantiated properly.
The above issues make it impossible to force construction of our proxy type so it can only occur through the use of a public static factory method, which would ensure that the proxy type was fully initialized with the IHostAdapter
loaded into it; specifically, because we’re required to have a public constructor. That means a consumer could initialize it by invoking the public constructor and have a bunk proxy object.
There’s really no way around this, the proxy type itself must be public, so I can’t have an internal or private object that I create and make available by a public, sanitized type. It’s a minor problem, and we do the best that we can in this situation by at least throwing an exception to alert a consumer that they initialized the type wrong.
That being said, let’s take a look at RoutableProxy
, based on .NET 5.0’s DispatchProxy
.
RoutableProxy Class
/// <summary> /// Provides a proxy object that handles method dispatch by routing segmented contract method calls /// to the call-routable plugins pointed to by a provided <see cref="IHostAdapter"/> instance. /// </summary> /// <suppresions> /// ReSharper disable EmptyConstructor /// </suppresions> public class RoutableProxy : DispatchProxy { private IHostAdapter? _adapter; /// <summary> /// Initializes a new instance of the <see cref="RoutableProxy"/> class. /// </summary> /// <remarks> /// Consumers should not initialize this object by calling this constructor directly. It exists /// only to fulfill requirements of <see cref="DispatchProxy"/>. /// </remarks> public RoutableProxy() { } /// <summary> /// Creates an instance of <typeparamref name="T"/> that will route method calls through the /// provided host adapter. /// </summary> /// <param name="adapter">The host adapter to route calls through.</param> /// <returns> /// An object instance implementing <typeparamref name="T"/> that will route calls through /// <c>adapter</c>. /// </returns> /// <typeparam name="T"> /// The segmented contract type whose calls will be routed through the proxy. /// </typeparam> public static T Create<T>(IHostAdapter adapter) where T : class { if (adapter == null) throw new ArgumentNullException(nameof(adapter)); object proxy = Create<T, RoutableProxy>(); var routableProxy = (RoutableProxy)proxy; routableProxy._adapter = adapter; return (T) proxy; } /// <inheritdoc/> protected override object? Invoke(MethodInfo? targetMethod, object?[]? args) { if (_adapter == null) throw new InvalidOperationException(Strings.RoutableProxyNotInitializedCorrectly); // It is unclear to me as to how targetMethod ever ends up being null. // Exception won't be thrown since, by virtue of the method signature, we must support null // values for this argument. if (targetMethod == null) { Logger.Warning(Strings.RoutableProxyNullMethodInfo); return null; } object contractImpl = _adapter.Route(targetMethod.Name); return targetMethod.Invoke(contractImpl, args); } }
We now have the means to segment a contract of our choice. Let’s take a peek at some sample usage of this type to segment the IContract
type we discussed earlier.
Segmenting IContract With RoutableProxy
public void SegmentContract(IHostAdapter adapter) { IContract proxy = RoutableProxy.Create<IContract>(adapter); // This will call IContract.MethodOne in PluginA.dll. proxy.MethodOne(); // This will call IContract.MethodTwo in PluginB.dll. proxy.MethodTwo(); }
There you go, a nice segmented contract.
How Does IHostAdapter Work?
That’s outside the scope of this article, but maybe I’ll write it about it in the future. Until then, pay attention to the Bad Echo Technologies repo to see how it works and how it gets used with RoutableProxy
.
As always, take care and thank you for your time in reading this.