/// <summary> /// Trace traces a function which returns an object with the given span. If the object is a task, the span will be finished when the task completes. /// </summary> /// <param name="span">The span to use for tracing. The scope for the span should not be set to finish on close.</param> /// <param name="func">The function to trace</param> /// <param name="onComplete">an optional function to call when the function/task completes. By default exceptions will be added to the span and the span will be finished.</param> /// <returns>The result of the function call</returns> public static object Trace(this Span span, Func <object> func, Action <Exception> onComplete = null) { onComplete = onComplete ?? new Action <Exception>(e => { if (e != null) { span.SetException(e); } span.Finish(); }); try { var result = func(); if (result is Task task) { var typ = task.GetType(); var genericArgs = GetGenericTypeArguments(typeof(Task <>), typ); if (genericArgs.Length == 1) { if (!_traceTaskMethods.TryGetValue(genericArgs[0], out var traceTask)) { traceTask = DynamicMethodBuilder <Func <Span, Task, Action <Exception>, Task> > .CreateMethodCallDelegate(typeof(Extensions), "TraceTask", methodGenericArguments : genericArgs); _traceTaskMethods[genericArgs[0]] = traceTask; } result = traceTask(span, task, onComplete); } else { result = TraceTask(span, task, onComplete); } } else { onComplete(null); } return(result); } catch (Exception e) { onComplete(e); throw; } }
/// <summary> /// Tries to call an instance method with the specified name, a single parameter, and a return value. /// </summary> /// <typeparam name="TArg1">The type of the method's single parameter.</typeparam> /// <typeparam name="TResult">The type of the method's result value.</typeparam> /// <param name="source">The object to call the method on.</param> /// <param name="methodName">The name of the method to call.</param> /// <param name="arg1">The value to pass as the method's single argument.</param> /// <param name="value">The value returned by the method.</param> /// <returns><c>true</c> if the method was found, <c>false</c> otherwise.</returns> public static bool TryCallMethod <TArg1, TResult>(this object source, string methodName, TArg1 arg1, out TResult value) { var type = source.GetType(); var paramType1 = typeof(TArg1); object cachedItem = Cache.GetOrAdd( $"{type.AssemblyQualifiedName}.{methodName}.{paramType1.AssemblyQualifiedName}", key => DynamicMethodBuilder <Func <object, TArg1, TResult> > .CreateMethodCallDelegate( type, methodName, methodParameterTypes: new[] { paramType1 })); if (cachedItem is Func <object, TArg1, TResult> func) { value = func(source, arg1); return(true); } value = default; return(false); }