supabase-community / supabase-csharp

A C# Client library for Supabase
https://github.com/supabase-community/supabase-csharp/wiki
MIT License
483 stars 50 forks source link

.Net 7 - Primary Key Attribute is not a supported dictionary key using converter of type 'System.Text.Json.Serialization.Converters.SmallObjectWithParameterizedConstructorConverter' #72

Closed bitsnorbytes closed 1 year ago

bitsnorbytes commented 1 year ago

Bug report

Describe the bug

I am writing an API application which does CRUD operation on supabase database.

This is my Model class.

[Table("Genre")]
public class Genre : BaseModel
{
    [PrimaryKey("id",false)]
    public int Id { get; set; }

    [Column("name")]
    public string Name { get; set; } = string.Empty;

    [Column("genre_id")]
    public int GenreId { get; set; }
}

This is my controller class.

[ApiController]
[Route("[controller]")]
public class GenreController : ControllerBase
{
    private readonly GenreService _genreService;

    public GenreController(GenreService genreService) =>
        _genreService = genreService;

    [HttpGet(Name = "GetGenre")]
    public async Task<List<Genre>> Get() =>
        await _genreService.GetAsync();
}

This is the service that handles the Database from the controller.

public class GenreService
{
    private readonly Client _supabaseclient;
    public GenreService(Client supabaseclient)
    {
        _supabaseclient = supabaseclient;
    }

    public async Task<List<Genre>> GetAsync() {
        var result = await _supabaseclient.From<Genre>().Get();
        return result.Models;
    }
}

This is the where the service is passes via DI

var url = builder.Configuration["supabase:url"];
var key = builder.Configuration["supabase:key"];
var options = new SupabaseOptions
{
    AutoRefreshToken = true,
    AutoConnectRealtime = true,
};

builder.Services.AddSingleton(provider => new Supabase.Client(url, key, options));
builder.Services.AddSingleton<GenreService>();
var app = builder.Build();

When I run this the code and do a get , I get the following error.

fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
      An unhandled exception has occurred while executing the request.
      System.NotSupportedException: The type 'Postgrest.Attributes.PrimaryKeyAttribute' is not a supported dictionary key using converter of type 'System.Text.Json.Serialization.Converters.SmallObjectWithParameterizedConstructorConverter`5[Postgrest.Attributes.PrimaryKeyAttribute,System.String,System.Boolean,System.Object,System.Object]'. Path: $.PrimaryKey.
       ---> System.NotSupportedException: The type 'Postgrest.Attributes.PrimaryKeyAttribute' is not a supported dictionary key using converter of type 'System.Text.Json.Serialization.Converters.SmallObjectWithParameterizedConstructorConverter`5[Postgrest.Attributes.PrimaryKeyAttribute,System.String,System.Boolean,System.Object,System.Object]'.
         at System.Text.Json.ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(Type keyType, JsonConverter converter)
         at System.Text.Json.Serialization.JsonConverter`1.WriteAsPropertyName(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
         at System.Text.Json.Serialization.JsonConverter`1.WriteAsPropertyNameCore(Utf8JsonWriter writer, T value, JsonSerializerOptions options, Boolean isWritingExtensionDataProperty)
         at System.Text.Json.Serialization.Converters.DictionaryOfTKeyTValueConverter`3.OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonDictionaryConverter`3.OnTryWrite(Utf8JsonWriter writer, TDictionary dictionary, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer)
         at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.Converters.ListOfTConverter`2.OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryWrite(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         --- End of inner exception stack trace ---
         at System.Text.Json.ThrowHelper.ThrowNotSupportedException(WriteStack& state, NotSupportedException ex)
         at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonConverter`1.WriteCoreAsObject(Utf8JsonWriter writer, Object value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.JsonSerializer.WriteCore[TValue](Utf8JsonWriter writer, TValue& value, JsonTypeInfo jsonTypeInfo, WriteStack& state)
         at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
         at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
         at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
         at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
         at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
         at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
         at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

The below snippet from the error seems to be the problem.

The type 'Postgrest.Attributes.PrimaryKeyAttribute' is not a supported dictionary key using converter of type 'System.Text.Json.Serialization.Converters.SmallObjectWithParameterizedConstructorConverter

When I looked for a fix, I got results of creating a custom converter for JSON Serialization.

To Reproduce

Configure the code components as described above.

Expected behavior

To fetch the rows from the database and return a JSON of the results.

Screenshots

If applicable, add screenshots to help explain your problem.

System information

acupofjose commented 1 year ago

This looks like a dependency collision? These libraries make use of JSON.NET for serialization/deserialization. Is it possible that dependency isn't installed on your project? First time seeing this one!

bitsnorbytes commented 1 year ago

@acupofjose Yes, that seems to be the issue. I added installed this package via dotnet add dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson --version 7.0.5 followed by builder.Services.AddControllers().AddNewtonsoftJson(); in my Program.cs where are all services are initialized and it worked!

By default, the dotnet picks up System.Text.Json for deserializing / serializing if NewtonsoftJson is not installed. Is this mentioned in the document anywhere ? Can we incorporate this if not already ?

Thanks

acupofjose commented 1 year ago

Interesting! Do you think the README would have been the best place for you to find that info? Thanks for the help!

bitsnorbytes commented 1 year ago

Interesting! Do you think the README would have been the best place for you to find that info? Thanks for the help!

Yes definitely! Also, When building blazor wasm, one of the optimisation recommendations for AOT - Use System.Text.Json. Do you think this can be incorporated in future releases?

acupofjose commented 1 year ago

Agreed, it's an optimization that would be nice, but I there are several custom converters using JSON.NET at the moment that I would require some modifications to work with System.Text.Json. It seems that performance-wise it would be an optimization that would have some benefit to the libraries!

For now, it'll have to be a future item!

acupofjose commented 1 year ago

Readme is updated - thanks for the issue @bitsnorbytes!