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 76ed0ecaa4a..301ee20057d 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/IObjectFieldDescriptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/IObjectFieldDescriptor.cs @@ -231,6 +231,37 @@ IObjectFieldDescriptor ResolveWith( /// IObjectFieldDescriptor ResolveWith(MemberInfo propertyOrMethod); + /// + /// Adds a resolver based on a delegate to the field. + /// A resolver is a method that resolves the value for a + /// field. The resolver can access parent object, arguments, services and more through the + /// . + /// + /// The resolver of the field + /// + /// Given the following resolvers class + /// + /// GetFoo( + /// [Service] IFooService service, + /// CancellationToken cancellationToken) => + /// service.GetFooAsync(cancellationToken); + /// } + /// ]]> + /// + /// The GetFoo method can be mapped like: + /// + /// x.Foo) + /// .ResolveWith(Resolvers.GetFoo); + /// ]]> + /// + /// + IObjectFieldDescriptor ResolveWith(Delegate @delegate); + /// /// Adds a subscription resolver to to the field /// diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/ObjectFieldDescriptor.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/ObjectFieldDescriptor.cs index 6e8ab58eae9..e40555dc051 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/ObjectFieldDescriptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/ObjectFieldDescriptor.cs @@ -400,6 +400,15 @@ public IObjectFieldDescriptor ResolveWith(MemberInfo propertyOrMethod) return ResolveWithInternal(propertyOrMethod, propertyOrMethod.DeclaringType); } + /// + public IObjectFieldDescriptor ResolveWith(Delegate @delegate) + { + ArgumentNullException.ThrowIfNull(@delegate); + + var method = @delegate.Method; + return ResolveWithInternal(method, method.IsStatic ? null : method.DeclaringType); + } + private IObjectFieldDescriptor ResolveWithInternal( MemberInfo propertyOrMethod, Type? resolverType) diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeTests.cs index 4d625b68366..62b3701c80c 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeTests.cs @@ -1733,6 +1733,60 @@ public void ResolveWithAsync() .MatchSnapshot(); } + [Fact] + public void ResolveWithStatic() + { + SchemaBuilder.New() + .AddQueryType() + .Create() + .MakeExecutable() + .Execute("{ foo baz }") + .ToJson() + .MatchSnapshot(); + } + + [Fact] + public void ResolveWithStaticAsync() + { + SchemaBuilder.New() + .AddQueryType() + .Create() + .MakeExecutable() + .Execute("{ foo baz qux }") + .ToJson() + .MatchSnapshot(); + } + + [Fact] + public void ResolveWithInstanceDelegate() + { + SchemaBuilder.New() + .AddQueryType() + .Create() + .MakeExecutable() + .Execute("{ foo baz }") + .ToJson() + .MatchSnapshot(); + } + + [Fact] + public void ResolveWithLambdaDelegate() + { + Func lambda = () => "Lambda"; + + SchemaBuilder.New() + .AddQueryType(new ObjectType(d => + { + d.Field(t => t.Foo).ResolveWith(lambda); + d.Field("baz").ResolveWith(lambda); + })) + .Create() + .MakeExecutable() + .Execute("{ foo baz }") + .ToJson() + .MatchSnapshot(); + } + [Fact] public void ResolveWith_NonGeneric() { @@ -2353,6 +2407,16 @@ public Task BarAsync(IResolverContext? context) => Task.FromResult(context is not null); } + public static class ResolveWithStaticQueryResolver + { + public static string Bar() => "Bar"; + + public static Task FooAsync() => Task.FromResult("Foo"); + + public static Task BarAsync(IResolverContext context) + => Task.FromResult(context is not null); + } + public class ResolveWithQueryType : ObjectType { protected override void Configure(IObjectTypeDescriptor descriptor) @@ -2378,6 +2442,35 @@ protected override void Configure(IObjectTypeDescriptor descri } } + public class ResolveWithStaticQueryType : ObjectType + { + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor.Field(t => t.Foo).ResolveWith(ResolveWithStaticQueryResolver.Bar); + descriptor.Field("baz").ResolveWith(ResolveWithStaticQueryResolver.Bar); + } + } + + public class ResolveWithStaticQueryTypeAsync : ObjectType + { + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor.Field(t => t.Foo).ResolveWith(ResolveWithStaticQueryResolver.FooAsync); + descriptor.Field("baz").ResolveWith(ResolveWithStaticQueryResolver.FooAsync); + descriptor.Field("qux").ResolveWith(ResolveWithStaticQueryResolver.BarAsync); + } + } + + public class ResolveWithInstanceDelegateQueryType : ObjectType + { + protected override void Configure(IObjectTypeDescriptor descriptor) + { + var resolver = new ResolveWithQueryResolver(); + descriptor.Field(t => t.Foo).ResolveWith(resolver.FooAsync); + descriptor.Field("baz").ResolveWith(resolver.BarAsync); + } + } + public class ResolveWithNonGenericObjectType : ObjectType { protected override void Configure(IObjectTypeDescriptor descriptor) diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.ResolveWithInstanceDelegate.snap b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.ResolveWithInstanceDelegate.snap new file mode 100644 index 00000000000..c102fc3d841 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.ResolveWithInstanceDelegate.snap @@ -0,0 +1,6 @@ +{ + "data": { + "foo": "Foo", + "baz": true + } +} diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.ResolveWithLambdaDelegate.snap b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.ResolveWithLambdaDelegate.snap new file mode 100644 index 00000000000..cc0f0890478 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.ResolveWithLambdaDelegate.snap @@ -0,0 +1,6 @@ +{ + "data": { + "foo": "Lambda", + "baz": "Lambda" + } +} diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.ResolveWithStatic.snap b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.ResolveWithStatic.snap new file mode 100644 index 00000000000..0f7762c7429 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.ResolveWithStatic.snap @@ -0,0 +1,6 @@ +{ + "data": { + "foo": "Bar", + "baz": "Bar" + } +} diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.ResolveWithStaticAsync.snap b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.ResolveWithStaticAsync.snap new file mode 100644 index 00000000000..49f1a810e3f --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.ResolveWithStaticAsync.snap @@ -0,0 +1,7 @@ +{ + "data": { + "foo": "Foo", + "baz": "Foo", + "qux": true + } +}