1

I'm trying to work through a very simple example of a graphql server and picked.

https://www.blexin.com/en-US/Article/Blog/Creating-our-API-with-GraphQL-and-Hot-Chocolate-79

(there's no SQL back end, I want to understand it end to end, so this has in memory tables of data).

I've implemented it pretty much as per the walkthrough (some of the attributes e.g "[UseFiltering]" don't compile, I've just commented them out for the moment).

my start up looks like this though

        services.AddSingleton<IAuthorService, InMemoryAuthorService>();
        services.AddSingleton<IBookService, InMemoryBookService>();

        services.
            AddGraphQLServer().
            AddType<AuthorType>().
            AddType<BookType>().
            AddQueryType<GraphQL.Query>();

The piece that seems odd is when I try to query the author of a book

{
  books {
    title
    authorId
    price
    author { 
      name
      surname 
    }
  }
}

Banana cake pop complains

field author argument book of type BookInput! is required

(if I don't ask for the Author then everything works nicely)

booktype looks like this (as per the walkthrough)

public class BookType : ObjectType<Book>
{
    protected override void Configure(IObjectTypeDescriptor<Book> descriptor)
    {
        descriptor.Field(b => b.Id).Type<IdType>();
        descriptor.Field(b => b.Title).Type<StringType>();
        descriptor.Field(b => b.Price).Type<DecimalType>();
        descriptor.Field<AuthorResolver>(t => t.GetAuthor(default, default));
    }
}

author resolver looks like this

public class AuthorResolver
{
    private readonly IAuthorService _authorService;

    public AuthorResolver([Service]IAuthorService authorService)
    {
        _authorService = authorService;
    }

    public Author GetAuthor(Book book, IResolverContext ctx)
    {
        return _authorService.GetAll().Where(a => a.Id == book.AuthorId).FirstOrDefault();
    }
}

again, as per the walkthrough.

I basically understand the error, but I can't comprehend how this was ever supposed to work, somehow the "book" parent has to get into the GetAuthor method on the AuthorResolver....I'm missing some magic, or v11 is missing some magic.

P.S.

my preference is for declarative typed expressions, rather than reflexive magic...so maybe I'm missing something

MrD at KookerellaLtd
  • 2,412
  • 1
  • 15
  • 17

2 Answers2

1

The problem is that the GetAuthor method on my AuthorResolver, was missing the "Parent" attribute, to trigger some magic....

    public Author GetAuthor([Parent]Book book, IResolverContext ctx)
    {
        return _authorService.GetAll().Where(a => a.Id == book.AuthorId).FirstOrDefault();
    }

Ideally I would want to remove this custom attribute magic.

MrD at KookerellaLtd
  • 2,412
  • 1
  • 15
  • 17
0

Actually, the current release version 11.0.9 doesn't require [Parent] attribute. I have especially checked that on the resolver class. But in the example below I have removed the resolver because it's too verbose to use it in your case. Also, I have removed Book parameter to show the approach of getting that parameter from the resolver context.

using System.Linq;
using HotChocolate.Resolvers;
using HotChocolate.Types;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace Ademchenko.GraphQLWorkshop
{
    public class Author
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }
    }

    public class Book
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public int AuthorId { get; set; }
        public decimal Price { get; set; }
    }

    public interface IAuthorService { IQueryable<Author> GetAll(); }

    public interface IBookService { IQueryable<Book> GetAll(); }

    public class InMemoryAuthorService : IAuthorService
    {
        private readonly Author[] _staticAuthors = {
            new Author {Name = "FooAuthor", Surname = "FooSurname", Id = 1},
            new Author {Name = "BarAuthor", Surname = "BarSurname", Id = 2},
        };

        public IQueryable<Author> GetAll() => _staticAuthors.AsQueryable();
    }

    public class InMemoryBookService : IBookService
    {
        private readonly Book[] _staticBooks = {
            new Book {Id = 11, Title = "FooBook", AuthorId = 1, Price = 10.2m},
            new Book {Id = 22, Title = "BarBook", AuthorId = 2, Price = 20.2m},
        };

        public IQueryable<Book> GetAll() => _staticBooks.AsQueryable();
    }

    public class Query
    {
        public IQueryable<Book> GetBooks(IResolverContext ctx) => ctx.Service<IBookService>().GetAll();
    }

    public class BookType : ObjectType<Book>
    {
        protected override void Configure(IObjectTypeDescriptor<Book> descriptor)
        {
            descriptor.Field(b => b.Id).Type<IdType>();
            descriptor.Field("author").Resolve((ctx, ct) => ctx.Service<IAuthorService>().GetAll().FirstOrDefault(a => a.Id == ctx.Parent<Book>().AuthorId));
        }
    }

    public class Startup
    {
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration) => Configuration = configuration;

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddSingleton<IAuthorService, InMemoryAuthorService>();
            services.AddSingleton<IBookService, InMemoryBookService>();

            services.AddGraphQLServer()
                .AddQueryType<Query>()
                .AddType<BookType>();
        }
        
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env) => app.UseRouting().UseEndpoints(endpoints => endpoints.MapGraphQL());
    }
}

This is a completely finished example, so if you make the following request to the endpoint:

{
  books
  {
    id,
    title
    authorId    
   author    
    {
      id
      name            
    }
  } 
}

you will get:

{
  "data": {
    "books": [
      {
        "id": "11",
        "title": "FooBook",
        "authorId": 1,
        "author": {
          "id": 1,
          "name": "FooAuthor"
        }
      },
      {
        "id": "22",
        "title": "BarBook",
        "authorId": 2,
        "author": {
          "id": 2,
          "name": "BarAuthor"
        }
      }
    ]
  }
}
ademchenko
  • 585
  • 5
  • 18