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
        }
Esempio n. 3
0
        // 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);
 }