public async Task <ExecutionResult> ExecuteAsync(ExecutionOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (options.Schema == null) { throw new InvalidOperationException("Cannot execute request if no schema is specified"); } if (options.Query == null) { throw new InvalidOperationException("Cannot execute request if no query is specified"); } if (options.FieldMiddleware == null) { throw new InvalidOperationException("Cannot execute request if no middleware builder specified"); } DateTime start = DateTime.UtcNow; var metrics = new Metrics(options.EnableMetrics).Start(options.OperationName); options.Schema.NameConverter = options.NameConverter; options.Schema.Filter = options.SchemaFilter; ExecutionResult result = null; ExecutionContext context = null; try { if (!options.Schema.Initialized) { using (metrics.Subject("schema", "Initializing schema")) { options.FieldMiddleware.ApplyTo(options.Schema); options.Schema.Initialize(); } } var document = options.Document; using (metrics.Subject("document", "Building document")) { if (document == null) { document = _documentBuilder.Build(options.Query); } } if (document.Operations.Count == 0) { throw new NoOperationError(); } var operation = GetOperation(options.OperationName, document); metrics.SetOperationName(operation?.Name); if (operation == null) { throw new InvalidOperationException($"Query does not contain operation '{options.OperationName}'."); } IValidationResult validationResult; using (metrics.Subject("document", "Validating document")) { validationResult = await _documentValidator.ValidateAsync( options.Query, options.Schema, document, options.ValidationRules, options.UserContext, options.Inputs); } if (options.ComplexityConfiguration != null && validationResult.IsValid) { using (metrics.Subject("document", "Analyzing complexity")) _complexityAnalyzer.Validate(document, options.ComplexityConfiguration); } context = BuildExecutionContext( options.Schema, options.Root, document, operation, options.Inputs, options.UserContext, options.CancellationToken, metrics, options.Listeners, options.ThrowOnUnhandledException, options.UnhandledExceptionDelegate, options.MaxParallelExecutionCount, options.RequestServices); foreach (var listener in options.Listeners) { await listener.AfterValidationAsync(context, validationResult) .ConfigureAwait(false); } if (!validationResult.IsValid) { return(new ExecutionResult { Errors = validationResult.Errors, Perf = metrics.Finish() }); } if (context.Errors.Count > 0) { return(new ExecutionResult { Errors = context.Errors, Perf = metrics.Finish() }); } using (metrics.Subject("execution", "Executing operation")) { if (context.Listeners != null) { foreach (var listener in context.Listeners) { await listener.BeforeExecutionAsync(context) .ConfigureAwait(false); } } IExecutionStrategy executionStrategy = SelectExecutionStrategy(context); if (executionStrategy == null) { throw new InvalidOperationException("Invalid ExecutionStrategy!"); } var task = executionStrategy.ExecuteAsync(context) .ConfigureAwait(false); if (context.Listeners != null) { foreach (var listener in context.Listeners) { await listener.BeforeExecutionAwaitedAsync(context) .ConfigureAwait(false); } } result = await task; if (context.Listeners != null) { foreach (var listener in context.Listeners) { await listener.AfterExecutionAsync(context) .ConfigureAwait(false); } } } if (context.Errors.Count > 0) { result.Errors = context.Errors; } } catch (OperationCanceledException) when(options.CancellationToken.IsCancellationRequested) { throw; } catch (ExecutionError ex) { result = new ExecutionResult { Errors = new ExecutionErrors { ex } }; } catch (Exception ex) { if (options.ThrowOnUnhandledException) { throw; } UnhandledExceptionContext exceptionContext = null; if (options.UnhandledExceptionDelegate != null) { exceptionContext = new UnhandledExceptionContext(context, null, ex); options.UnhandledExceptionDelegate(exceptionContext); ex = exceptionContext.Exception; } result = new ExecutionResult { Errors = new ExecutionErrors { ex is ExecutionError executionError ? executionError : new UnhandledError(exceptionContext?.ErrorMessage ?? "Error executing document.", ex) } }; } finally { result ??= new ExecutionResult(); result.Perf = metrics.Finish(); } try { _traceLogger.LogTrace(start, options.OperationName, options.Query, result); } catch { // We don't want log tracing to break it - but we should notify the user of this } return(result); }