/// <summary>
        /// Executes a GraphQL request and returns the result. The default implementation builds the root node
        /// and passes execution to <see cref="ExecuteNodeTreeAsync(ExecutionContext, ObjectExecutionNode)"/>.
        /// Once complete, the values are collected into an object that is ready to be serialized and returned
        /// within an <see cref="ExecutionResult"/>.
        /// </summary>
        public virtual async Task <ExecutionResult> ExecuteAsync(ExecutionContext context)
        {
            var rootType = ExecutionHelper.GetOperationRootType(context.Document, context.Schema, context.Operation);
            var rootNode = BuildExecutionRootNode(context, rootType);

            await ExecuteNodeTreeAsync(context, rootNode)
            .ConfigureAwait(false);

            // After the entire node tree has been executed, get the values
            object data = rootNode.ToValue();

            return(new ExecutionResult
            {
                Executed = true,
                Data = data,
                Query = context.Document.OriginalQuery,
                Document = context.Document,
                Operation = context.Operation,
                Extensions = context.Extensions
            });
        }
        protected virtual async Task <IObservable <ExecutionResult> > ResolveEventStreamAsync(ExecutionContext context, ExecutionNode node)
        {
            context.CancellationToken.ThrowIfCancellationRequested();

            var arguments = ExecutionHelper.GetArgumentValues(
                node.FieldDefinition.Arguments,
                node.Field.Arguments,
                context.Variables);

            object source = (node.Parent != null)
                ? node.Parent.Result
                : context.RootValue;

            try
            {
                var resolveContext = new ResolveEventStreamContext
                {
                    FieldAst          = node.Field,
                    FieldDefinition   = node.FieldDefinition,
                    ParentType        = node.GetParentType(context.Schema),
                    Arguments         = arguments,
                    Source            = source,
                    Schema            = context.Schema,
                    Document          = context.Document,
                    RootValue         = context.RootValue,
                    UserContext       = context.UserContext,
                    Operation         = context.Operation,
                    Variables         = context.Variables,
                    CancellationToken = context.CancellationToken,
                    Metrics           = context.Metrics,
                    Errors            = context.Errors,
                    Path            = node.Path,
                    RequestServices = context.RequestServices,
                };

                var eventStreamField = node.FieldDefinition as EventStreamFieldType;


                IObservable <object> subscription;

                if (eventStreamField?.Subscriber != null)
                {
                    subscription = eventStreamField.Subscriber.Subscribe(resolveContext);
                }
                else if (eventStreamField?.AsyncSubscriber != null)
                {
                    subscription = await eventStreamField.AsyncSubscriber.SubscribeAsync(resolveContext).ConfigureAwait(false);
                }
                else
                {
                    throw new InvalidOperationException($"Subscriber not set for field '{node.Field.Name}'.");
                }

                return(subscription
                       .Select(value => BuildSubscriptionExecutionNode(node.Parent, node.GraphType, node.Field, node.FieldDefinition, node.IndexInParentNode, value))
                       .SelectMany(async executionNode =>
                {
                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
                            await listener.BeforeExecutionAsync(context)
                            .ConfigureAwait(false);
                        }
                    }

                    // Execute the whole execution tree and return the result
                    await ExecuteNodeTreeAsync(context, executionNode).ConfigureAwait(false);

                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
                            await listener.AfterExecutionAsync(context)
                            .ConfigureAwait(false);
                        }
                    }

                    // Set the execution node's value to null if necessary
                    // Note: assumes that the subscription field is nullable, regardless of how it was defined
                    // See https://github.com/graphql-dotnet/graphql-dotnet/pull/2240#discussion_r570631402
                    //TODO: check if a non-null subscription field is allowed per the spec
                    //TODO: check if errors should be returned along with the data
                    executionNode.PropagateNull();

                    // Return the result
                    return new ExecutionResult
                    {
                        Data = new RootExecutionNode(null, null)
                        {
                            SubFields = new ExecutionNode[]
                            {
                                executionNode,
                            }
                        },
                    }.With(context);
                })
                       .Catch <ExecutionResult, Exception>(exception =>
                                                           Observable.Return(
                                                               new ExecutionResult
                {
                    Errors = new ExecutionErrors
                    {
                        GenerateError(
                            context,
                            $"Could not subscribe to field '{node.Field.Name}' in query '{context.Document.OriginalQuery}'.",
                            node.Field,
                            node.ResponsePath,
                            exception)
                    }
                }.With(context))));
            }
            catch (Exception ex)
            {
                var message = $"Error trying to resolve field '{node.Field.Name}'.";
                var error   = GenerateError(context, message, node.Field, node.ResponsePath, ex);
                context.Errors.Add(error);
                return(null);
            }
        }