Skip to content

Instantly share code, notes, and snippets.

@abdullin
Created October 8, 2015 08:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save abdullin/72f2dd53a3269112b255 to your computer and use it in GitHub Desktop.
Save abdullin/72f2dd53a3269112b255 to your computer and use it in GitHub Desktop.
public sealed class RedirectToDynamicEvent
{
public readonly IDictionary<Type, List<Wire>> Dict = new Dictionary<Type, List<Wire>>();
public sealed class Wire
{
readonly MethodInfo _method;
public Type ParameterType;
readonly object _subject;
readonly bool _includeVersion;
public readonly string SubjectName ;
public Wire(MethodInfo method, Type parameterType, object subject, bool includeVersion)
{
if (subject == null) throw new ArgumentNullException("subject");
SubjectName = subject.GetType().Name;
_method = method;
ParameterType = parameterType;
_subject = subject;
_includeVersion = includeVersion;
}
public void Call( object evt, long version)
{
if (_includeVersion)
{
_method.Invoke(_subject, new[] { evt, version });
}
else
{
_method.Invoke(_subject, new[] { evt });
}
}
}
static readonly MethodInfo InternalPreserveStackTraceMethod =
typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
static Wire ExtractHandlerInfo(object subject, MethodInfo info)
{
var parameters = info.GetParameters();
if (parameters.Length == 0 || parameters.Length > 2)
{
throw new ArgumentException("We allow only 1 or 2 parameters on " + this.GetType().Name);
}
bool includeVersion = false;
var type = parameters[0].ParameterType;
if (parameters.Length == 2)
{
if (parameters[1].ParameterType != typeof(long))
{
throw new ArgumentException("Second parameter can only be a long on " + this.GetType().Name);
}
includeVersion = true;
}
return new Wire(info, type, subject, includeVersion);
}
public void WireToWhen(object o)
{
var infos = o.GetType()
.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => m.Name == "When")
.Where(m =>
{
var length = m.GetParameters().Length;
return length >=1 && length <= 2;
});
foreach (var methodInfo in infos)
{
if (null == methodInfo)
throw new InvalidOperationException();
var handler = ExtractHandlerInfo(o, methodInfo);
List<Wire> list;
if (!Dict.TryGetValue(handler.ParameterType, out list))
{
list = new List<Wire>();
Dict.Add(handler.ParameterType, list);
}
list.Add(handler);
}
}
[DebuggerNonUserCode]
public void InvokeEvent(object evt, long version)
{
var type = evt.GetType();
List<Wire> info;
if (!Dict.TryGetValue(type, out info))
return;
try
{
var sw = new Stopwatch();
foreach (var wire in info)
{
sw.Restart();
try
{
wire.Call(evt, version);
}
catch (ArgumentNullException ex)
{
this.Log().Error(ex, "Error processing event {0}", evt);
}
catch (NullReferenceException ex)
{
this.Log().Error(ex, "Error processing event {0}", evt);
}
sw.Stop();
var seconds = sw.Elapsed.TotalSeconds;
var handler = wire.SubjectName;
var message = type.Name;
if (seconds > 1)
{
// record time spent by the handling method, only if we are relatively slow
Metrics.Timer("domain.events." + message + "." + handler + ".handle-ms", (int)sw.ElapsedMilliseconds);
}
if (seconds > 2)
{
this.Log()
.Warn("[Warn]: {handler}\ttook {seconds} seconds to process event {event}", handler, seconds, evt.ToString());
}
}
}
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