Take a look at the following
pseudocode:
START to SaveSomething
CALL Logger with 'Entering SaveSomething'
BEGIN
IF User Has Permission
CALL PersistSomething
EXCEPTION
CALL Logger with 'Procedure Failed'
END
CALL Logger with 'Exiting SaveSomething'
END SaveSomething
If you stand-back and squint your eyes, this will probably look a little familiar. This represents that all-to-familiar pattern where we blur the lines of responsibility because its the most convenient way to accomplish things like logging, permission checks, tracing, etc... Unfortunately, this can lead to things like the following making your code much harder to read, debug, etc...
#if (DEBUG)
CALL Logger with 'Procedure Entered'
#endif
The slippery slope should be obvious, but the more important issue is that we've mixed completely separate areas of concern making our code more inflexible. In this case, authorization, logging, and entity specific domain logic are all jumbled together. Putting this specific; and possibly terrible; example aside... this may be reasonable in some cases, but there are patterns that can provide us more flexibility. Ideally we'd be able to aggregate concerns such as these without being so tightly coupled. In this case, we'll explore a combination of the Proxy Pattern and the Interceptor Pattern which are commonly found in Aspect-Oriented Programming (AOP). The combination of these patterns creates what is loosely defined as a Ghost Proxy Object, although some usages do not include interception. Lets start with a simple Domain Object with a simple operation.
public class DomainObject : IDomainObject
{
private readonly IDal _dal;
public DomainObject() : this(IoC.Resolve<IDal>()) { }
internal DomainObject(IDal injectedDal)
{
_dal = injectedDal;
}
public virtual void SaveSomething(object thing)
{
_dal.SaveSomething(thing);
}
}
The class is pretty straightforward... get an object, and then save it. From a Domain perspective, that is the only concern this component needs to have. Now lets wrap this in a proxy and add methods to wire-up externally supplied delegates. The actual code to wire-up the delegates is not really important and would be pretty trivial to implement. suffice it to say, registered delegates will be called before and after the proxied method is called. The goal is to have a class that represents the base DomainObject while providing us with opportunities to execute externally registered logic. Additionally, all this should be accomplished without any changes to; or accommodations by; the base type aside from it being inheritable.
public sealed class ProxyObject : DomainObject, IInterceptable
{
/* The real domain object instance to be called */
readonly IDomainObject _realInstance;
static IDictionary<string, BeginAction[]> _beginActions = new Dictionary<string, BeginAction[]>();
static IDictionary<string, EndAction[]> _endActions = new Dictionary<string, EndAction[]>();
public ProxyObject() : this(IoC.Resolve<IDal>()) { }
internal ProxyObject(IDal injectedDal)
{
_realInstance = new DomainObject(injectedDal);
}
public override void SaveSomething(object thing)
{
this.InvokeBeginMethod(ref _beginActions, "Void SaveSomething(System.Object)", new[] { thing });
_realInstance.SaveSomething(thing);
this.InvokeEndMethod(ref _endActions, "Void SaveSomething(System.Object)", new[] { thing }, null);
}
public void AddInterceptMethod(MemberInfo memberInfo, BeginAction beginAction, EndAction endAction)
{ /* Extension Method for instances of IInterceptable */
this.AddInterceptMethod(ref _beginActions, ref _endActions, memberInfo, beginAction, endAction);
}
public void RemoveInterceptMethod(MemberInfo memberInfo)
{ /* Extension Method for instances of IInterceptable */
this.RemoveInterceptMethod(ref _beginActions, ref _endActions, memberInfo);
}
}
Lets start by calling the un-proxied DomainObject to see the basic behavior in action.
IDomainObject realObject = new DomainObject();
realObject.SaveSomething("Foo");

Calling an instance of the proxy would produce the same result, but we need to inject additional functionality such as logging, tracing, security, etc... To do this, we must first create an instance of the Proxy and register the Delegates which will be called before and after proxied methods are called.
MethodInfo methodToIntercept = typeof(IDomainObject).GetMethod("SaveSomething");
ProxyObject proxyObject = new ProxyObject();
proxyObject.AddInterceptMethod(methodToIntercept, LogBeginAction, LogEndAction);
proxyObject.AddInterceptMethod(methodToIntercept, PermissionCheckBeginAction, null);

Once registered, any calls to new instances of the proxy type will be wrapped with the intercepting delegate calls.
IDomainObject proxyDomainObject = new ProxyObject();
proxyDomainObject.SaveSomething("Foo");

As you can see this works fine, but this could be improved by using an IoC container. This gives us the flexibility to control this behavior externally, which provides a wide range of new options. In this case, my IoC Container returns an instance of ProxyObject which is mapped to the IDomainObject interface in an external configuration file.
IDomainObject proxyDomainObject2 = IoC.Resolve<IDomainObject>();
proxyDomainObject2.SaveSomething("Foo");

These patterns can provide some really interesting capabilities although; as you can see; they require some investment in additional code. These days, proxies such as this can be automatically created using Code Generation or Dynamic Proxy tools (here is a sample). Another important consideration is performance... Profile your application and you'll see more in memory which means more to be GC'd. In the end, this means things will be slower. Like anything else, this is all about options, so take a look and see where this pattern might fit into your tool bag. Enjoy...
Tags: side projects, design patterns, code generation