graphql-platform icon indicating copy to clipboard operation
graphql-platform copied to clipboard

F# Record and Purecode First schema Generation

Open aammfe opened this issue 4 years ago • 23 comments

Describe the bug While using F# Record in Purecode First schema Generation Hot throw following exception HotChocolate.SchemaException: 'Unable to infer or resolve a schema type from the type reference Input: System.Collections.IEqualityComparer.'

image

To Reproduce Steps to reproduce the behavior:

  1. F# code type Article = {ArticleId:string; Name:string }
  2. Repository
 public class BookService
        {
            private readonly IMongoCollection<Article> _articles;

            public BookService(IBookstoreDatabaseSettings settings)
            {
                var client = new MongoClient(settings.ConnectionString);
                var database = client.GetDatabase(settings.DatabaseName);

                _articles = database.GetCollection<Article>("Article");
            }

            public IQueryable<Article> GetArticles() => _articles.AsQueryable();
}
  1. Query
public class QueryType
        {
            private readonly BookService bookService;

            public QueryType(BookService bookService)
            {
                this.bookService = bookService;
            }

            [UseFiltering]
            public IQueryable<Article> GetArticles()
            {
                return bookService.GetArticles();
            }
        }
  1. Startup.cs
services.AddGraphQL(sp => SchemaBuilder.New()
              .AddQueryType<QueryType>()
              .Create());

Expected behavior HotChoclate Should work with any .net type as it does with C#

aammfe avatar Jul 15 '20 18:07 aammfe

We will have a look at it although it will take a while since at the moment we are focusing on the next major version of hot chocolate. If you want to try to implement this please go ahead.

michaelstaib avatar Jul 20 '20 10:07 michaelstaib

Hello, I would like to help with this but I'm really lost in the codebase and don't know where to start. Are there any pointers or something that would help me to try to implement this?

landy avatar Jan 14 '21 09:01 landy

HI @landy,

we can help. Are you on our slack channel?

michaelstaib avatar Jan 14 '21 10:01 michaelstaib

For anyone tripping over this, it is possible to work around this by using [<ReferenceEquality>] on your records

timotheegloerfeld avatar Apr 09 '21 09:04 timotheegloerfeld

For anyone tripping over this, it is possible to work around this by using [<ReferenceEquality>] on your records

@timotheegloerfeld Did you find a way to represent non-nullable type in F# record?

sep2 avatar May 10 '21 14:05 sep2

For anyone tripping over this, it is possible to work around this by using [] on your records

@timotheegloerfeld Did you find a way to represent non-nullable type in F# record?

Never mind, I found this [<GraphQLNonNullType>] attribute is exactly what I want.

sep2 avatar May 10 '21 15:05 sep2

For anyone tripping over this, it is possible to work around this by using [<ReferenceEquality>] on your records

@timotheegloerfeld I noticed that if my Record type contains an option then the equality error still occurs. Have you encountered this?

wonbyte avatar May 20 '21 02:05 wonbyte

Yes I have encountered this too. Options don't seem to work as of now. Unfortunately I didn't have much time lately to investigate or setup unit tests as suggested in #2103

timotheegloerfeld avatar May 20 '21 06:05 timotheegloerfeld

@landy I am beginning work on this if you want to help out!

wonbyte avatar May 21 '21 16:05 wonbyte

@wonbyte Do you have a branch and/or a set of tasks that are to be done? Potentially my colleagues an I may be able to dedicate some time to it and help

AlexeyRaga avatar Oct 05 '21 04:10 AlexeyRaga

@AlexeyRaga we did not get started on this so of you want to help just jump in. I can update the current sharp branch. What we need are test cases first. Essentially schema snapshot tests so that we see various sharp constructs to build a schema. Once we have the tests we can fix how things are inferred.

michaelstaib avatar Oct 05 '21 07:10 michaelstaib

@michaelstaib sounds good! Can you point me to an example of such a test? I assume that you should have plenty of them for C#, so I can follow the same pattern?

AlexeyRaga avatar Oct 05 '21 11:10 AlexeyRaga

In this case we are interested in schema snapshot tests, which are simple to write. Essentially we setup a new service collection and configure our GraphQL server, at the end we use the extension method BuildSchemaAsync to create a schema from our configuration. We are using snapshooter to create a SDL representation of the schema MatchSnapshot. Essentially if this test succeeds it creates a schema representation that can be reviewed and every new test run validates against this snapshot.

[Fact]
public async Task EnsureThatFieldsWithDifferentCasingAreNotMerged()
{
    await new ServiceCollection()
        .AddGraphQL()
        .AddQueryType<QueryFieldCasing>()
        .BuildSchemaAsync()
        .MatchSnapshotAsync();
}

michaelstaib avatar Oct 05 '21 19:10 michaelstaib

I will update the F# branch and you can do a PR to the branch.

michaelstaib avatar Oct 05 '21 19:10 michaelstaib

@michaelstaib awaiting for the branch to be rebased

AlexeyRaga avatar Oct 06 '21 22:10 AlexeyRaga

I know this is not implemented yet, but I suspect I got a same problem, when trying to use CliMutable records, e.g.:

Unable to infer or resolve a schema type from the type reference Input: IEqualityComparer. (HotChocolate.Types.ObjectType<.Program.Person>)

Here is an easy repro:

With F#:

open System
open Microsoft.AspNetCore.Builder
open Microsoft.Extensions.Hosting
open Microsoft.Extensions.DependencyInjection
open Microsoft.EntityFrameworkCore 
open HotChocolate.Types 
open HotChocolate.Data
open HotChocolate;

[<CLIMutable>]
type Person =
    { Id : Guid
      FirstName : string
      LastName : string
      Address : string
      City : string}

type AppDbContext(opts) =
    inherit DbContext(opts)
    member this.Persons = this.Set<Person>()

type Query() =
    [<UsePaging>]
    [<UseProjection>]
    [<UseFiltering>]
    [<UseSorting>]
    member this.Persons ([<Service>] dbContext: AppDbContext) =
        dbContext.Persons

[<EntryPoint>]
let main args =

    let builder = WebApplication.CreateBuilder(args)

    builder.Services
            .AddDbContext<AppDbContext>(
                fun opts -> opts.UseSqlite("Data Source=foo.sqlite") |> ignore
            )
            .AddGraphQLServer()
            .AddQueryType<Query>()
            .AddFiltering()
            .AddSorting()
            .AddProjections() |> ignore

    let app = builder.Build()

    app.UseDeveloperExceptionPage() |> ignore
    app.UseRouting() |> ignore
    app.MapGraphQL() |> ignore

    let createDb =
        use scope = app.Services.CreateScope()
        let db = scope.ServiceProvider.GetRequiredService<AppDbContext>()
        db.Database.EnsureDeleted() |> Console.WriteLine
        db.Database.EnsureCreated() |> Console.WriteLine

    createDb
    
    app.Run()

    0 // Exit code
  <ItemGroup>
    <PackageReference Include="EntityFrameworkCore.FSharp" Version="6.0.2" />
    <PackageReference Include="HotChocolate.AspNetCore" Version="12.2.1" />
    <PackageReference Include="HotChocolate.Data" Version="12.3.0" />
    <PackageReference Include="HotChocolate.Data.EntityFramework" Version="12.3.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.0" />
  </ItemGroup>

Ciantic avatar Nov 17 '21 22:11 Ciantic

The f# records are just .net classes, but with more interfaces implemented by default to gain amongst other the value equality. I suspect a C# class with IEqualityComparer implemented might generate the same exception. Maybe Hot Choco does not support any interface implementation for graphql types?

Disable both comparison and equality on your F# types like below. That might solve the issue for you too.

// disable f# equality since Hotchocolate 12.0.0-preview.27 will throw the error below
// Unable to infer or resolve a schema type from the type reference `Input: IEqualityComparer`. (HotChocolate.Types.ObjectType<ConferencePlanner.GraphQL.Data.AddSpeakerPayload>)
[<NoComparison; NoEquality>]
type AddSpeakerPayload = { Speaker: Speaker; Errors: Errors }

Junocaen avatar Nov 18 '21 23:11 Junocaen

I am interested on this. Send some love for F# :)

adelarsq avatar May 04 '22 19:05 adelarsq

It's there any work on this issue?

adelarsq avatar Jul 04 '22 01:07 adelarsq

Looks like there is none :(

AlexeyRaga avatar Jul 04 '22 05:07 AlexeyRaga

@AlexeyRaga we need a couple of F# natives to help us. If there is interest join us on slack and lets get started.

https://slack.chillicream.com

michaelstaib avatar Jul 07 '22 12:07 michaelstaib

Looks like the work is being done here: https://github.com/ChilliCream/graphql-platform/pull/5595

AlexeyRaga avatar Feb 28 '23 23:02 AlexeyRaga

@AlexeyRaga @michaelstaib Was this resolved by #5973?

glen-84 avatar Oct 20 '23 14:10 glen-84

@glen-84 Partially, yes. We should be OK now for the equality stuff, and we should be able to use Option.

More work is required to make it a nice F# experience, though, for example:

  • Support F# union types (map to GraphQL Union or/and Interface)
  • Support for F# nullability: every field of an F# record should be treated as non-nullable by default, unless it is of type Option<T>

When these two are done, I'll call it a decent F# support :)

AlexeyRaga avatar Oct 21 '23 02:10 AlexeyRaga

Thanks for the summary, though I'm asking more specifically about this particular issue – has the error in the OP been resolved? If so, I'll go ahead and close this issue. 🙂

glen-84 avatar Oct 21 '23 07:10 glen-84