Showing posts with label AOP. Show all posts
Showing posts with label AOP. Show all posts

Monday, April 18, 2011

Testing Asynchronous Logic

I've been doing a fair bit of asynchronous logic recently where I'm designing objects to raise events after long-running operations have completed, and naturally this creates a few challenges during testing. When a method uses the asynchronous pattern, the work is executed on a separate thread and execution returns immediately to the caller. This is a problem for the a unit test as they are by design a series of standalone sequential steps -- the test may finish or execute assertions before the event is called.

The immediate workaround is to add delays into the tests using a Thread.Sleep statement.

[Test]
public void AsynchronousTestsDontAlwaysWork()
{
    bool eventCalled = false;
    
    var subject = new MyClass();
    subject.OnComplete += (o,e) => eventCalled = true;
    
    subject.DoWorkAsync();
    
    Thread.Sleep(100);
    
    Assert.AreTrue( eventCalled ); // can fail because the event might raise after this assertion
}

This strategy will work, but it's dangerous in the long-term because the duration of the asynchronous operation is dependent on the complexity of the implementation and the environment the test is executing in.  In other words, you may need to increase sleep interval if the implementation needs to do more work or if the tests fail on slower machines. In the end, the sleep interval reflects the lowest common denominator in your environment (that slow laptop that intern uses) and the entire test run becomes painfully (and unnecessarily) slow.

The solution is to add a little extra plumbing to block execution until the event is fired.

[Test]
public void AsynchronousTestWithBlockingAlwaysWorks()
{
    var reset = new System.Threading.ManualResetEvent(false);
    
    var subject = new MyClass();
    subject.OnComplete += (o,e) => reset.Set();
    
    subject.DoWorkAsync();
    
    if (!reset.WaitOne(1000)) // block for at most 1 second
    {
        Assert.Fail("The test timed out waiting for the OnComplete event.");
    }
}

The above example uses the ManualResetEvent, which in short is a signalling mechanism. The WaitOne method blocks execution until another thread calls the Set method or the timeout value is exceeded. In the above example, I've set up an anonymous delegate for my event so that the test finishes as soon as the asynchronous logic completes.

Cleaning it up...

As I mentioned earlier, I've been doing a lot of this type of testing so the above pattern has appeared in more than one test. It would be nice to encapsulate this a bit and streamline it into a reusable utility. This post on StackOverflow had an interesting concept to wrap the blocking logic into an object that supports IDisposable. The original poster was attempting to use Expressions to access events, which sadly isn't possible from outside the class, but here's an updated example that uses reflection and a named parameter for the event:

public sealed class EventWatcher : IDisposable
{
    private readonly object _target;
    private readonly EventInfo _eventInfo;
    private readonly Delegate _listener;
    private readonly ManualResetEvent _reset;
    private readonly int _timeout;

    public static EventWatcher Create<T>(T target, string eventName, int timeout = 1000)
    {
        EventInfo eventInfo = typeof (T).GetEvent(eventName);
        if (eventInfo == null)
            throw new ArgumentException("Event not found.", "eventName");

        return new EventWatcher(target, eventInfo, timeout);
    }

    private EventWatcher(object target, EventInfo eventInfo, int timeout)
    {
        _target = target;
        _eventInfo = eventInfo;
        _timeout = timeout;

        _reset = new ManualResetEvent(false);

        // Create our event listener
        _listener = CreateEventListenerDelegate(eventInfo.EventHandlerType);

        _eventInfo.AddEventHandler(target, _listener);
    }

    public void SetEventWasRaised()
    {
        _reset.Set();
    }

    private Delegate CreateEventListenerDelegate(Type eventType)
    {
        // Create the event listener's body, setting the 'eventWasRaised_' field.
        var setMethod = typeof(EventWatcher).GetMethod("SetEventWasRaised");
        var body = Expression.Call(Expression.Constant(this), setMethod);

        // Get the event delegate's parameters from its 'Invoke' method.
        MethodInfo invokeMethod = eventType.GetMethod("Invoke");
        var parameters = invokeMethod.GetParameters()
            .Select((p) => Expression.Parameter(p.ParameterType, p.Name));

        // Create the listener.
        var listener = Expression.Lambda(eventType, body, parameters);
        return listener.Compile();
    }

    public void Dispose()
    {
        bool eventWasFired = _reset.WaitOne(_timeout);
        // Remove the event listener.
        _eventInfo.RemoveEventHandler(_target,_listener);

        if (!eventWasFired)
        {
            string message = String.Format("Test timed-out waiting for event \"{0}\" to be raised.", _eventInfo.Name);
            Assert.Fail(message);
        }
    }
}

[Test]
public void DemonstrateEventWatcherWithUsingBlockAndDisposePattern()
{
    var subject = new MyClass();
    
    using(EventWatcher.Create(subject, "OnComplete"))
    {
        subject.DoWorkAsync();
        
    } // throws Assert.Fail() if event wasn't called.
}

Some Refinements...

This solution works, but there are a few drawbacks:

  1. The using block is a very succinct mechanism for controlling scope, but unfortunately if an exception occurs in the body of the using statement it will get swallowed by the exception in our EventWatcher's Dispose method.
  2. This only validates that the event was called, and it doesn't give us access to the event argument, which might be of interest to the test.
  3. The event name is a literal string value which isn't very refactor friendly.

Preventing Hidden Exceptions from the Finally block

To address the first problem where exceptions aren't properly handled inside the using statement, we change the signature slightly to perform the test logic in an Action.

// snip...
public static void WaitForEvent<T>(T target, string eventName, Action action) 
{ 
    var watcher = Create(target, eventName);

    try 
    { 
        action(); 
    } 
    catch (Exception) 
    { 
        watcher.Clear(); 
        throw; 
    } 
    finally 
    { 
        watcher.Dispose(); 
    } 
}
// ...end snip

[Test] 
public void DemonstrateEventWatcherWithAnActionInsteadOfUsingBlock() 
{ 
    EventWatcher.WaitForEvent(subject, "OnComplete", () => subject.DoWorkAsync()); 
}

Gaining Access to the EventArgs

Addressing the second problem requires some changes to the CreateEventListenerDelegate so that the arguments of the event are recorded.

//snip
public void SetEventWasRaised(EventArgs e) 
{ 
    EventArg = e; 
    _reset.Set(); 
}

public EventArgs EventArg { get; private set; }

private Delegate CreateEventListenerDelegate(Type eventType) 
{ 
    // Get the event delegate's parameters from its 'Invoke' method. 
    MethodInfo invokeMethod = eventType.GetMethod("Invoke"); 
    var parameters = invokeMethod.GetParameters() 
        .Select((p) => Expression.Parameter(p.ParameterType, p.Name)).ToList();

    // Setup the callback to pass in the EventArgs 
    var setMethod = typeof(EventWatcher).GetMethod("SetEventWasRaised"); 
    var body = Expression.Call(Expression.Constant(this), setMethod, parameters[1]);

    // Create the listener. 
    var listener = Expression.Lambda(eventType, body, parameters); 
    return listener.Compile(); 
} // end snip

[Test] 
public void DemonstrateEventWatcherCanCaptureEventArgs() 
{ 
    EventArgs e; 
    EventWatcher.WaitForEvent(subject, "OnComplete", out e, t => t.DoWorkAsync()); 
    
    Assert.IsNotNull(e); 
} 

Making it Refactor-Friendly...

Tackling the last problem where we don't have type-safety for our event, we need a special workaround because we're unable to use an Expression to capture our event. From a language perspective, C# will only allow external callers to add or remove event handler assignments, so we can't write a lamda expression that points to an event reference. Hopefully this is something that will be added to the language in a future version, but until then accessing the event requires some special hackery.

Taking a play from the Moq source code, we can infer the event name by using a dynamic proxy and intercepting the method invocation when we perform an event assignment. It's a long and awkward way around, but it works, albeit it comes with all the caveats of using a dynamic proxy: non-sealed classes with virtual events, MarshalByRefObject, or interfaces.

A note about virtual events: I’m personally not a big fan of virtual events (Resharper warnings, etc), but if you’re comfortable putting the event declarations on an interface, you can explicitly specify the interface in the generic type argument.  EventWatcher.Create<IMyInterface>(…)

public class EventAssignmentInterceptor : Castle.DynamicProxy.IInterceptor
{
    private Castle.DynamicProxy.IInvocation _lastInvocation;

    #region IInterceptor implementation

    public void Intercept(IInvocation invocation)
    {
        _lastInvocation = invocation;
    }

    #endregion

    public string GetEventName()
    {
        string methodName = _lastInvocation.Method.Name;
        return methodName.Replace("add_", "").Replace("remove_", "");
    }
}

public class EventHelper
{
    public static string GetEventName<T>(T target, Action<T> action) where T : class
    {
        var generator = new Castle.DynamicProxy.ProxyGenerator();
        var interceptor = new EventAssignmentInterceptor();

        T proxy;

        if (typeof(T).IsInterface)
        {
            proxy = generator.CreateInterfaceProxyWithoutTarget<T>(interceptor);
        }
        else
        {
            proxy = generator.CreateClassProxy<T>(interceptor);
        }

        action(proxy);

        return interceptor.GetEventName();
    }
}

Examples

Here’s a few examples that illustrate the various syntax.  Note, for sake a brevity (this post is crazy large) the overloads that define events as literals are not shown.

Basic usage in a using block. Note that exceptions inside the using may be swallowed by the dispose, and there's no access to the event args.

using(EventWatcher.Create(subject, t => t.OnComplete += null)) 
{ 
    subject.PerformWork(); 
} 

using(EventWatcher.Create(subject, t => t.OnComplete += null, timeout:1000) 
{ 
    subject.PerformWork(); 
}

Condensed usage. Provides access to the EventArgs and avoids the dispose problem.

EventArgs e = EventWatcher.Create(subject, t=> t.OnComplete +=null, () => subject.PerformWork() );

EventArgs e; 
EventWatcher.Create(subject, t=> t.OnComplete += null, out e, ()=> subject.PerformWork() ); 

Fluent usage. A cleaner syntax that declares the event and the action separately.

EventArgs e = EventWatcher 
            .For(subject, t => t.OnComplete += null) 
            .WaitOne(() => subject.PerformWork()) 

EventArgs e; 
EventWatcher.For(subject, t=> t.OnComplete += null) 
            .WaitOne( () => subject.PerformWork(), out e);

Full Source

The full source is here.

public sealed class EventWatcher : IDisposable
{
    private readonly object _target;
    private readonly EventInfo _eventInfo;
    private readonly Delegate _listener;
    private readonly ManualResetEvent _reset;
    private readonly int _timeout;

    #region Create
    public static EventWatcher Create<T>(T target, string eventName, int timeout = 1000)
    {
        EventInfo eventInfo = typeof(T).GetEvent(eventName);
        if (eventInfo == null)
            throw new ArgumentException("Event not found.", "eventName");

        return new EventWatcher(target, eventInfo, timeout);
    }

    public static EventWatcher Create<T>(T subject, Action<T> eventAssignment, int timeout = 1000) where T : class
    {
        string eventName = EventHelper.GetEventName(subject, eventAssignment);

        return Create(subject, eventName, timeout);
    } 
    #endregion

    #region For
    public static EventWatcherSetup<T> For<T>(T subject, Action<T> eventAssignment) where T : class
    {
        string eventName = EventHelper.GetEventName(subject, eventAssignment);
        return For(subject, eventName);
    }

    public static EventWatcherSetup<T> For<T>(T subject, string eventName)
    {
        return new EventWatcherSetup<T>(subject, eventName);
    } 
    #endregion

    #region WaitForEvent
    public static EventArgs WaitForEvent<T>(T target, Action<T> eventAssignment, Action action) where T : class
    {
        EventArgs e;
        WaitForEvent(target, eventAssignment, out e, action);
        return e;
    }

    public static void WaitForEvent<T>(T target, Action<T> eventAssignment, out EventArgs e, Action action) where T : class
    {
        string eventName = EventHelper.GetEventName(target, eventAssignment);
        WaitForEvent(target, eventName, out e, action);
    }

    public static EventArgs WaitForEvent<T>(T target, string eventName, Action action)
    {
        EventArgs e;
        WaitForEvent(target, eventName, out e, action);
        return e;
    }

    public static void WaitForEvent<T>(T target, string eventName, out EventArgs e, Action action)
    {
        var watcher = Create(target, eventName);

        try
        {
            action();
        }
        catch (Exception)
        {
            watcher.Clear();
            throw;
        }
        finally
        {
            e = watcher.EventArg;
            watcher.Dispose();
        }
    } 
    #endregion

    private EventWatcher(object target, EventInfo eventInfo, int timeout)
    {
        _target = target;
        _eventInfo = eventInfo;
        _timeout = timeout;

        _reset = new ManualResetEvent(false);

        // Create our event listener
        _listener = CreateEventListenerDelegate(eventInfo.EventHandlerType);

        _eventInfo.AddEventHandler(target, _listener);
    }

    public void SetEventWasRaised(EventArgs e)
    {
        EventArg = e;
        _reset.Set();
    }

    public void Clear()
    {
        _reset.Set();
    }

    public EventArgs EventArg { get; private set; }

    private Delegate CreateEventListenerDelegate(Type eventType)
    {
        // Get the event delegate's parameters from its 'Invoke' method.
        MethodInfo invokeMethod = eventType.GetMethod("Invoke");
        var parameters = invokeMethod.GetParameters()
            .Select((p) => Expression.Parameter(p.ParameterType, p.Name)).ToList();

        var setMethod = typeof(EventWatcher).GetMethod("SetEventWasRaised");
        var body = Expression.Call(Expression.Constant(this), setMethod, parameters[1]);

        // Create the listener.
        var listener = Expression.Lambda(eventType, body, parameters);
        return listener.Compile();
    }

    public void Dispose()
    {
        bool eventWasFired = _reset.WaitOne(_timeout);
        // Remove the event listener.
        _eventInfo.RemoveEventHandler(_target,_listener);

        if (!eventWasFired)
        {
            string message = String.Format("Test timed-out waiting for event \"{0}\" to be raised.", _eventInfo.Name);
            Assert.Fail(message);
        }
    }
}

public class EventWatcherSetup<T>
{
    private readonly T _target;
    private readonly string _eventName;

    public EventWatcherSetup(T target, string eventName)
    {
        _target = target;
        _eventName = eventName;
    }

    public EventArgs WaitOne(Action action)
    {
        EventArgs e;

        WaitOne(action, out e);
        
        return e;
    }
    public void WaitOne(Action action, out EventArgs e)
    {
        EventWatcher.WaitForEvent(_target,_eventName, out e, action);
    }

}

public class EventAssignmentInterceptor : Castle.DynamicProxy.IInterceptor
{
    public Castle.DynamicProxy.IInvocation LastInvocation;

    #region Implementation of IInterceptor

    public void Intercept(IInvocation invocation)
    {
        LastInvocation = invocation;
    }

    #endregion

    public string GetEventName()
    {
        string methodName = LastInvocation.Method.Name;
        return methodName.Replace("add_", "").Replace("remove_", "");
    }
}

public class EventHelper
{
    public static string GetEventName<T>(T target, Action<T> action) where T : class
    {
        var generator = new Castle.DynamicProxy.ProxyGenerator();
        var interceptor = new EventAssignmentInterceptor();

        T proxy;

        if (typeof(T).IsInterface)
        {
            proxy = generator.CreateInterfaceProxyWithoutTarget<T>(interceptor);
        }
        else
        {
            proxy = generator.CreateClassProxy<T>(interceptor);
        }

        action(proxy);

        return interceptor.GetEventName();
    }
}

submit to reddit

Thursday, July 23, 2009

Using RuntimeTypeHandles to improve Memory of .NET Apps

It's been a few months since I wrote my article Creating a better wrapper using AOP, and I haven't thought much of it, but a recent comment left by Krzysztof has motivated me to re-open the project.  Krzysztof has an interesting optimization that I want to try, and perhaps I will delve into that in another post, but today I want to expand on my intentions of improving both performance and memory management as mentioned in the caveats of my article.  In truth, most of this article is inspired by the work of Jeffrey Richter's C# via CLR and Vance Morrison's blog, and I've been itching to test out the concept of using runtime type handles for a while.

Some Background

Most developers are led to believe that Reflection is a performance hit, but few understand why.  Here's my attempt to explain it in a nutshell.  I’m neither a Microsoft employee nor expert in this matter, I’m simply paraphrasing and I encourage you to pick up Jeff’s book.  If I’m paraphrasing incorrectly, please let me know.

When your code is executed for the first time, the CLR finds all references to Types in the MSIL in order to JIT compile them.  As part of this process, Assemblies are loaded and Type information is read from the meta-data and put into the AppDomain’s heap using internal structures – mainly pointers and method tables.  The first time a Type’s method is called, it is JIT compiled into native instructions and a pointer refers to these compiled bits.  Nice, light and fast.

Reflection however is different.  By calling the Type’s built-in GetType method, we are forcing the runtime to read the meta-data and potentially construct dozens of managed objects (MethodInfo, FieldInfo, PropertyInfo, etc) and collectively these can be somewhat large data structures.  Often as most reflection methods use strings to identify methods, we’re performing a case-insensitive scan of the meta-data.  When we call those methods our parameters have to be pushed onto the stack as an array of objects.  When compared to compiled native code with pointers, you can see why this is slower.  (Roughly 200x slower)

Clearly, the .NET runtime doesn’t need these large data-structures to execute our code.  Instead we can leverage the same pointers that the CLR uses through their managed equivalents: RuntimeTypeHandle, RuntimeMethodHandle and RuntimeFieldHandle.  At their most basic level, they are the pointers (IntPtr objects) to our Types, without all the additional reflection performance overhead.  Since an IntPtr is basically just a numerical value (a ValueType nonetheless), they use considerably less memory.

An interesting point: an example from Vance’s blog demonstrates that typeof(Foo) is considerably slower than typeof(Foo).TypeHandle because the JIT recognizes you need the pointer and not the Type.

Even more surprising, anObj.GetType() == typeof(string) is faster than (anObj is string) for the exact same JIT optimization.  Amazing.

Using Handles

If your application holds onto Type data for extended periods, it may be worth your while to use handles.  Here’s a few examples of using them.

Get the handle for a Type
[Test]
public void CanGetHandleFromType()
{
    RuntimeTypeHandle handle = typeof(string).TypeHandle;
    Type type = Type.GetTypeFromHandle(handle);

    Assert.AreEqual("A String".GetType(), type);
}
Get the handle for a Method
[Test]
public void CanGetHandleFromMethod()
{
    MethodInfo m1 = typeof(Foo).GetMethod("Do");
    RuntimeMethodHandle handle = m1.MethodHandle;

    MethodInfo m2 = MethodBase.GetMethodFromHandle(handle) as MethodInfo;

    Assert.IsNotNull(m2);
    Assert.AreEqual(m1, m2);
}

Applying Handles

Getting back to my AOP example, I can now create a really simple mapping table for my AOP wrapper that’s lean(er) on memory and will cut down on needless calls to the Reflection API, thus speeding things up considerably.  Note that I’m doing all the Reflection work upfront and then storing only pointers.

Update: After some initial profiling, I wasn’t surprised to discover that loading the MethodInfo by the handle is as expensive as the original GetMethod call.  As such, I’ve opted to store my mapping table as Dictionary<RuntimeMethodInfo,MethodInfo> instead of Dictionary<RuntimeMethodInfo,RuntimeMethodInfo>

public class InvocationMapping
{
    public InvocationMapping(object instance)
    {
        _table = new Dictionary<RuntimeMethodHandle, MethodInfo>();
        _instance = instance;
    }

    public static InvocationMapping CreateMapping<TWrapper>(object target)
    {
        InvocationMapping mapping = new InvocationMapping(target);

        Type wrapperType = typeof(TWrapper);
        var wrapperProperties = wrapperType.GetProperties();

        Type targetType = target.GetType();
        foreach (var property in wrapperProperties)
        {
            MappedFieldAttribute attribute =
                                    property.GetCustomAttributes(typeof(MappedFieldAttribute),true)
                                    .OfType<MappedFieldAttribute>()
                                    .SingleOrDefault(mf => mf.TargetType == targetType);

            if (attribute != null)
            {
                PropertyInfo targetProperty = targetType.GetProperty(attribute.FieldName);

                mapping.Add(property, targetProperty);
            }
        }

        return mapping;
    }

    public Dictionary<RuntimeMethodHandle, MethodInfo> MappingTable
    {
        get { return _table; }
    }

    public void Add(PropertyInfo property, PropertyInfo targetProperty)
    {
        if (property.CanRead && targetProperty.CanRead)
        {
            _table.Add(property.GetGetMethod().MethodHandle, targetProperty.GetGetMethod());
        }
        if (property.CanWrite && targetProperty.CanWrite)
        {
            _table.Add(property.GetSetMethod().MethodHandle, targetProperty.GetSetMethod());
        }
    }

    public bool Supports(RuntimeMethodInfo handle)
    {
          return _table.ContainsKey(handle);
    }

    public object Invoke(RuntimeMethodInfo handle, params object[] args)
    {
         MethodInfo method = _table[handle];
         return method.Invoke(_instance, args);
    }

    private Dictionary<RuntimeMethodHandle, MethodInfo> _table;
    private object _instance;
}

My next post will look at combining this approach with a IProxyBuilderHook and/or ISelectorType.

submit to reddit

Tuesday, February 03, 2009

Creating a better Wrapper using AOP

Recently, I was working on a system that had to use several third-party data sources with different object models.  Since changing their code was out of the question, the obvious workaround was to wrap the third-party objects into a common interface in my codebase.  The resulting code was dead simple, but the monotonous repetition of creating property accessors for several dozen fields left me with a sinking feeling that there ought to be a better way.

public class PersonWrapper : IPerson // REPEAT for each 3rd party object...
{
     public PersonWrapper(CompanyA.Person person) 
     {
          _person = person;
     }

     public string EmailAddress
     {
          get { return _person.Email; }
          set { _person.Email = value; }
     }

    // REPEAT for each Property we want to expose...

     CompanyA.Person _person;     
}

The better way snuck into my head when a colleague found an interface in the third-party library that was similar to our needs.  We only needed a small handful of properties of their interface, so he asked, "Can we inherit only half of an interface, somehow?"  This odd question with a seemingly obvious answer (there isn't a CLR keyword to break interface definitions) pushed me to think about the problem differently.  We can't choose what parts of an interface we want to inherit, but we can choose a different interface and inherit from it dynamicallyNot a trivial task but certainly possible as many frameworks have been dynamically generating classes from interfaces for years.  This technique only solved half of the problem, so I discarded the notion.

Later that night, the solution to the other half seemed obvious: decorate the interface with attributes that describe the relationship to the the third-party types, and use an aspect oriented concept known as call-interception to forward calls on the interface to the mapped fields of the third-party type.  If it sounds complicated, it is -- but fortunately, the tools make it easy to do, and I'll try my best to walk you through it.

AOP 101

For starters, it helps to understand that typically AOP is used to augment the behavior of existing classes by creating a special proxy around objects at runtime.  For the existing code that will use those augmented types, the proxy is identical to the original -- the key difference is that each call to your object passes through a special middleman (known as an interceptor) that can perform additional operations before and after each call.  The most commonly used example is logging the parameters that were passed in or the time that the method took to execute.  This diagram attempts to describe what's happening:

DynamicProxy-AOP

However, a dynamic proxy for an interface is different because there's no existing class, so our interceptor is responsible for doing all the work:

DynamicProxy-Interface

Mapping Interfaces to Third Party Types

So rather than creating a concrete wrapper for each vendor's type, we annotate our interface with the mapping information. The advantage is that we can manage our interface definition and mappings in a single place, making it easy to extend to new fields and other vendors without incurring class explosion.

public interface IPerson
{
    [MappedField("First", typeof(CompanyA.Person))]
    [MappedField("FName", typeof(CompanyB.Contact))]
    string FirstName { get; set; }
    
    [MappedField("Last", typeof(CompanyA.Person))]
    [MappedField("LName", typeof(CompanyB.Contact))]
    string LastName { get; set; }
}

[AttributeUsage(AttributeTargets.Property, AllowMultiple=true, Inherited=true)]
public sealed class MappedFieldAttribute : Attribute
{
    public MappedFieldAttribute(string fieldName, Type targetType)
    {
        FieldName = fieldName;
        TargetType = targetType;
    }
    
    public string FieldName
    {
        get;
        private set;
    }
    
    public Type TargetType
    {
        get;
        private set;
    }
}

Intercepting Calls

Now that our interface contains the information to map to properties in our third-party class, we need to dynamically generate a derived class that implements this interface.  I'm using Castle projects' DynamicProxy, though there are several other ways to do this.  We'll configure the derived class (Proxy) with a custom interceptor that will read information about the method being called, and using some Reflection goodness, it will redirect the incoming call to the third-party object. 

DynamicProxy's interceptor interface is simple. The IInvocation object contains all the information about the incoming call:

public interface IInterceptor 
{ 
    void Intercept(IInvocation invocation); 
} 

If we were creating a proxy for a concrete Type, we'd likely want to pass the call from the proxy onto the target object using the invocation.Proceed() method, but because we're using an interface with no target implementation, we'll have to write the implementation within the Interceptor.  We can implement anything we want though the only constraint is that we must set the invocation.ReturnValue if the method has a return value.

The details of the method being called are represented in the invocation.Method.  When the call is for a property, the Method is the actual accessor method, ie get_<PropertyName> and set_<PropertyName>.  This represents a bit of a gotcha because our attribute definition isn't on the accessor Method, it's on the PropertyInfo, so we have a bit of work to get our custom attributes.

The FieldMapperInterceptor implementation looks like this:

public class FieldMapperInterceptor : IInterceptor
{
    public FieldMapperInterceptor(object target)
    {
        _targetObject = target;
        _targetType = target.GetType();
    }
    
    public void Intercept(IInvocation invocation)
    {
        if (!InterceptProperty(invocation))
        {
            throw new NotSupportedException("This method/property is not mapped.");
        }
    }

    protected bool InterceptProperty(IInvocation invocation)
    {
        MethodInfo method = invocation.Method;
        string methodName = method.Name;

        if (!IsPropertyAccessor(method)) return false;

        string propertyName = methodName.Substring(4);
        bool writeOperation = methodName.StartsWith("set_");

        // get attribute from the Property (not the get_/set_ method)
        PropertyInfo property = method.DeclaringType.GetProperty(propertyName);
        MappedFieldAttribute attribute =
				property.GetCustomAttributes(typeof(MappedFieldAttribute), true)
                                               .OfType<MappedFieldAttribute>
                                               .SingleOrDefault(mf => mf.TargetType == _targetType);
        if (attribute == null) return false;

        // locate the property on the target object
        PropertyInfo targetProperty = _targetType.GetProperty(attribute.FieldName);
        if (targetProperty == null)
        {
            throw new NotSupportedException("Field not found on the target Type.");
        }

        if (writeOperation)
        {
            object propertyValue = invocation.Arguments.Last();
            object[] index = invocation.Arguments.Take(invocation.Arguments.Length -1 ).ToArray(); 
            targetProperty.SetValue(_targetObject, propertyValue, index);
        }
        else
        {
            invocation.ReturnValue = targetProperty.GetValue(_targetObject, invocation.Arguments);
        }

        return true;
    }

    public bool IsPropertyAccessor(MethodInfo method)
    {
        string methodName = method.Name;
        return (methodName.StartsWith("get_") | methodName.StartsWith("set_"));
    }
    
    private object _targetObject;
    private Type _targetType;
}

Caveats

The FieldMapperInterceptor will work with any interface using the MappedFieldAttribute and any third-party object, but there are a few caveats:

  • The implementation is only dealing with Properties, though the approach for methods would be similar (and probably easier).  Maybe I'll post a follow up if there's interest.
  • There's definitely room for performance and memory improvements via caching and RuntimeTypeHandles.
  • Does not support generic parameters.
  • While Castle's DynamicProxy2 is very light weight, there is a cost to instantiating objects. Albeit very minor.

Putting it All Together

With this in place, dynamically graphing our custom interface to our third-party types is a snap:

[Test] 
public void Demo() 
{ 
    ProxyGenerator generator = new ProxyGenerator(); 
    CompanyA.Person actualObject = new CompanyA.Person(); 
    FieldMapperInterceptor interceptor = new FieldMapperInterceptor(actualObject); 
    IPerson person = proxyGenerator.CreateInterfaceWithoutTarget<IPerson>(interceptor); 

    person.FirstName = "Bryan"; 

    Assert.AreEqual(actualObject.FName,person.FirstName); 
}

Comments welcome.

submit to reddit