ChilliCream / graphql-platform

Welcome to the home of the Hot Chocolate GraphQL server for .NET, the Strawberry Shake GraphQL client for .NET and Banana Cake Pop the awesome Monaco based GraphQL IDE.
https://chillicream.com
MIT License
5.25k stars 745 forks source link

JsonDocument problem when using [UseProjection] #5893

Open alandecastros opened 1 year ago

alandecastros commented 1 year ago

Is there an existing issue for this?

Product

Hot Chocolate

Describe the bug

I've tested on the latest version of HotChocolate (13.0.5). The JsonDocument works fine if I don't use the UseProjection attribute. IF I use it I get the following error:

{
  "errors": [
    {
      "message": "Unexpected Execution Error",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "testes"
      ],
      "extensions": {
        "message": "Type 'System.Text.Json.JsonDocument' does not have a default constructor (Parameter 'type')",
        "stackTrace": "   at System.Linq.Expressions.Expression.New(Type type)\r\n   at HotChocolate.Data.Projections.Expressions.QueryableProjectionScopeExtensions.CreateMemberInit(QueryableProjectionScope scope)\r\n   at HotChocolate.Data.Projections.Expressions.Handlers.QueryableProjectionFieldHandler.TryHandleLeave(QueryableProjectionContext context, ISelection selection, ISelectionVisitorAction& action)\r\n   at HotChocolate.Data.Projections.ProjectionVisitor`1.Leave(ISelection selection, TContext context)\r\n   at HotChocolate.Data.Projections.SelectionVisitor`1.Visit(ISelection selection, TContext context)\r\n   at HotChocolate.Data.Projections.SelectionVisitor`1.VisitObjectType(IOutputField field, ObjectType objectType, ISelection selection, TContext context)\r\n   at HotChocolate.Data.Projections.Expressions.QueryableProjectionVisitor.VisitObjectType(IOutputField field, ObjectType objectType, ISelection selection, QueryableProjectionContext context)\r\n   at HotChocolate.Data.Projections.SelectionVisitor`1.VisitChildren(IOutputField field, TContext context)\r\n   at HotChocolate.Data.Projections.SelectionVisitor`1.Visit(IOutputField field, TContext context)\r\n   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(IOutputField field, TContext context)\r\n   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(TContext context, ISelection selection)\r\n   at HotChocolate.Data.Projections.Expressions.QueryableProjectionProvider.<CreateApplicator>b__7_0[TEntityType](IResolverContext context, Object input)\r\n   at HotChocolate.Data.Projections.Expressions.QueryableProjectionProvider.<>c__DisplayClass4_0`1.<<CreateExecutor>g__ExecuteAsync|1>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at HotChocolate.Data.ToListMiddleware`1.InvokeAsync(IMiddlewareContext context)\r\n   at HotChocolate.Resolvers.Expressions.Parameters.ServiceHelper.<>c__DisplayClass7_3`1.<<UseResolverServiceInternal>b__5>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at HotChocolate.Resolvers.Expressions.Parameters.ServiceHelper.<>c__DisplayClass7_1`1.<<UseResolverServiceInternal>b__3>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)\r\n   at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)"
      }
    }
  ]
}

Steps to reproduce

https://github.com/glen-84/hc-5893 (.NET 8, HC 13.8.1)

  1. Create a query using [UseProjection]
  2. The model that is returned must have a JsonDocument attribute

Relevant log output

No response

Additional Context?

No response

Version

13.0.5

michaelstaib commented 1 year ago

Your issue needs a repro for us to help you.

alandecastros commented 1 year ago

Sure @michaelstaib ! Thanks for the fast response.

// Program.cs
builder.Services
    .AddGraphQLServer()
    .RegisterDbContext<AppDbContext>()
    .AddProjections()
    .AddQueryType<Query>();
// Query.cs
public class TestModel
{
    public string Codigo { get; set; } = Guid.NewGuid().ToString();
    public JsonDocument? Data { get; set; }
}

public class Query
{
    [UseProjection]
    public IQueryable<TestModel> GetTests(AppDbContext context) =>
        context.Set<TestModel>();

    public IQueryable<TestModel> GetTests2(AppDbContext context) =>
        context.Set<TestModel>();
}

This works

{
  tests2 {
    codigo
    data {
      rootElement
    }
  }
}

Correct output

{
  "data": {
    "tests2": [
      {
        "codigo": "0d14d05f-8bf2-42dc-a829-ccf36ea778cf",
        "data": {
          "rootElement": "{\"a\": 1}"
        }
      },
      {
        "codigo": "18ab0a12-cc63-45ba-a03b-5e16a1924c41",
        "data": {
          "rootElement": "{\"a\": 1}"
        }
      },
      {
        "codigo": "6ba887dc-85c7-4c45-bfb2-1dccba34e6f7",
        "data": {
          "rootElement": "{\"a\": 1}"
        }
      },
      {
        "codigo": "b55483da-2507-45dc-90fc-21cd6bfb0058",
        "data": {
          "rootElement": {
            "a": 223
          }
        }
      },
      {
        "codigo": "345545445345453",
        "data": null
      }
    ]
  }
}

This does not work

{
  tests {
    codigo
    data {
      rootElement
    }
  }
}

I get the following error:

{
  "errors": [
    {
      "message": "Unexpected Execution Error",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "tests"
      ],
      "extensions": {
        "message": "Type 'System.Text.Json.JsonDocument' does not have a default constructor (Parameter 'type')",
        "stackTrace": "   at System.Linq.Expressions.Expression.New(Type type)\r\n   at HotChocolate.Data.Projections.Expressions.QueryableProjectionScopeExtensions.CreateMemberInit(QueryableProjectionScope scope)\r\n   at HotChocolate.Data.Projections.Expressions.Handlers.QueryableProjectionFieldHandler.TryHandleLeave(QueryableProjectionContext context, ISelection selection, ISelectionVisitorAction& action)\r\n   at HotChocolate.Data.Projections.ProjectionVisitor`1.Leave(ISelection selection, TContext context)\r\n   at HotChocolate.Data.Projections.SelectionVisitor`1.Visit(ISelection selection, TContext context)\r\n   at HotChocolate.Data.Projections.SelectionVisitor`1.VisitObjectType(IOutputField field, ObjectType objectType, ISelection selection, TContext context)\r\n   at HotChocolate.Data.Projections.Expressions.QueryableProjectionVisitor.VisitObjectType(IOutputField field, ObjectType objectType, ISelection selection, QueryableProjectionContext context)\r\n   at HotChocolate.Data.Projections.SelectionVisitor`1.VisitChildren(IOutputField field, TContext context)\r\n   at HotChocolate.Data.Projections.SelectionVisitor`1.Visit(IOutputField field, TContext context)\r\n   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(IOutputField field, TContext context)\r\n   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(TContext context, ISelection selection)\r\n   at HotChocolate.Data.Projections.Expressions.QueryableProjectionProvider.<CreateApplicator>b__7_0[TEntityType](IResolverContext context, Object input)\r\n   at HotChocolate.Data.Projections.Expressions.QueryableProjectionProvider.<>c__DisplayClass4_0`1.<<CreateExecutor>g__ExecuteAsync|1>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at HotChocolate.Data.ToListMiddleware`1.InvokeAsync(IMiddlewareContext context)\r\n   at HotChocolate.Resolvers.Expressions.Parameters.ServiceHelper.<>c__DisplayClass7_3`1.<<UseResolverServiceInternal>b__5>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at HotChocolate.Resolvers.Expressions.Parameters.ServiceHelper.<>c__DisplayClass7_1`1.<<UseResolverServiceInternal>b__3>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)\r\n   at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)"
      }
    }
  ]
}
michaelstaib commented 1 year ago

Is ef core able to project json documents like that?

alandecastros commented 1 year ago

@michaelstaib I've just tested this out:

var q = context.Set<TestModel>().Select(x => 
            x.Data != null
                ? (JsonElement?)x.Data.RootElement
                : null
        );

And it works fine in EF Core.

glen-84 commented 10 months ago

@alandecastros Your repro is incomplete – which DB provider are you using?

alandecastros commented 10 months ago

@glen-84 I've just made a more complete repro using HotChocolate 13.8.1

//Program.cs

using HotChocolateBugRepro;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDbContext>(options =>
{
    options.UseNpgsql("User ID=postgres;Password=postgres;Host=localhost;Port=5432;Database=postgres;Pooling=true")
        .EnableSensitiveDataLogging();
});

builder.Services
    .AddGraphQLServer()
    .RegisterDbContext<AppDbContext>()
    .AddProjections()
    .AddQueryType<Query>();

// Add services to the container.

var app = builder.Build();

// Configure the HTTP request pipeline.

app.MapGraphQL();

app.Run();

public partial class Program { }
//AppDbContext.cs

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(Program).Assembly);
    }
}
// UsuarioModel.cs

public class UsuarioModel
{
    public string Codigo { get; set; } = Guid.NewGuid().ToString();
    public JsonDocument? Data { get; set; }
}

public class UsuarioModelConfig : IEntityTypeConfiguration<UsuarioModel>
{
    public void Configure(EntityTypeBuilder<UsuarioModel> builder)
    {
        builder.ToTable("usuario");

        builder.HasKey(x => x.Codigo);

        builder.Property(x => x.Codigo)
            .HasColumnName("usua_cd_usuario");

        builder.Property(x => x.Data)
            .HasColumnName("usua_json_data");
    }
}
// Query.cs

public class Query
{
    [UseProjection]
    public IQueryable<UsuarioModel> GetUsuarios(AppDbContext context) =>
    context.Set<UsuarioModel>();

    public IQueryable<UsuarioModel> GetUsuarios2(AppDbContext context) =>
        context.Set<UsuarioModel>();
}

Packages HotChocolate.AspNetCore 13.8.1 HotChocolate.Data.EntityFramework 13.8.1 Npgsql.EntityFrameworkCore.PostgreSQL 8.0.0

Queries & results

Query that works (without UseProjection)

{
    usuarios2 {
      codigo
      data {
        rootElement
      }
    }
}

///

{
  "data": {
    "usuarios2": [
      {
        "codigo": "08db053c-6de4-c931-c84b-d66385540000",
        "data": {
          "rootElement": {
            "a": 1,
            "b": "2",
            "c": {
              "d": 1,
              "e": "f"
            }
          }
        }
      }
    ]
}

Query that does not work (using UseProjection)

{
    usuarios {
      codigo
      data {
        rootElement
      }
    }
}

///

{
  "errors": [
    {
      "message": "Unexpected Execution Error",
      "locations": [
        {
          "line": 2,
          "column": 5
        }
      ],
      "path": [
        "usuarios"
      ],
      "extensions": {
        "message": "Type 'System.Text.Json.JsonDocument' does not have a default constructor (Parameter 'type')",
        "stackTrace": "   at System.Linq.Expressions.Expression.New(Type type)\r\n   at HotChocolate.Data.Projections.Expressions.QueryableProjectionScopeExtensions.CreateMemberInit(QueryableProjectionScope scope)\r\n   at HotChocolate.Data.Projections.Expressions.Handlers.QueryableProjectionFieldHandler.TryHandleLeave(QueryableProjectionContext context, ISelection selection, ISelectionVisitorAction& action)\r\n   at HotChocolate.Data.Projections.ProjectionVisitor`1.Leave(ISelection selection, TContext context)\r\n   at HotChocolate.Data.Projections.SelectionVisitor`1.Visit(ISelection selection, TContext context)\r\n   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(ISelection selection, TContext context)\r\n   at HotChocolate.Data.Projections.SelectionVisitor`1.VisitObjectType(IOutputField field, ObjectType objectType, ISelection selection, TContext context)\r\n   at HotChocolate.Data.Projections.Expressions.QueryableProjectionVisitor.VisitObjectType(IOutputField field, ObjectType objectType, ISelection selection, QueryableProjectionContext context)\r\n   at HotChocolate.Data.Projections.SelectionVisitor`1.VisitChildren(IOutputField field, TContext context)\r\n   at HotChocolate.Data.Projections.SelectionVisitor`1.Visit(IOutputField field, TContext context)\r\n   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(IOutputField field, TContext context)\r\n   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(TContext context, ISelection selection)\r\n   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(TContext context)\r\n   at HotChocolate.Data.Projections.Expressions.QueryableProjectionProvider.<CreateApplicator>b__7_0[TEntityType](IResolverContext context, Object input)\r\n   at HotChocolate.Data.Projections.Expressions.QueryableProjectionProvider.<>c__DisplayClass4_0`1.<<CreateExecutor>g__ExecuteAsync|1>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at HotChocolate.Data.ToListMiddleware`1.InvokeAsync(IMiddlewareContext context)\r\n   at HotChocolate.Resolvers.Expressions.Parameters.ServiceHelper.<>c__DisplayClass7_3`1.<<UseResolverServiceInternal>b__5>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at HotChocolate.Resolvers.Expressions.Parameters.ServiceHelper.<>c__DisplayClass7_1`1.<<UseResolverServiceInternal>b__3>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)\r\n   at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)"
      }
    }
  ]
}

When calling the second query HotChocolate doesn't even send an SQL to the database.

Regards

glen-84 commented 10 months ago

Thanks, I've added a link to a minimal reproduction under the Steps to reproduce section in the initial comment.