GQL009: Use async resolver
| Value | |
|---|---|
| Rule ID | GQL009 |
| Category | Usage |
| Default severity | Error |
| Enabled by default | Yes |
| Code fix provided | Yes |
| Introduced in | v7.7 |
Cause
This rule triggers when the sync version of the Resolve or ResolveScoped
methods is used with awaitable delegate.
Rule description
ResolveAsync and ResolveScopedAsync methods should be used to register
awaitable delegates. The most common awaitable types are
System.Threading.Tasks.Task and System.Threading.Tasks.ValueTask, but any
type that defines a valid GetAwaiter() method, which returns a valid awaiter
with a GetResult() method is considered awaitable and is detected by the
analyzer.
The rule is useful when FieldBuilder is defined without return type argument
or the return type argument is defined as object or dynamic and compiler
allows returning Task<T> and other awaitables from the delegate.
How to fix violations
Replace the sync Resolve or ResolveScoped method with matching async version
and await the delegate when needed.
Example of a violation
public class MyGraphType : ObjectGraphType<Person>
{
public MyGraphType()
{
// 1. no return type defined
Field<StringGraphType>("Title")
.Resolve(ctx => Task.FromResult("developer"));
// 2. the method returns ValueTask<T>
Field<StringGraphType>("Title")
.Resolve(ctx => GetTitleAsync());
// 3. method group, return type defined in Field method
Field<StringGraphType, object>("Title")
.Resolve(ResolveTitleAsync);
// 4. field builder created with awaitable return type
Field<StringGraphType, Task<string>>("Title")
.ResolveScoped(ctx => Task.FromResult("developer"));
// 5. the method returns ValueTask<T>
Field<StringGraphType>("Title")
.ResolveScoped(ctx => GetTitleAsync());
// 6. object or dynamic is used as return type in Returns method
Field<StringGraphType>("Title").Returns<dynamic>()
.ResolveScoped(ctx => ResolveTitleAsync(ctx));
}
private ValueTask<string> GetTitleAsync() => ValueTask.FromResult("developer");
private Task<string> ResolveTitleAsync(IResolveFieldContext<Person> ctx) =>
Task.FromResult("developer");
}Example of how to fix
public class MyGraphType : ObjectGraphType<Person>
{
public MyGraphType()
{
// 1. no return type defined
// await the delegate
Field<StringGraphType>("Title")
.ResolveAsync(async ctx => await Task.FromResult("developer"));
// 2. the method returns ValueTask<T>
// await the delegate
Field<StringGraphType>("Title")
.ResolveAsync(async ctx => await GetTitleAsync());
// 3. method group, return type defined in Field method
// await the delegate
Field<StringGraphType, object>("Title")
.ResolveAsync(async ctx => await ResolveTitleAsync(ctx));
// 4. field builder created with awaitable return type
// unwrap the return type
Field<StringGraphType, string>("Title")
.ResolveScopedAsync(ctx => Task.FromResult("developer"));
// 5. the method returns ValueTask<T>
// await the delegate and defined the return type
Field<StringGraphType, string>("Title")
.ResolveScopedAsync(async ctx => await GetTitleAsync());
// 6. object or dynamic is used as return type in Returns method
// await the delegate and define the source type and return type
// on the ResolveScopedAsync method
Field<StringGraphType>("Title").Returns<dynamic>()
.ResolveScopedAsync<Person, dynamic>(async ctx =>
await ResolveTitleAsync(ctx));
}
private ValueTask<string> GetTitleAsync() => ValueTask.FromResult("developer");
private Task<string> ResolveTitleAsync(IResolveFieldContext<Person> ctx) =>
Task.FromResult("developer");
}Suppress a warning
If you just want to suppress a single violation, add preprocessor directives to your source file to disable and then re-enable the rule.
#pragma warning disable GQL009
// The code that's violating the rule is on this line.
#pragma warning restore GQL009To disable the rule for a file, folder, or project, set its severity to none
in the
configuration file.
[*.cs]
dotnet_diagnostic.GQL009.severity = noneFor more information, see How to suppress code analysis warnings.