See issues and pull requests done in v5.
New Features 1. DoNotMapClrType attribute can now be placed on the graph type or the CLR typeWhen using the .AddClrTypeMappings()
builder extension method, GraphQL.NET scans the specified assembly for graph types that inherit from ObjectGraphType<T>
and adds a mapping for the CLR type represented by T
with the graph type it matched upon. It skips adding a mapping for any graph type marked with the [DoNotMapClrType]
attribute. In v5, it will also skip adding the mapping if the CLR type is marked with the [DoNotMapClrType]
attribute.
Extensions
deserialized from GraphQL requests can now be set on the ExecutionOptions.Extensions
property and passed through to field resolvers via IResolveFieldContext.InputExtensions
. Note that standard .NET dictionaries (such as Dictionary<TKey, TValue>
) are thread-safe for read-only operations. Also you can access these extensions from validation rules via ValidationContext.Extensions
.
GraphQL.NET v5 uses GraphQL-Parser v8. This release brought numerous changes in the parser object model, which began to better fit the latest version of the published official GraphQL specification. GraphQL-Parser v8 has a lot of backward incompatible changes, but you are unlikely to come across them if you do not use advanced features.
4.IGraphQLSerializer
interface with JSON deserialization support
IGraphQLSerializer.ReadAsync
is implemented by the GraphQL.SystemTextJson
and GraphQL.NewtonsoftJson
libraries. It supports deserialization of any type, with special support for the GraphQLRequest
class. It also supports deserializing to a IList<GraphQLRequest>
, which will deserialize multiple requests or a single request (with or without the JSON array wrapper) into a list.
When calling the AddSystemTextJson
or AddNewtonsoftJson
extension method to the IGraphQLBuilder
interface, the method will register the IGraphQLSerializer
and IGraphQLTextSerializer
interfaces with the appropriate serialization engine. These interfaces handle both serialization and deserialization of objects.
This makes it so that you can write JSON-based transport code independent of the JSON serialization engine used by your application, simplifying the most common use case, while still being configurable through your DI framework.
You can also use IGraphQLTextSerializer.ReadNode
to deserialize a framework-dependent JSON element node, stored within an object
, into a specific type. The specific serialization engine you are using may have additional Read
members as would be expected for that library.
Be aware that System.Text.Json
defaults to case-sensitive deserialization, while Newtonsoft.Json
defaults to case-insensitive deserialization. However, the supported data models (such as GraphQLRequest
and OperationMessage
) will always deserialize with case-sensitive camelCase deserialization. You can write your own data classes which will behave in the default manner of the serializer's configuration. You can configure the serializer to use camelCase for all properties by default. You can also tag properties with serializer-specific attributes to change deserialization behavior, such as adding a JsonPropertyName
attribute to a data member to override its serialized property name.
Specific support is provided for serializing and deserializing to the following data models:
Class NotesExecutionResult
Only serialization is supported ExecutionError
Only serialization is supported GraphQLRequest
IList<GraphQLRequest>
Other common collection variations, such as IEnumerable<>
or List<>
, are also supported OperationMessage
Payload
is an object
and can be deserialized to GraphQLRequest
via ReadNode
ApolloTrace
Inputs
Note that when deserializing a IList<GraphQLRequest>
, and when the JSON data is a single request rather than a list of requests, the request will be deserialized into a list or array of a single item. For example, {"query":"{ hero }"}
deserializes into new[] { new GraphQLRequest { Query = "{ hero }" }}
.
IGraphQLTextSerializer
interface to support serialization to/from strings.
IGraphQLTextSerializer.Serialize
and IGraphQLTextSerializer.Deserialize
support serializing objects to and from string
values. For the GraphQL.SystemTextJson
and GraphQL.NewtonsoftJson
libraries, these serialize and deserialize to JSON strings.
AutoRegisteringObjectGraphType
and AutoRegisteringInputObjectGraphType
enhancements
These two classes now provide a range of customizable behavior for data models without the need for creating individual graph types for each data model. New for v5, fields' names and graph types can be customized by applying attributes to the respective property, such as is shown in the below example:
class Person
{
[Name("Id")]
[OutputType(typeof(IdGraphType))]
public int PersonId { get; set; }
public string Name { get; set; }
[Authorize("Administrators")]
public int Age { get; set; }
[Description("Employee's job position")]
public string? Title { get; set; }
}
Nullable reference type attribute interpretation
When the CLR type has nullable reference type annotations, these annotations are read and interpreted by GraphQL.NET when constructing the graph type. For instance, this is how the following CLR types are mapped to graph types after schema initialization:
CLR type Graph typestring?
StringGraphType
string
NonNullGraphType<StringGraphType>
List<int>
NonNullGraphType<ListGraphType<NonNullGraphType<IntGraphType>>>
CLR type mappings registered in the schema are supported as well.
In addition to the above, if the [Id]
attribute is marked on the property, it will override the interpreted graph type such as in the following examples:
[Id]
Graph type string?
IdGraphType
string
NonNullGraphType<IdGraphType>
List<int>
NonNullGraphType<ListGraphType<NonNullGraphType<IdGraphType>>>
Custom attributes can also be added to perform the following behavior changes:
AutoRegisteringObjectGraphType<T>
instances
Methods will be detected and added to fields on the graph. Asynchronous methods and data loader methods are supported such as shown in the below example:
CLR type Graph typeTask<string>
NonNullGraphType<StringGraphType>
IDataLoaderResult<Person?>
PersonGraphType
The above example assumes that the Person
CLR type was mapped to PersonGraphType
in the schema CLR type mappings.
Arguments are also added as query arguments to the field, and recognize default values and other attributes set on the parameter. Certain argument types are recognized and treated special; special fields do not add a query argument to the field:
Argument type Value passed to the methodIResolveFieldContext
The field resolver's context; useful in advanced scenarios CancellationToken
The cancellation token from the resolve context Any, tagged with [FromServices]
Pulls the service of the argument type from the RequestServices
property of the resolve context
Also note:
Async
have the "Async" suffix removed from the default field name.[Scoped]
(when using GraphQL.MicrosoftDI
) create a service scope for the field resolver's execution.This allows for code such as the following:
[Name("Person")]
public class Human
{
[Id]
public int Id { get; set; }
public string Name { get; set; }
[Name("Orders")]
[Scoped]
public async Task<IEnumerable<Order>> GetOrdersAsync(
[FromServices] MyDbContext db,
CancellationToken token,
[Name("Sort")] SortOrder sortOrder = SortOrder.Date)
{
var query = db.Orders.Where(x => x.HumanId == Id);
if (sortOrder == SortOrder.Date)
query = query.OrderByDesc(x => x.OrderDate);
return query.ToListAsync(token);
}
}
public enum SortOrder
{
Date
}
The above code would generate a GraphQL schema like this:
type Person {
id: ID!
name: String!
orders(sort: SortOrder! = DATE): [Order!]!
}
CLR field support
CLR fields are not automatically added to graph types, but can be added by overriding the GetRegisteredMembers
method of a AutoRegisteringObjectGraphType<T>
or AutoRegisteringInputObjectGraphType<T>
instance.
The classes can be overridden, providing the ability to customize behavior of automatically generated graph types. For instance, to exclude properties of a certain type, you could write this:
private class CustomAutoObjectType<T> : AutoRegisteringObjectGraphType<T>
{
protected override IEnumerable<FieldType> ProvideFields()
{
var props = GetRegisteredProperties();
foreach (var prop in props)
{
if (prop.PropertyType != typeof(MyType))
yield return CreateField(prop);
}
}
}
Similarly, by overriding CreateField
you can change the default name, description, graph type, or other information applied to each generated field.
Most of these changes can be performed declaratively by attributes, but by creating a derived class you can change default behavior imperatively without needing to add attributes to all of your data models.
These protected
methods can be overridden to provide the following customizations to automatically-generated graph types:
FieldType
from a MemberInfo
Applying custom behavior to field generation GetTypeInformation Parses a CLR type and NRT annotations to return a graph type Use a specific graph type for a certain CLR type GetArgumentInformation Parses a method argument to return a query argument or expression Return an expression for specific types, such as a user context
Note that if you override GetRegisteredMembers
to include private properties or fields for an input graph, you may also need to override ParseDictionary
as well.
If you utilize dependency injection within your schema, you can register your custom graph type to be used instead of the built-in type as follows:
services.AddSingleton(typeof(AutoRegisteringObjectGraphType<>), typeof(CustomAutoObjectType<>));
Then any graph type defined as AutoRegisteringObjectGraphType<...>
will use your custom type instead.
Any attribute that derives from GraphQLAttribute
, such as AuthorizeAttribute
, can be set on a CLR class or one if its properties, fields, methods or method arguments and is configured for the graph, field type or query argument. New attributes have been updated or added for convenience as follows:
[Name]
Specifies a GraphQL name for a CLR class, member or method parameter [InputName]
Specifies a GraphQL name for an input CLR class, member or method parameter [OutputName]
Specifies a GraphQL name for an output CLR class or member [InputType]
Specifies a graph type for a field on an input model, or for a query argument [OutputType]
Specifies a graph type for a field on an output model [Ignore]
Indicates that a CLR member should not be mapped to a field [Metadata]
Specifies custom metadata to be added to the graph type, field or query argument [Scoped]
For methods, specifies to create a DI service scope during resolver execution [FromServices]
For method parameters, specifies that the argument value should be pulled from DI [FromSource]
For method parameters, specifies that the argument value should be the context 'Source' [FromUserContext]
For method parameters, specifies that the argument value should be the user context [Authorize]
Specifies an authorization policy for the graph type for field [GraphQLMetadata]
Specifies name, description, deprecation reason, or other properties for the graph type or field
Note: [Scoped]
is provided through the GraphQL.MicrosoftDI NuGet package.
Custom attributes can be easily added to control any other initialization of graphs, fields or query arguments.
7. More strict behavior of FloatGraphType for special valuesThis is a spec-compliance issue (bug fix), that fixes parsing of Nan and -/+ Infinity values. The spec says that:
8. Support for cancellation at validation stageNon-finite floating-point internal values (NaN and Infinity) cannot be coerced to Float and must raise a field error.
With new visitors design from GraphQL-Parser v8 it is possible now to cancel GraphQL request at validation stage before actual execution. DocumentExecuter
uses the same cancellation token specified into ExecutionOptions
to pass into IDocumentValidator.ValidateAsync
.
InputObjectGraphType
supports ToAST
/IsValidDefault
ToAST
is supported for InputObjectGraphType
and enables printing a code-first schema that uses InputObjectGraphType
(ToAST
threw NotImplementedException
before), i.e. schemas with default input objects can be printed out of the box now. InputObjectGraphType.IsValidDefault
now checks all fields on the provided input object value. To revert IsValidDefault
to v4 behavior use that snippet:
public override bool IsValidDefault(object value) => value is TSourceType;
10. EnumerationGraphType<T>
enhancements
The following new attributes are detected on auto-generated enum graph types:
Attribute Description[Name]
Specifies the name of the enum type or value [Ignore]
Does not add the enum value to the enum type [Metadata]
Adds the specified metadata to enum type or value
As before, you can still use the [Description]
or [Obsolete]
attributes to add descriptions or deprecation reasons to enum graph types or enum values.
You can also derive from GraphQLAttribute
to create your own attributes to modify enum graph types or enum values as they are being built by EnumerationGraphType<T>
.
Now you may get directives along with their arguments that have been provided in the GraphQL query request. New APIs are similar to ones used for field arguments:
Field<StringGraphType>("myField", resolve: context =>
{
var dir = ctx.GetDirective("myDirective");
var arg = dir.GetArgument<string>("arg");
...
});
12. The ExecutionStrategy
selected for an operation can be configured through IGraphQLBuilder
Previously, in order to change the execution strategy for a specific operation -- for instance, using a serial execution strategy for 'query' operation types -- required creating a custom document executer and overriding the SelectExecutionStrategy
method.
Now, for DI configurations, you can call the .AddExecutionStrategy<T>(OperationType)
method to provide this configuration without overriding the method. See below for an example.
public class SerialDocumentExecuter : DocumentExecuter
{
public SerialDocumentExecuter(IDocumentBuilder documentBuilder, IDocumentValidator documentValidator, IComplexityAnalyzer complexityAnalyzer, IDocumentCache documentCache, IEnumerable<IConfigureExecutionOptions> configurations)
: base(documentBuilder, documentValidator, complexityAnalyzer, documentCache, configurations)
{
}
protected override IExecutionStrategy SelectExecutionStrategy(ExecutionContext context)
{
return context.Operation.Operation switch
{
OperationType.Query => SerialExecutionStrategy.Instance,
_ => base.SelectExecutionStrategy(context)
};
}
}
services.AddGraphQL()
.AddSystemTextJson()
.AddSchema<StarWarsSchema>()
.AddDocumentExecuter<SerialDocumentExecuter>();
services.AddGraphQL(builder => builder
.AddSystemTextJson()
.AddSchema<StarWarsSchema>()
.AddExecutionStrategy<SerialExecutionStrategy>(OperationType.Query));
You can also register your own implementation of IExecutionStrategySelector
which can inspect the ExecutionContext
to make additional decisions before selecting an execution strategy.
public class MyExecutionStrategySelector : IExecutionStrategySelector
{
public virtual IExecutionStrategy Select(ExecutionContext context)
{
return context.Operation.Operation switch
{
OperationType.Query => ParallelExecutionStrategy.Instance,
OperationType.Mutation => SerialExecutionStrategy.Instance,
OperationType.Subscription => SubscriptionExecutionStrategy.Instance,
_ => throw new InvalidOperationException()
};
}
}
servcies.AddGraphQL(builder => builder
.AddExecutionStrategySelector<MyExecutionStrategySelector>());
The DocumentExecuter.SelectExecutionStrategy
method is still available to be overridden for backwards compatibility but may be removed in the next major version.
FieldDelegate
improvements for reflected methods
When configuring a CLR method for a field, the method arguments now allow the use of all of the new attributes available to AutoRegisteringObjectGraphType
, such as [FromServices]
. Field resolvers are now precompiled, resulting in faster performance.
As always, when the CLR type is not the source type, the CLR type is pulled from DI. Now the CLR type will be pulled from context.RequestServices
to allow for scoped instances. If RequestServices
is null
, the root DI provider will be used as it was before.
Note that existing methods will require the use of [FromSource]
and [FromUserContext]
for applicable method arguments.
[GraphQLMetadata("Droid")]
class DroidType
{
private readonly Repository _repo;
public DroidType(Repository repo)
{
_repo = repo;
}
public int Id(Droid source) => source.Id;
public IEnumerable<Droid> Friends(Droid source) => _repo.FriendsOf(source.Id);
}
[GraphQLMetadata("Droid")]
class DroidType
{
private readonly Repository _repo;
public DroidType(Repository repo)
{
_repo = repo;
}
public int Id([FromSource] Droid source) => source.Id;
public IEnumerable<Droid> Friends([FromSource] Droid source) => _repo.FriendsOf(source.Id);
}
[GraphQLMetadata("Droid")]
class DroidType
{
public int Id([FromSource] Droid source) => source.Id;
public IEnumerable<Droid> Friends([FromSource] Droid source, [FromServices] Repository repo) => repo.FriendsOf(source.Id);
}
Similar changes may be necessary when using FieldDelegate
to assign a field resolver.
The execution pipeline has been changed to use ValueTask
throughout. To support this change, the following interfaces have been slightly changed to have methods with ValueTask
signatures:
IFieldResolver
IEventStreamResolver
(renamed to ISourceStreamResolver
) (IAsyncEventStreamResolver
has been removed)IFieldMiddleware
IValidationRule
This will result in a substantial speed increase for schemas that use field middleware.
In addition, ValueTask<T>
return types are supported for fields built on CLR methods via the schema builder, fields built on CLR methods via AutoRegisteringObjectGraphType
, and fields built on CLR methods via FieldDelegate
.
When manually instantiating a field or subscription resolver, you may use a delegate that return a ValueTask
by using new constructors available on the FuncFieldResolver
or SourceStreamResolver
classes.
NameFieldResolver
enhanced method support
When adding a field by name only, such as Field<StringGraphType>("Name");
, and the field matches a method rather than a property on the source object, the method parameters are parsed similarly to FieldDelegate
as noted above with support for query arguments, IResolveFieldContext
, [FromServices]
and so on.
A new builder method AddAutoSchema
has been added to allow building a schema entirely from CLR types using the new features within the auto-registering graph types to build the schema. Below is a sample:
var services = new ServiceCollection();
services.AddGraphQL(b => b
.AddAutoSchema<Query>(s => s.WithMutation<Mutation>())
.AddSystemTextJson());
var provider = services.BuildServiceProvider();
var result = await provider.GetRequiredService<IDocumentExecuter>().ExecuteAsync(o =>
{
o.RequestServices = provider;
o.Schema = provider.GetRequiredService<ISchema>();
o.Query = "{hero}";
});
var resultString = provider.GetRequiredService<IGraphQLTextSerializer>().Serialize(result);
public class Query
{
public static string Hero => "Luke Skywalker";
public static IEnumerable<Droid> Droids => new Droid[] { new Droid("R2D2"), new Droid("C3PO") };
}
public class Mutation
{
public static string Hero(string name) => name;
}
public record Droid(string Name);
Subscriptions are supported; interface or union graph types are not currently supported. You may mix the documented "graphtype-first" approach with the CLR types to implement anything not supported by the auto-registering graph types.
17.IGraphQLSerializer
implementations support serialization of ExecutionError
instances.
Previously, only when serializing an ExecutionResult
instance would ExecutionError
instances be properly serialized. Now you may serialize a ExecutionError
instance directly and it will be handled by the specified IErrorInfoProvider
and serialized correctly. This can be useful when implementing the newer graphql-transport-ws
WebSockets protocol.
IDocumentExecuter<>
interface added to better support multiple schema registrations.
To better support user classes based on a specific schema, the IDocumentExecuter<>
interface and default implementation has been added which allows for executing a request without specifying the schema in the ExecutionOptions
. The execution will pull the schema from dependency injection at run-time, supporting both singleton and scoped schemas. RequestServices
is required to be provided.
var executer = serviceProvider.GetRequiredService<IDocumentExecuter<MySchema>>();
var options = new ExecutionOptions
{
Query = "{hero}",
RequestServices = serviceProvider,
};
var result = await executer.ExecuteAsync(options);
19. Subscription support improved
Support for subscriptions has been moved from the GraphQL.SystemReactive
nuget package directly into the main GraphQL
package. There is no need to use SubscriptionDocumentExecuter
(removed), and the default document executer will support subscriptions without overriding SelectExecutionStrategy
.
The new implementation of SubscriptionExecutionStrategy
supports some new features and bug fixes:
Serial execution of data events' field resolvers is supported by passing an instance of SerialExecutionStrategy
to the constructor. As before, parallel execution is default.
Errors and output extensions are returned along with data events.
Memory leaks have been eliminated in the case of errors, output extensions, metrics being enabled, or the use of the context's array pool.
The unhandled exception handler properly handles all error situations that it was designed to.
The System.Reactive
nuget reference is not necessary for GraphQL. You may still choose to use System.Reactive
nuget package in your library if you wish.
Derived implementations allow for a scoped DI provider during execution of data events. It will be necessary to override ProcessDataAsync
and change the ExecutionContext.RequestServices
property to a scoped instance before calling base.ProcessDataAsync
.
There are a number of other minor issues fixed; see these links for more details:
Authorize
, AuthorizeWithRoles
and AllowAnonymous
extension methods added in GraphQL 5.1.0 and 5.1.1
AuthorizeWithRoles
allows for specifying roles rather than just policies that can be used to validate a request. Authorize
can be used to specify that only authentication is required, without specifying any specific roles or policies. AllowAnonymous
typically indicates that anonymous access should be allowed to a field of a graph type requiring authorization, providing that no other fields were selected. As with AuthorizeWithPolicy
(renamed from AuthorizeWith
), these new methods require support by a third-party library to perform the validation.
Similar to the ASP.NET Core AuthorizeAttribute
, the new AuthorizeWithRoles
method accepts a comma-separated list of role names that would allow access to the graph or field.
graph.AuthorizeWithRoles("Administrators,Managers");
You may also supply a list of strings as in the following example:
graph.AuthorizeWithRoles("Administrators", "Managers");
For schema-first and "type-first" graphs, the [GraphQLAuthorize]
has been updated to support roles and can now be used without any policy or role names, and an [AllowAnonymous]
attribute has been added.
RequestServices
added to ValidationContext
in GraphQL 5.1.0
This allows for validation rules to access scoped services if necessary.
Breaking Changes 1. UnhandledExceptionDelegateExecutionOptions.UnhandledExceptionDelegate
and IExecutionContext.UnhandledExceptionDelegate
properties type was changed from Action<UnhandledExceptionContext>
to Func<UnhandledExceptionContext, Task>
so now you may use async/await for exception handling. In this regard, some methods in ExecutionStrategy
were renamed to have Async
suffix.
IDocumentCache
now has asynchronous methods instead of synchronous methods.
The default get/set property of the interface has been replaced with GetAsync
and SetAsync
methods. Keys cannot be removed by setting a null value as they could before.
IResolveFieldContext.Extensions
property renamed to OutputExtensions
and related changes
To clarify and differ output extensions from input extensions, IResolveFieldContext.Extensions
has now been renamed to OutputExtensions
. The GetExtension
and SetExtension
thread-safe extension methods have also been renamed to GetOutputExtension
and SetOutputExtension
respectively.
ExecutionOptions.Inputs
and ValidationContext.Inputs
properties renamed to Variables
To better align the execution options and variable context with the specification, the Inputs
property containing the execution variables has now been renamed to Variables
.
ConfigureExecution
GraphQL builder method renamed to ConfigureExecutionOptions
Also, IConfigureExecution
renamed to IConfigureExecutionOptions
.
AddGraphQL
now accepts a configuration delegate instead of returning IGraphQLBuilder
In order to prevent default implementations from ever being registered in the DI engine, the AddGraphQL
method now accepts a configuration delegate where you can configure the GraphQL.NET DI components. To support this change, the GraphQLBuilder
constructor now requires a configuration delegate parameter and will execute the delegate before calling GraphQLBuilderBase.RegisterDefaultServices
.
This requires a change similar to the following:
services.AddGraphQL()
.AddSystemTextJson()
.AddSchema<StarWarsSchema>();
services.AddGraphQL(builder => builder
.AddSystemTextJson()
.AddSchema<StarWarsSchema>());
7. GraphQLExtensions.BuildNamedType
was renamed and moved to SchemaTypes.BuildGraphQLType
8. GraphQLBuilderBase.Initialize
was renamed to RegisterDefaultServices
9. ExecutionHelper.GetArgumentValues
was renamed to GetArguments
10. DirectiveGraphType
was renamed to Directive
11. schema
, variableDefinitions
and variables
arguments were removed from ValidationContext.GetVariableValues
Use ValidationContext.Schema
, ValidationContext.Operation.Variables
and ValidationContext.Variables
properties
ValidationContext.OperationName
was changed to ValidationContext.Operation
13. All arguments from IDocumentValidator.ValidateAsync
were wrapped into ValidationOptions
struct 14. All methods from IGraphQLBuilder
were moved into IServiceRegister
interface
Use IGraphQLBuilder.Services
property if you need to register services into DI container. If you use provided extension methods upon IGraphQLBuilder
then your code does not require any changes.
GraphQL.Language.AST
namespace and all classes from it have been removed in favor of ones from GraphQLParser.AST
namespace in GraphQL-Parser project. Examples of changed usages:
GraphQL.Language.AST.Document
-> GraphQLParser.AST.GraphQLDocument
GraphQL.Language.AST.IValue
-> GraphQLParser.AST.GraphQLValue
GraphQL.Language.AST.Field
-> GraphQLParser.AST.GraphQLField
GraphQL.Language.AST.SelectionSet
-> GraphQLParser.AST.GraphQLSelectionSet
GraphQL.Language.AST.IHaveDirectives
-> GraphQLParser.AST.IHasDirectivesNode
GraphQL.Language.AST.IType
-> GraphQLParser.AST.GraphQLType
GraphQLParser.ROM
struct instead of string
:
ExecutionResult.Query
Metrics.SetOperationName
IComplexGraphType.GetField
QueryArguments.Find
SchemaDirectives.Find
SchemaDirectives.this[]
SchemaDirectives.Dictionary
ValidationContext.GetFragment
ValidationError
's constructors take originalQuery as ROM
OperationType
and DirectiveLocation
enums were removed, use enums from GraphQLParser.AST
namespaceSourceLocation
struct was removed, use GraphQLLocation
from GraphQLParser.AST
namespaceCoreToVanillaConverter
class was removedErrorLocation
struct was removed, use Location
from GraphQLParser
namespaceValidationContext.GetFragment
method was removed, use ValidationContext.Document.FindFragmentDefinition
IResolveFieldContext.SubFields
and IExecutionStrategy.GetSubFields
returns dictionary with values of tuple of queried field and its field definitionGraphQLParser.AST.GraphQLValue
instead of GraphQL.Language.AST.IValue
IInputObjectGraphType.ToAST
returns GraphQLParser.AST.GraphQLObjectValue
instead of GraphQL.Language.AST.IValue
The following classes and members that were marked with [Obsolete]
in v4 have been removed:
GraphQL.NewtonsoftJson.StringExtensions.GetValue
GraphQL.NewtonsoftJson.StringExtensions.ToDictionary
Use Read
or Deserialize
instead GraphQL.SystemTextJson.ObjectDictionaryConverter
Use InputsJsonConverter
instead GraphQL.SystemTextJson.StringExtensions.ToDictionary
Use Read
or Deserialize
instead GraphQL.TypeExtensions.GetEnumerableElementType
GraphQL.TypeExtensions.IsNullable
GraphQL.Builders.ConnectionBuilder.Unidirectional
Unidirectional
is default and does not need to be called GraphQL.IDocumentExecutionListener.BeforeExecutionAwaitedAsync
Use IDataLoaderResult
interface instead GraphQL.IDocumentExecutionListener.BeforeExecutionStepAwaitedAsync
Use IDataLoaderResult
interface instead GraphQL.Utilities.DeprecatedDirectiveVisitor
Various classes' properties in the GraphQL.Language.AST
namespace are now read-only instead of read-write, such as Field.Alias
.
Various classes' constructors in the GraphQL.Language.AST
namespace have been removed in favor of other constructors.
IDocumentWriter
has been renamed to IGraphQLSerializer
and related changes.
As such, the DocumentWriter
classes have been renamed to GraphQLSerializer
, and the AddDocumentWriter
extension method for IGraphQLBuilder
has been renamed to AddSerializer
. The WriteAsync
method's functionality has not changed.
ToInputs
) have been removed.
Please use the Read<Inputs>()
method of an IGraphQLSerializer
implementation, or the Deserialize<Inputs>()
method of an IGraphQLTextSerializer
implementation. Note that these methods will return null
if a null string or the string "null" is passed to them. The ExecutionOptions.Variables
property does not require Inputs.Empty
, but if you have tests based on the .ToInputs()
extension method, you may want a direct replacement. Equivalent code to the previous functionality is as follows:
using GraphQL;
using GraphQL.SystemTextJson;
public static class StringExtensions
{
private static readonly GraphQLSerializer _serializer = new();
public static Inputs ToInputs(this string json)
=> json == null ? Inputs.Empty : _serializer.Deserialize<Inputs>(json) ?? Inputs.Empty;
public static Inputs ToInputs(this System.Text.Json.JsonElement element)
=> _serializer.ReadNode<Inputs>(element) ?? Inputs.Empty;
public static T? FromJson<T>(this string json)
=> _serializer.Deserialize<T>(json);
public static System.Threading.Tasks.ValueTask<T?> FromJsonAsync<T>(this System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default)
=> _serializer.ReadAsync<T>(stream, cancellationToken);
}
The new Read
and Deserialize
methods of the Newtonsoft.Json
implementation will default to reading dates as strings unless configured otherwise in the settings.
WriteToStringAsync
extension methods have been removed.
Please use the Serialize()
method of an IGraphQLTextSerializer
implementation. The asynchronous text serialization methods have been removed as the underlying serialization providers execute synchronously when serializing to a string.
The WriteAsync()
method can be used to asynchronously serialize to a stream. However, the Newtonsoft.Json
serializer does not support asynchronous serialization, so synchronous calls are made to the underlying stream. Only System.Text.Json
supports asynchronous writing.
InputsConverter
renamed to InputsJsonConverter
ExecutionResultContractResolver
renamed to GraphQLContractResolver
GraphQLMetadataAttribute
cannot be applied to graph type classes
The [GraphQLMetadata]
attribute is designed to be used for schema-first configurations and has not changed in this regard. For code-first graph definitions, please set the GraphQL type name within the constructor.
public class HumanType : ObjectGraphType<Human>
{
public HumanType()
{
Name = "Person";
...
}
}
22. AstPrinter
class was removed
AstPrinter
class was removed in favor of SDLPrinter
from GraphQL-Parser project.
Code before changes:
INode node = ...;
string s = AstPrinter.Print(node);
Code after changes:
ASTNode node = ...;
var writer = new StringWriter();
var printer = new SDLPrinter();
sdlPrinter.PrintAsync(node, writer).GetAwaiter().GetResult();
string s = writer.ToString();
SDLPrinter
is a highly optimized visitor for asynchronous non-blocking SDL output into provided TextWriter
. In the majority of cases it does not allocate memory in the managed heap at all.
InputObjectGraphType<TSourceType>
InputObjectGraphType<TSourceType>.ToAST
and InputObjectGraphType<TSourceType>.IsValidDefault
methods were changed in such a way that now you may be required to also override ToAST
if you override ParseDictionary
. Changes in those methods are made for earlier error detection and schema printing.
AutoRegisteringObjectGraphType
changes
The protected method GetRegisteredProperties
has been renamed to GetRegisteredMembers
and now supports properties, methods and fields, although fields are not included with the default implementation. Override the method in a derived class to include fields.
New for v5, methods are included by default. To revert to v4 behavior, which does not include methods, create a derived class as follows:
public class AutoRegisteringObjectGraphTypeWithoutMethods<T> : AutoRegisteringObjectGraphType<T>
{
public AutoRegisteringObjectGraphTypeWithoutMethods() : base() { }
public AutoRegisteringObjectGraphTypeWithoutMethods(params Expression<Func<T, object?>>[]? excludedProperties) : base(excludedProperties) { }
protected override IEnumerable<MemberInfo> GetRegisteredMembers() => base.GetRegisteredMembers().Where(x => x is PropertyInfo);
}
Register this class within your DI engine like this:
services.AddTransient(typeof(AutoRegisteringObjectGraphType<>), typeof(AutoRegisteringObjectGraphTypeWithoutMethods<>));
25. AutoRegisteringInputObjectGraphType
changes
The protected method GetRegisteredProperties
has been renamed to GetRegisteredMembers
and now supports returning both properties and fields, although fields are not included with the default implementation. Override the method in a derived class to include fields.
EnumerationGraphType
parses exact names
Consider GraphQL enum Color { RED GREEN BLUE }
and corresponding EnumerationGraphType
. In v4 ParseValue("rED")
yields internal value for RED
name. In v5 this behavior was changed and ParseValue("rED")
throws error Unable to convert 'rED' to the scalar type 'Color'
.
See https://github.com/graphql-dotnet/graphql-dotnet/issues/3105 for code to revert to the old behavior.
27.EnumerationGraphType.AddValue
changes
description
argument from EnumerationGraphType.AddValue
method was marked as optional and moved after value
argument. If you use this method and set descriptions, you will need to change the order of arguments. Since changing the order of arguments in some cases can remain invisible to the caller and lead to hardly detected bugs, the method name has been changed from AddValue
to Add
.
GraphQL.NewtonsoftJson.GraphQLSerializer
has changed.
Previously the settings class used was Newtonsoft.Json.JsonSerializerSettings
. Now the class is GraphQL.NewtonsoftJson.JsonSerializerSettings
. The class inherits from the former class, but sets the default date parsing behavior set to 'none'.
[FromSource]
and [FromUserContext]
where applicable
See New Features: 'Schema builder and FieldDelegate
improvements for reflected methods' above.
[FromSource]
and [FromUserContext]
where applicable
See New Features: 'Schema builder and FieldDelegate
improvements for reflected methods' above.
The following classes and methods have been removed:
EventStreamResolver
implementation which accepted an IAccessor
as a construtor parameter.AsyncEventStreamResolver
implementation which accepted an IAccessor
as a construtor parameter.DelegateFieldModelBinderResolver
class.ReflectionHelper.BuildArguments
method.You may use the following classes and methods as replacements:
MemberResolver
class is an IFieldResolver
implementation for a property, method or field. Expressions are passed to the constructor for the instance (and if applicable, method arguments), which is immediately compiled.SourceStreamMethodResolver
class is an ISourceStreamResolver
(previously IEventStreamResolver
) implementation for a method that returns an IObservable<T>
or Task<IObservable<T>>
. It also provides a basic IFieldResolver
implementation for subscription fields.AutoRegisteringHelper.BuildFieldResolver
method builds a field resolver around a specifed property, method or field.AutoRegisteringHelper.BuildEventStreamResolver
method builds an event stream resolver around a specified method.The following interfaces have been modified to support a ValueTask
pipeline:
IFieldResolver
ISourceStreamResolver
(previously IEventStreamResolver
)IFieldMiddleware
and FieldMiddlewareDelegate
IValidationRule
The following interfaces have been removed:
IAsyncEventStreamResolver
All classes which implemented the above interfaces have been modified as necessary:
ExpressionFieldResolver
FuncFieldResolver
(AsyncFieldResolver
was absorbed by it)InstrumentFieldsMiddleware
NameFieldResolver
SourceStreamResolver
(previously EventStreamResolver
and AsyncEventStreamResolver
)These properties have been removed:
EventStreamFieldType.AsyncSubscriber
(note: the EventStreamFieldType
class was removed and the Subscriber
property moved to the FieldType
class and renamed to StreamResolver
)FieldConfig.AsyncSubscriber
Any direct implementation of these interfaces or classes derived from the above list will need to be modified to fit the new design.
In addition, it is required that any asynchronous fields must use an appropriate asynchronous field builder method or asynchronous field resolver, and inferred methods (built by the schema builder, FieldDelegate
, or AutoRegisteringObjectGraphType
) must be strongly typed.
Field<CharacterInterface>("hero", resolve: context => data.GetDroidByIdAsync("3"));
FieldAsync<CharacterInterface>("hero", resolve: async context => await data.GetDroidByIdAsync("3"));
AddField(new FieldType
{
Name = "hero",
Resolver = new FuncFieldResolver<Task<Droid>>(context => data.GetDroidByIdAsync("3")),
});
AddField(new FieldType
{
Name = "hero",
Resolver = new AsyncFieldResolver<Droid>(context => data.GetDroidByIdAsync("3")),
});
Func<IResolveFieldContext, string, object> func = (context, id) => data.GetDroidByIdAsync(id);
FieldDelegate<DroidType>(
"droid",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the droid" }
),
resolve: func
);
Func<IResolveFieldContext, string, Task<Droid>> func = (context, id) => data.GetDroidByIdAsync(id);
FieldDelegate<DroidType>(
"droid",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the droid" }
),
resolve: func
);
33. IResolveEventStreamContext
interface and ResolveEventStreamContext
class removed
Please use the IResolveFieldContext
interface and the ResolveFieldContext
class instead. No other changes are required.
EventStreamFieldType
class removed
Please use FieldType
instead. The Subscriber
property has been moved to the FieldType
class so no other changes should be required. The AsyncSubscriber
property has been removed as described above.
IEventStreamResolver<T>
interface removed
For custom resolver implementations, please implement ISourceStreamResolver
(previously IEventStreamResolver
) interface instead.
These classes have been removed:
ScopedAsyncFieldResolver
AsyncFieldResolver
AsyncEventStreamResolver
Please use the new ValueTask
-based constructors on ScopedFieldResolver
, FuncFieldResolver
and SourceStreamResolver
(previously EventStreamResolver
) instead.
var resolver = new AsyncFieldResolver<string>(async context => await GetSomeString());
var resolver = new FuncFieldResolver<string>(async context => await GetSomeString());
Func<IResolveFieldContext, Task<string>> func = async context => await GetSomeString();
var resolver = new AsyncFieldResolver(func);
Func<IResolveFieldContext, ValueTask<string>> func = async context => await GetSomeString();
var resolver = new FuncFieldResolver(func);
Func<IResolveFieldContext, Task<string>> func = async context => await GetSomeString();
var resolver = new FuncFieldResolver(context => new ValueTask<string>(func(context)));
Func<IResolveFieldContext, Task<string>> func = async context => await GetSomeString();
var resolver = new FuncFieldResolver(async context => await func(context));
Field builder methods have not changed and still require a Task<T>
return value for asynchronous field resolver delegates.
NameFieldResolver
implementation supports methods with arguments; may cause AmbigiousMatchException
The NameFieldResolver
, used when adding a field by name (e.g. Field<StringGraphType>("Name");
), now supports methods with arguments. During resolver execution, it first looks for a matching property with the specified name, and if none is found, looks for a method with the matching name. Since it now supports methods with arguments as well as methods without arguments, an AmbigiousMatchException
can occur if the name refers to a public method with multiple overloads. Either specify a field resolver explicitly, or reduce the number of public methods with the same name to one.
SchemaTypes
updated to support DI-injected mapping providers
Initialize
method signature changed to include DI-injected mappings.
GetGraphTypeFromClrType
method signature changed to include DI-injected mappings. Rather than a list of CLR to graph type tuples provided to the method, now a list of IGraphTypeMappingProvider
instances is provided.
ExecutionResultJsonConverter
does not handle ExecutionError
.
If you directly create an instance of ExecutionResultJsonConverter
and adding it to a set of serializer options, you will now need to also add ExecutionErrorJsonConverter
also. The IErrorInfoProvider
instance previously passed to the ExecutionResultJsonConverter
will need to be passed to the ExecutionErrorJsonConverter
instead. Typically no changes are necessary to user code for this API change.
Subscription support is provided by the DocumentExecuter
implementation without the need to use SubscriptionDocumentExecuter
or override DocumentExecuter.SelectExecutionStrategy
. You may also remove references to the IGraphQLBuilder.AddSubscriptionDocumentExecuter
method.
Subscription support has been moved into the main project. If you have a need to reference SubscriptionExecutionStrategy
, it now exists within the GraphQL
nuget package. You will need to remove references to the GraphQL.SystemReactive
nuget package.
SubscriptionExecutionResult
class removed
The SubscriptionExecutionResult.Streams
property has been moved to the ExecutionResult
class. Please use the ExecutionResult
class rather than the SubscriptionExecutionResult
class.
DateTimeOffsetGraphType
does not adjust to UTC
Previously any ISO-8601 date/time values were converted to UTC before being returned in a DateTimeOffset
value from the DateTimeOffsetGraphType
. This results in a loss of information that was provided to the GraphQL request. In v5, the time offset is preserved.
Although typically DateTimeOffset
values are not assumed to be in any specific time zone, if your code does so, you may need to make changes to your code or implement a custom scalar to replace the default scalar.
You may be affected by this change if you use certain versions of Npgsql. See https://www.npgsql.org/doc/types/datetime.html
44.OverlappingFieldsCanBeMerged
validation rule enabled by default
Previously this rule, part of the GraphQL specification, was not enabled by default; in GraphQL.NET v5 it is enabled by default as part of the DocumentValidator.CoreRules
list.
IEventStreamResolver
is now ISourceStreamResolver
EventStreamResolver
is now SourceStreamResolver
IAsyncEventStreamResolver
and AsyncEventStreamResolver
have been removedIEventStreamResolver.Subscriber
is now ISourceStreamResolver.ResolveAsync
Subscribe
and SubscribeAsync
methods are now ResolveStream
and ResolveStreamAsync
ValidationContext.GetRecursiveVariables
returns null instead of an empty list
This was done for the purposes of the overall strategy for reducing memory consumption.
This change was made to clarify and differentiate between AuthorizeWithRoles
.
GraphQLAuthorizeAttribute
renamed to AuthorizeAttribute
in 5.1.1
This change was made to align with the rest of the attributes' names.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4