async ValueTask RunCore(ConsoleAppContext _) { var result = methodInfo.Invoke(instance, invokeArgs); if (result != null) { switch (result) { case int exitCode: invokeResult = exitCode; break; case Task <int> taskWithExitCode: invokeResult = await taskWithExitCode; break; case Task task: await task; break; case ValueTask <int> valueTaskWithExitCode: invokeResult = await valueTaskWithExitCode; break; case ValueTask valueTask: await valueTask; break; } } }
public async Task RunAsync(Type type, MethodInfo method, string?[] args) { logger.LogTrace("ConsoleAppEngine.Run Start"); var ctx = new ConsoleAppContext(args, DateTime.UtcNow, cancellationToken, logger); await RunCore(ctx, type, method, args, 1); // 0 is type selector }
// Try to invoke method. async Task RunCore(Type type, MethodInfo methodInfo, object?instance, string?[] args, int argsOffset) { object?[] invokeArgs; ParameterInfo[] originalParameters = methodInfo.GetParameters(); var isService = provider.GetService <IServiceProviderIsService>(); try { var parameters = originalParameters; if (isService != null) { parameters = parameters.Where(x => !(x.ParameterType == typeof(ConsoleAppContext) || isService.IsService(x.ParameterType))).ToArray(); } if (!TryGetInvokeArguments(parameters, args, argsOffset, out invokeArgs, out var errorMessage)) { await SetFailAsync(errorMessage + " args: " + string.Join(" ", args)); return; } } catch (Exception ex) { await SetFailAsync("Fail to match method parameter on " + type.Name + "." + methodInfo.Name + ". args: " + string.Join(" ", args), ex); return; } var ctx = new ConsoleAppContext(args, DateTime.UtcNow, cancellationToken, logger, methodInfo, provider); // re:create invokeArgs, merge with DI parameter. if (invokeArgs.Length != originalParameters.Length) { var newInvokeArgs = new object?[originalParameters.Length]; var invokeArgsIndex = 0; for (int i = 0; i < originalParameters.Length; i++) { var p = originalParameters[i].ParameterType; if (p == typeof(ConsoleAppContext)) { newInvokeArgs[i] = ctx; } else if (isService !.IsService(p)) { try { newInvokeArgs[i] = provider.GetService(p); } catch (Exception ex) { await SetFailAsync("Fail to get service parameter. ParameterType:" + p.FullName, ex); return; } } else { newInvokeArgs[i] = invokeArgs[invokeArgsIndex++]; } }
public WithFilterInvoker(MethodInfo methodInfo, object instance, object[] invokeArgs, IServiceProvider serviceProvider, ConsoleAppFilter[] globalFilters, ConsoleAppContext context) { this.methodInfo = methodInfo; this.instance = instance; this.invokeArgs = invokeArgs; this.serviceProvider = serviceProvider; this.globalFilters = globalFilters; this.context = context; }
public async ValueTask OnEngineCompleteAsync(ConsoleAppContext context, string?errorMessageIfFailed, Exception?exceptionIfExists) { var exceptions = new AggregateExceptionHolder(); foreach (var item in interceptors) { try { await item.OnEngineCompleteAsync(context, errorMessageIfFailed, exceptionIfExists); } catch (Exception e) { exceptions.Add(e); } } exceptions.ThrowIfExists(); }
public async ValueTask OnMethodBeginAsync(ConsoleAppContext context) { var exceptions = new AggregateExceptionHolder(); foreach (var item in interceptors) { try { await item.OnMethodBeginAsync(context); } catch (Exception e) { exceptions.Add(e); } } exceptions.ThrowIfExists(); }
async Task RunCore(Type type, MethodInfo methodInfo, string?[] args, int argsOffset) { object instance; object[] invokeArgs; try { if (!TryGetInvokeArguments(methodInfo.GetParameters(), args, argsOffset, out invokeArgs, out var errorMessage)) { await SetFailAsync(errorMessage + " args: " + string.Join(" ", args)); return; } } catch (Exception ex) { await SetFailAsync("Fail to match method parameter on " + type.Name + "." + methodInfo.Name + ". args: " + string.Join(" ", args), ex); return; } var ctx = new ConsoleAppContext(args, DateTime.UtcNow, cancellationToken, logger, methodInfo, provider); try { instance = provider.GetService(type); typeof(ConsoleAppBase).GetProperty(nameof(ConsoleAppBase.Context)).SetValue(instance, ctx); } catch (Exception ex) { await SetFailAsync("Fail to create ConsoleAppBase instance. Type:" + type.FullName, ex); return; } try { var invoker = new WithFilterInvoker(methodInfo, instance, invokeArgs, provider, options.GlobalFilters ?? Array.Empty <ConsoleAppFilter>(), ctx); var result = await invoker.InvokeAsync(); if (result != null) { Environment.ExitCode = result.Value; } } catch (Exception ex) { if (ex is OperationCanceledException operationCanceledException && operationCanceledException.CancellationToken == cancellationToken) { // NOTE: Do nothing if the exception has thrown by the CancellationToken of ConsoleAppEngine. // If the user code throws OperationCanceledException, ConsoleAppEngine should not handle that. return; } if (ex is TargetInvocationException tex) { await SetFailAsync("Fail in console app running on " + type.Name + "." + methodInfo.Name, tex.InnerException); return; } else { await SetFailAsync("Fail in console app running on " + type.Name + "." + methodInfo.Name, ex); return; } } logger.LogTrace("ConsoleAppEngine.Run Complete Successfully"); }
public ValueTask OnEngineCompleteAsync(ConsoleAppContext context, string?errorMessageIfFailed, Exception?exceptionIfExists) { return(Empty); }
public ValueTask OnMethodBeginAsync(ConsoleAppContext context) { return(Empty); }
public async Task RunAsync(Type type, string[] args) { logger.LogTrace("ConsoleAppEngine.Run Start"); int argsOffset = 0; MethodInfo?method = null; var ctx = new ConsoleAppContext(args, DateTime.UtcNow, cancellationToken, logger); try { await interceptor.OnMethodBeginAsync(ctx); if (type == typeof(void)) { await SetFailAsync(ctx, "Type or method does not found on this Program. args: " + string.Join(" ", args)); return; } var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); if (methods.Length == 0) { await SetFailAsync(ctx, "Method can not select. T of Run/UseConsoleAppEngine<T> have to be contain single method or command. Type:" + type.FullName); return; } MethodInfo?helpMethod = null; foreach (var item in methods) { var command = item.GetCustomAttribute <CommandAttribute>(); if (command != null) { if (args.Length > 0 && command.EqualsAny(args[0])) { // command's priority is first method = item; argsOffset = 1; goto RUN; } else { if (command.EqualsAny("help")) { helpMethod = item; } } } else { if (method != null) { await SetFailAsync(ctx, "Found more than one public methods(without command). Type:" + type.FullName + " Method:" + method.Name + " and " + item.Name); return; } method = item; // found single public(non-command) method. } } if (method != null) { goto RUN; } // completely not found, invalid command name show help. if (helpMethod != null) { method = helpMethod; goto RUN; } else { Console.Write(new CommandHelpBuilder().BuildHelpMessage(methods, null)); await interceptor.OnMethodEndAsync(ctx, null, null); return; } } catch (Exception ex) { await SetFailAsync(ctx, "Fail to get method. Type:" + type.FullName, ex); return; } RUN: await RunCore(ctx, type, method, args, argsOffset); }
async ValueTask SetFailAsync(ConsoleAppContext context, string message, Exception ex) { Environment.ExitCode = 1; logger.LogError(ex, message); await interceptor.OnMethodEndAsync(context, message, ex); }
async Task RunCore(ConsoleAppContext ctx, Type type, MethodInfo methodInfo, string?[] args, int argsOffset) { object instance; object[] invokeArgs; try { if (!TryGetInvokeArguments(methodInfo.GetParameters(), args, argsOffset, out invokeArgs, out var errorMessage)) { await SetFailAsync(ctx, errorMessage + " args: " + string.Join(" ", args)); return; } } catch (Exception ex) { await SetFailAsync(ctx, "Fail to match method parameter on " + type.Name + "." + methodInfo.Name + ". args: " + string.Join(" ", args), ex); return; } try { instance = provider.GetService(type); typeof(ConsoleAppBase).GetProperty(nameof(ConsoleAppBase.Context)).SetValue(instance, ctx); } catch (Exception ex) { await SetFailAsync(ctx, "Fail to create ConsoleAppBase instance. Type:" + type.FullName, ex); return; } try { var result = methodInfo.Invoke(instance, invokeArgs); switch (result) { case int exitCode: Environment.ExitCode = exitCode; break; case Task <int> taskWithExitCode: Environment.ExitCode = await taskWithExitCode; break; case Task task: await task; break; case ValueTask <int> valueTaskWithExitCode: Environment.ExitCode = await valueTaskWithExitCode; break; case ValueTask valueTask: await valueTask; break; } } catch (Exception ex) { if (ex is OperationCanceledException operationCanceledException && operationCanceledException.CancellationToken == cancellationToken) { // NOTE: Do nothing if the exception has thrown by the CancellationToken of ConsoleAppEngine. // If the user code throws OperationCanceledException, ConsoleAppEngine should not handle that. return; } if (ex is TargetInvocationException tex) { await SetFailAsync(ctx, "Fail in console app running on " + type.Name + "." + methodInfo.Name, tex.InnerException); return; } else { await SetFailAsync(ctx, "Fail in console app running on " + type.Name + "." + methodInfo.Name, ex); return; } } await interceptor.OnMethodEndAsync(ctx, null, null); logger.LogTrace("ConsoleAppEngine.Run Complete Successfully"); }
ValueTask InvokeAsync(ConsoleAppContext context) { return(filter.Invoke(context, next)); }
public abstract ValueTask Invoke(ConsoleAppContext context, Func <ConsoleAppContext, ValueTask> next);
async ValueTask SetFailAsync(ConsoleAppContext context, string message) { Environment.ExitCode = 1; logger.LogError(message); await interceptor.OnEngineCompleteAsync(context, message, null); }
public ValueTask OnMethodEndAsync(ConsoleAppContext context, string?errorMessageIfFailed, Exception?exceptionIfExists) { return(Empty); }