diff --git a/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj b/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj
index d1823310945..e2a9e78a5aa 100644
--- a/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj
+++ b/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj
@@ -159,6 +159,12 @@
ResultBuilder.cs
+
+ ResolverTask.cs
+
+
+ ResolverTask.cs
+
diff --git a/src/HotChocolate/Core/src/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Execution/Processing/Selection.cs
index c1764a41a5c..2618d12f4e5 100644
--- a/src/HotChocolate/Core/src/Execution/Processing/Selection.cs
+++ b/src/HotChocolate/Core/src/Execution/Processing/Selection.cs
@@ -55,6 +55,11 @@ public class Selection : ISelection
{
_flags |= Flags.List;
}
+
+ if (Field.HasStreamResult)
+ {
+ _flags |= Flags.StreamResult;
+ }
}
protected Selection(Selection selection)
@@ -123,10 +128,17 @@ protected Selection(Selection selection)
///
public IArgumentMap Arguments { get; }
- public bool IsStream(long includeFlags)
+ ///
+ public bool HasStreamResult => (_flags & Flags.StreamResult) == Flags.StreamResult;
+
+ ///
+ public bool HasStreamDirective(long includeFlags)
=> (_flags & Flags.Stream) == Flags.Stream &&
(_streamIfCondition is 0 || (includeFlags & _streamIfCondition) != _streamIfCondition);
+ ///
+ /// Specifies if the current selection is immutable.
+ ///
public bool IsReadOnly => (_flags & Flags.Sealed) == Flags.Sealed;
///
@@ -324,6 +336,7 @@ private enum Flags
Internal = 1,
Sealed = 2,
List = 4,
- Stream = 8
+ Stream = 8,
+ StreamResult = 16
}
}
diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.Execute.cs b/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.Execute.cs
index ba0b67db1c9..586ddba4cd4 100644
--- a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.Execute.cs
+++ b/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.Execute.cs
@@ -130,12 +130,18 @@ private async ValueTask ExecuteResolverPipelineAsync(CancellationToken cancellat
return;
}
- if (_selection.IsStream(_operationContext.IncludeFlags))
+ if (_selection.HasStreamDirective(_operationContext.IncludeFlags))
{
_resolverContext.Result = await CreateStreamResultAsync(result).ConfigureAwait(false);
return;
}
+ if (_selection.HasStreamResult)
+ {
+ _resolverContext.Result = await CreateListFromStreamAsync(result).ConfigureAwait(false);
+ return;
+ }
+
switch (_resolverContext.Result)
{
case IExecutable executable:
diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.cs b/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.cs
index 561956524de..cbe6e5a605b 100644
--- a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.cs
+++ b/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.cs
@@ -3,7 +3,6 @@
using System.Threading;
using System.Threading.Tasks;
using HotChocolate.Execution.Instrumentation;
-using HotChocolate.Types;
using Microsoft.Extensions.ObjectPool;
namespace HotChocolate.Execution.Processing.Tasks;
diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs
index 27be66070ba..121cc684f5c 100644
--- a/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs
+++ b/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs
@@ -17,5 +17,17 @@ public interface IOptionalSelection
///
bool IsConditional { get; }
+ ///
+ /// Defines if this selection will be included into the request execution.
+ ///
+ ///
+ /// The execution include flags.
+ ///
+ ///
+ /// Allow internal selections to be included.
+ ///
+ ///
+ /// True, if this selection shall be included into the request execution.
+ ///
bool IsIncluded(long includeFlags, bool allowInternals = false);
}
diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs
index ae4fa3db6a0..290baa2fe48 100644
--- a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs
+++ b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs
@@ -1,6 +1,6 @@
#nullable enable
-using System.Diagnostics.SymbolStore;
+using System.Collections.Generic;
using HotChocolate.Language;
using HotChocolate.Resolvers;
using HotChocolate.Types;
@@ -38,7 +38,7 @@ public interface ISelection : IOptionalSelection
TypeKind TypeKind { get; }
///
- /// Specifies if the return type fo this selection is a list.
+ /// Specifies if the return type of this selection is a list type.
///
bool IsList { get; }
@@ -82,5 +82,21 @@ public interface ISelection : IOptionalSelection
///
IArgumentMap Arguments { get; }
- bool IsStream(long includeFlags);
+ ///
+ /// Defines that the resolver pipeline returns an
+ /// as its result.
+ ///
+ bool HasStreamResult { get; }
+
+ ///
+ /// Defines if this selection is annotated with the stream directive.
+ ///
+ ///
+ /// The execution include flags that determine if the stream directive is applied for the
+ /// current execution run.
+ ///
+ ///
+ /// Returns if this selection is annotated with the stream directive.
+ ///
+ bool HasStreamDirective(long includeFlags);
}
diff --git a/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs
index c268b1f766c..b3caf752d78 100644
--- a/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs
+++ b/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs
@@ -1418,7 +1418,7 @@ internal class TypeResources {
return ResourceManager.GetString("ConnectionType_TotalCount_Description", resourceCulture);
}
}
-
+
internal static string CollectionSegmentType_PageInfo_Description {
get {
return ResourceManager.GetString("CollectionSegmentType_PageInfo_Description", resourceCulture);
@@ -1526,5 +1526,11 @@ internal class TypeResources {
return ResourceManager.GetString("InterfaceTypeDescriptor_MustBePropertyOrMethod", resourceCulture);
}
}
+
+ internal static string ThrowHelper_FieldBase_Sealed {
+ get {
+ return ResourceManager.GetString("ThrowHelper_FieldBase_Sealed", resourceCulture);
+ }
+ }
}
}
diff --git a/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx b/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx
index 3ade3deb5d3..ba25655d28a 100644
--- a/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx
+++ b/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx
@@ -873,4 +873,7 @@ Type: `{0}`
A field of an interface can only be inferred from a property or a method.
+
+ The field is already sealed and cannot be mutated.
+
diff --git a/src/HotChocolate/Core/src/Types/Types/Attributes/StreamResultAttribute.cs b/src/HotChocolate/Core/src/Types/Types/Attributes/StreamResultAttribute.cs
new file mode 100644
index 00000000000..611719132c9
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types/Types/Attributes/StreamResultAttribute.cs
@@ -0,0 +1,22 @@
+#nullable enable
+using System;
+using System.Reflection;
+using HotChocolate.Types.Descriptors;
+
+namespace HotChocolate.Types;
+
+///
+/// Marks a resolver as returning a stream result
+/// which will allow the execution engine to compile a result handler for the resolver.
+///
+[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
+public sealed class StreamResultAttribute : ObjectFieldDescriptorAttribute
+{
+ public override void OnConfigure(
+ IDescriptorContext context,
+ IObjectFieldDescriptor descriptor,
+ MemberInfo member)
+ {
+ descriptor.StreamResult();
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types/Types/Contracts/IObjectField.cs b/src/HotChocolate/Core/src/Types/Types/Contracts/IObjectField.cs
index 0ed94150fe2..b0d80e8a5e2 100644
--- a/src/HotChocolate/Core/src/Types/Types/Contracts/IObjectField.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Contracts/IObjectField.cs
@@ -21,6 +21,12 @@ public interface IObjectField : IOutputField
///
bool IsParallelExecutable { get; }
+ ///
+ /// Defines that the resolver pipeline returns an
+ /// as its result.
+ ///
+ bool HasStreamResult { get; }
+
///
/// Gets the field resolver middleware.
///
diff --git a/src/HotChocolate/Core/src/Types/Types/Contracts/IOutputField.cs b/src/HotChocolate/Core/src/Types/Types/Contracts/IOutputField.cs
index d84689f31e5..c9f390b9587 100644
--- a/src/HotChocolate/Core/src/Types/Types/Contracts/IOutputField.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Contracts/IOutputField.cs
@@ -1,5 +1,7 @@
#nullable enable
+using System.Collections.Generic;
+
namespace HotChocolate.Types;
///
diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/IObjectFieldDescriptor.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/IObjectFieldDescriptor.cs
index 0e3158cf24b..919d45faf60 100644
--- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/IObjectFieldDescriptor.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/IObjectFieldDescriptor.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using HotChocolate.Language;
@@ -91,6 +92,12 @@ IObjectFieldDescriptor Type(TOutputType outputType)
///
IObjectFieldDescriptor Type(Type type);
+ ///
+ /// Defines weather the resolver pipeline will return
+ /// as its result.
+ ///
+ IObjectFieldDescriptor StreamResult(bool hasStreamResult = true);
+
///
/// Defines a field argument.
///
diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/FieldDefinitionBase.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/FieldDefinitionBase.cs
index fc03bb01d70..d9b8c69491f 100644
--- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/FieldDefinitionBase.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/FieldDefinitionBase.cs
@@ -14,16 +14,38 @@ public abstract class FieldDefinitionBase
, IHasIgnore
{
private List? _directives;
+ private string? _deprecationReason;
+
+ ///
+ /// Gets the internal field flags from this field.
+ ///
+ internal FieldFlags Flags { get; set; } = FieldFlags.None;
///
/// Describes why this syntax node is deprecated.
///
- public string? DeprecationReason { get; set; }
+ public string? DeprecationReason
+ {
+ get => _deprecationReason;
+ set
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ Flags &= ~FieldFlags.Deprecated;
+ }
+ else
+ {
+ Flags |= FieldFlags.Deprecated;
+ }
+
+ _deprecationReason = value;
+ }
+ }
///
/// If true, the field is deprecated
///
- public bool IsDeprecated => !string.IsNullOrEmpty(DeprecationReason);
+ public bool IsDeprecated => (Flags & FieldFlags.Deprecated) == FieldFlags.Deprecated;
///
/// Gets the field type.
@@ -34,18 +56,32 @@ public abstract class FieldDefinitionBase
/// Defines if this field is ignored and will
/// not be included into the schema.
///
- public bool Ignore { get; set; }
+ public bool Ignore
+ {
+ get => (Flags & FieldFlags.Ignored) == FieldFlags.Ignored;
+ set
+ {
+ if (value)
+ {
+ Flags |= FieldFlags.Ignored;
+ }
+ else
+ {
+ Flags &= ~FieldFlags.Ignored;
+ }
+ }
+ }
///
/// Gets the list of directives that are annotated to this field.
///
- public IList Directives =>
- _directives ??= new List();
+ public IList Directives
+ => _directives ??= new List();
///
/// Specifies if this field has any directives.
///
- public bool HasDirectives => _directives is { Count: > 0 };
+ public bool HasDirectives => _directives?.Count > 0;
///
/// Gets the list of directives that are annotated to this field.
diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ObjectFieldDefinition.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ObjectFieldDefinition.cs
index 39ff48eb930..53558b2d4b5 100644
--- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ObjectFieldDefinition.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ObjectFieldDefinition.cs
@@ -25,7 +25,10 @@ public class ObjectFieldDefinition : OutputFieldDefinitionBase
///
/// Initializes a new instance of .
///
- public ObjectFieldDefinition() { }
+ public ObjectFieldDefinition()
+ {
+ IsParallelExecutable = true;
+ }
///
/// Initializes a new instance of .
@@ -42,6 +45,7 @@ public class ObjectFieldDefinition : OutputFieldDefinitionBase
Type = type;
Resolver = resolver;
PureResolver = pureResolver;
+ IsParallelExecutable = true;
}
///
@@ -142,12 +146,60 @@ public IList