Created
October 24, 2011 15:03
-
-
Save abdullin/1309255 to your computer and use it in GitHub Desktop.
RedirectToWhen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <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; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment