public
Created

RedirectToWhen

  • Download Gist
RedirectToDynamicCommand.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
/// <summary>
/// Creates convention-based routing rules
/// </summary>
public sealed class RedirectToDynamicCommand
{
readonly IDictionary<Type, Wire> _dict = new Dictionary<Type, Wire>();
 
sealed class Wire
{
public MethodInfo Method;
public object Instance;
}
 
static readonly MethodInfo InternalPreserveStackTraceMethod =
typeof (Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
 
 
public void AddWhenMethodsFrom(object o, bool overrideThis = false)
{
var infos = o.GetType()
.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => m.Name == "When")
.Where(m => m.GetParameters().Length == 1);
 
foreach (var methodInfo in infos)
{
var parameterType = methodInfo.GetParameters().First().ParameterType;
var wire = new Wire
{
Instance = o,
Method = methodInfo
};
if (overrideThis)
{
_dict[parameterType] = wire;
}
else
{
_dict.Add(parameterType, wire);
}
}
}
 
[DebuggerNonUserCode]
public void InvokeCommand(IFunctionalShelfCommand command)
{
Wire info;
var type = command.GetType();
if (!_dict.TryGetValue(type, out info))
{
var s = string.Format("Failed to locate When({0})", type.Name);
throw new InvalidOperationException(s);
}
try
{
info.Method.Invoke(info.Instance, new[] {command});
}
catch (TargetInvocationException ex)
{
if (null != InternalPreserveStackTraceMethod)
InternalPreserveStackTraceMethod.Invoke(ex.InnerException, new object[0]);
throw ex.InnerException;
}
}
}
RedirectToDynamicEvent.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
/// <summary>
/// Creates convention-based routing rules
/// </summary>
public sealed class RedirectToDynamicEvent
{
public readonly IDictionary<Type, List<Wire>> Dict = new Dictionary<Type, List<Wire>>();
 
public sealed class Wire
{
public Action<object> Call;
}
 
static readonly MethodInfo InternalPreserveStackTraceMethod =
typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
 
 
 
public void WireToWhen(object o)
{
var infos = o.GetType()
.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => m.Name == "When")
.Where(m => m.GetParameters().Length == 1);
 
foreach (var methodInfo in infos)
{
if (null == methodInfo)
throw new InvalidOperationException();
 
var wires = new HashSet<Type>();
var parameterType = methodInfo.GetParameters().First().ParameterType;
wires.Add(parameterType);
 
 
// if this is an interface, then we wire up to all inheritors in loaded assemblies
// TODO: make this explicit
if (parameterType.IsInterface)
{
var inheritors = typeof (CreateCustomer).Assembly.GetExportedTypes().Where(parameterType.IsAssignableFrom);
foreach (var inheritor in inheritors)
{
wires.Add(inheritor);
}
}
 
foreach (var type in wires)
{
 
List<Wire> list;
if (!Dict.TryGetValue(type, out list))
{
list = new List<Wire>();
Dict.Add(type, list);
}
 
 
var wire = BuildWire(o, type, methodInfo);
list.Add(wire);
}
}
}
 
static Wire BuildWire(object o, Type type, MethodInfo methodInfo)
{
var info = methodInfo;
var dm = new DynamicMethod("MethodWrapper", null, new[] {typeof (object), typeof (object)});
var il = dm.GetILGenerator();
 
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, o.GetType());
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Castclass, type);
il.EmitCall(OpCodes.Call, info, null);
il.Emit(OpCodes.Ret);
 
var call = (Action<object, object>) dm.CreateDelegate(typeof (Action<object, object>));
var wire = new Wire
{
Call = o1 => call(o, o1)
};
return wire;
}
 
public void WireTo<TMessage>(Action<TMessage> msg)
{
var type = typeof (TMessage);
List<Wire> list;
if (!Dict.TryGetValue(type, out list))
{
list = new List<Wire>();
Dict.Add(type, list);
}
list.Add(new Wire
{
Call = o => msg((TMessage)o)
});
}
 
 
[DebuggerNonUserCode]
public void InvokeEvent(object @event)
{
var type = @event.GetType();
List<Wire> info;
if (!Dict.TryGetValue(type, out info))
{
return;
}
try
{
foreach (var wire in info)
{
wire.Call(@event);
}
}
catch (TargetInvocationException ex)
{
if (null != InternalPreserveStackTraceMethod)
InternalPreserveStackTraceMethod.Invoke(ex.InnerException, new object[0]);
throw ex.InnerException;
}
}
}
RedirectToWhen.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/// <summary>
/// Class that statically caches information about methods named
/// <em>When</em> based on the parameter type. Then, given an instance
/// and an argument (command of event) we can pass this argument
/// directly to the corresponding method.
/// </summary>
/// <remarks>For updates visit: https://gist.github.com/1309255 and also
/// http://bliki.abdullin.com/event-sourcing/ on the usage patterns
/// </remarks>
public static class RedirectToWhen
{
private static readonly MethodInfo InternalPreserveStackTraceMethod = typeof(Exception)
.GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
 
static class Cache<T>
{
public static readonly IDictionary<Type, MethodInfo> Dict = typeof(T)
.GetMethods(BindingFlags.Public |BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => m.Name == "When")
.Where(m => m.GetParameters().Length == 1)
.ToDictionary(m => m.GetParameters().First().ParameterType, m => m);
}
[DebuggerNonUserCode]
public static void InvokeEventOptional<T>(T instance, IShelfEvent command)
{
MethodInfo info;
var type = command.GetType();
if (!Cache<T>.Dict.TryGetValue(type, out info))
{
// we don't care if state does not consume events
// they are persisted anyway
return;
}
try
{
info.Invoke(instance, new[] {command});
}
catch (TargetInvocationException ex)
{
if (null != InternalPreserveStackTraceMethod)
InternalPreserveStackTraceMethod.Invoke(ex.InnerException, new object[0]);
throw ex.InnerException;
}
}
[DebuggerNonUserCode]
public static void InvokeCommand<T>(T instance, IShelfCommand command)
{
MethodInfo info;
var type = command.GetType();
if (!Cache<T>.Dict.TryGetValue(type, out info))
{
var s = string.Format("Failed to locate {0}.When({1})", typeof (T).Name, type.Name);
throw new InvalidOperationException(s);
}
try
{
info.Invoke(instance, new[] {command});
}
catch (TargetInvocationException ex)
{
if (null != InternalPreserveStackTraceMethod)
InternalPreserveStackTraceMethod.Invoke(ex.InnerException, new object[0]);
throw ex.InnerException;
}
}
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.