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 GQL009

To disable the rule for a file, folder, or project, set its severity to none in the configuration file.

[*.cs]
dotnet_diagnostic.GQL009.severity = none

For more information, see How to suppress code analysis warnings.