Skip to content

Instantly share code, notes, and snippets.

@danbarua
Created April 27, 2015 14:56
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save danbarua/8a9387957f8a0d884f41 to your computer and use it in GitHub Desktop.
Save danbarua/8a9387957f8a0d884f41 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using ServiceStack.Net30.Collections.Concurrent;
/// <summary>
/// Helper class redirects events to private Apply(event) method
/// </summary>
public static class RedirectToApply
{
/// <summary>The cache.</summary>
private static readonly IDictionary<Type, IDictionary<Type, MethodInfo>> Cache = new ConcurrentDictionary<Type, IDictionary<Type, MethodInfo>>();
/// <summary>The internal preserve stack trace method.</summary>
private static readonly MethodInfo InternalPreserveStackTraceMethod = typeof(Exception).GetMethod(
"InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
/// <summary>Invokes the Apply() method on the instance, throwing an exception if it does not exist.</summary>
/// <param name="instance">The instance.</param>
/// <param name="event">The event.</param>
/// <exception cref="InvalidOperationException">Thrown where the object does not have an Apply() method.</exception>
/// <exception cref="Exception">Thrown whent the Apply() method throws an exception.</exception>
[DebuggerNonUserCode]
public static void InvokeApply(object instance, object @event)
{
MethodInfo info;
var instanceType = instance.GetType();
IDictionary<Type, MethodInfo> lookup;
if (!Cache.TryGetValue(instanceType, out lookup))
{
lookup =
instanceType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => m.Name == "Apply")
.Where(m => m.GetParameters().Length == 1)
.ToDictionary(m => m.GetParameters().First().ParameterType, m => m);
Cache[instanceType] = lookup;
}
var eventType = @event.GetType();
if (!lookup.TryGetValue(eventType, out info))
{
var s = string.Format("Failed to locate {0}.Apply({1})", instanceType.Name, eventType.Name);
throw new InvalidOperationException(s);
}
try
{
info.Invoke(instance, new[] { @event });
}
catch (TargetInvocationException ex)
{
if (null != InternalPreserveStackTraceMethod)
{
InternalPreserveStackTraceMethod.Invoke(ex.InnerException, new object[0]);
}
throw ex.InnerException;
}
}
/// <summary>Optionally invokes the Apply(event) method on the instance where the method exists.</summary>
/// <param name="instance">The instance.</param>
/// <param name="event">The event.</param>
/// <exception cref="Exception">Thrown when the Apply() method throws an exception.</exception> [DebuggerNonUserCode]
public static void InvokeApplyOptional(object instance, object @event)
{
MethodInfo info;
var instanceType = instance.GetType();
IDictionary<Type, MethodInfo> lookup;
if (!Cache.TryGetValue(instanceType, out lookup))
{
lookup =
instanceType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => m.Name == "Apply")
.Where(m => m.GetParameters().Length == 1)
.ToDictionary(m => m.GetParameters().First().ParameterType, m => m);
Cache[instanceType] = lookup;
}
var eventType = @event.GetType();
if (!lookup.TryGetValue(eventType, out info))
{
// event is not needed for aggregate state
return;
}
try
{
info.Invoke(instance, new[] { @event });
}
catch (TargetInvocationException ex)
{
if (null != InternalPreserveStackTraceMethod)
{
InternalPreserveStackTraceMethod.Invoke(ex.InnerException, new object[0]);
}
throw ex.InnerException;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment