Code Monkey home page Code Monkey logo

postgrest-csharp's Introduction


[Notice]: v4.0.0 renames this package from postgrest-csharp to Supabase.Postgrest. Which includes changing the namespace from Postgrest to Supabase.Postgrest.

Now supporting (many) LINQ expressions!

await client.Table<Movie>()
            .Select(x => new object[] { x.Id, x.Name, x.Tags, x.ReleaseDate })
            .Where(x => x.Tags.Contains("Action") || x.Tags.Contains("Adventure"))
            .Order(x => x.ReleaseDate, Ordering.Descending)
            .Get();

await client.Table<Movie>()
            .Set(x => x.WatchedAt, DateTime.Now)
            .Where(x => x.Id == "11111-22222-33333-44444")
            // Or .Filter(x => x.Id, Operator.Equals, "11111-22222-33333-44444")
            .Update();

Documentation can be found here.

Postgrest-csharp is written primarily as a helper library for supabase/supabase-csharp, however, it should be easy enough to use outside of the supabase ecosystem.

The bulk of this library is a translation and c-sharp-ification of the supabase/postgrest-js library.

Getting Started

Postgrest-csharp is heavily dependent on Models deriving from BaseModel. To interact with the API, one must have the associated model specified.

To use this library on the Supabase Hosted service but separately from the supabase-csharp, you'll need to specify your url and public key like so:

var auth = new Supabase.Gotrue.Client(new ClientOptions<Session>
{
    Url = "https://PROJECT_ID.supabase.co/auth/v1",
    Headers = new Dictionary<string, string>
    {
        { "apikey", SUPABASE_PUBLIC_KEY },
        { "Authorization", $"Bearer {SUPABASE_USER_TOKEN}" }
    }
})

Leverage Table,PrimaryKey, and Column attributes to specify names of classes/properties that are different from their C# Versions.

[Table("messages")]
public class Message : BaseModel
{
    [PrimaryKey("id")]
    public int Id { get; set; }

    [Column("username")]
    public string UserName { get; set; }

    [Column("channel_id")]
    public int ChannelId { get; set; }

    public override bool Equals(object obj)
    {
        return obj is Message message &&
                Id == message.Id;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Id);
    }
}

Utilizing the client is then just a matter of instantiating it and specifying the Model one is working with.

void Initialize()
{
    var client = new Client("http://localhost:3000");

    // Get All Messages
    var response = await client.Table<Message>().Get();
    List<Message> models = response.Models;

    // Insert
    var newMessage = new Message { UserName = "acupofjose", ChannelId = 1 };
    await client.Table<Message>().Insert();

    // Update
    var model = response.Models.First();
    model.UserName = "elrhomariyounes";
    await model.Update();

    // Delete
    await response.Models.Last().Delete();
}

Foreign Keys, Join Tables, and Relationships

The Postgrest server does introspection on relationships between tables and supports returning query data from tables with these included. Foreign key constrains are required for postgrest to detect these relationships.

This library implements the attribute, Reference to specify on a model when a relationship should be included in a query.

  • One-to-one Relationships: One-to-one relationships are detected if there’s an unique constraint on a foreign key.
  • One-to-many Relationships: The inverse one-to-many relationship between two tables is detected based on the foreign key reference.
  • Many-to-many Relationships: Many-to-many relationships are detected based on the join table. The join table must contain foreign keys to other two tables and they must be part of its composite key.

Given the following schema:

example schema

We can define the following models:

[Table("movie")]
public class Movie : BaseModel
{
    [PrimaryKey("id")]
    public int Id { get; set; }

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

    [Reference(typeof(Person))]
    public List<Person> Persons { get; set; }

    [Column("created_at")]
    public DateTime CreatedAt { get; set; }
}

[Table("person")]
public class Person : BaseModel
{
    [PrimaryKey("id")]
    public int Id { get; set; }

    [Column("first_name")]
    public string FirstName { get; set; }

    [Column("last_name")]
    public string LastName { get; set; }

    [Reference(typeof(Profile))]
    public Profile Profile { get; set; }

    [Column("created_at")]
    public DateTime CreatedAt { get; set; }
}

[Table("profile")]
public class Profile : BaseModel
{
    [Column("email")]
    public string Email { get; set; }
}

Note that each related model should inherit BaseModel and specify its Table and Column attributes as usual.

The Reference Attribute by default will include the referenced model in all GET queries on the table (this can be disabled in its constructor).

As such, a query on the Movie model (given the above) would return something like:

[
    {
        id: 1,
        created_at: "2022-08-20T00:29:45.400188",
        name: "Top Gun: Maverick",
        person: [
            {
                id: 1,
                created_at: "2022-08-20T00:30:02.120528",
                first_name: "Tom",
                last_name: "Cruise",
                profile: {
                    profile_id: 1,
                    email: "[email protected]",
                    created_at: "2022-08-20T00:30:33.72443"
                }
            },
            {
                id: 3,
                created_at: "2022-08-20T00:30:33.72443",
                first_name: "Bob",
                last_name: "Saggett",
                profile: {
                    profile_id: 3,
                    email: "[email protected]",
                    created_at: "2022-08-20T00:30:33.72443"
                }
            }
        ]
    },
    // ...
]

Circular References

Circular relations can be added between models, however, circular relations should only be parsed one level deep for models. For example, given the models here, a raw response would look like the following (note that the Person object returns the root Movie and the Person->Profile returns its root Person object).

If desired, this can be avoided by making specific join models that do not have the circular references.

[
  {
    "id": "68722a22-6a6b-4410-a955-b4eb8ca7953f",
    "created_at": "0001-01-01T05:51:00",
    "name": "Supabase in Action",
    "person": [
      {
        "id": "6aa849d8-dd09-4932-bc6f-6fe3b585e87f",
        "first_name": "John",
        "last_name": "Doe",
        "created_at": "0001-01-01T05:51:00",
        "movie": [
          {
            "id": "68722a22-6a6b-4410-a955-b4eb8ca7953f",
            "name": "Supabase in Action",
            "created_at": "0001-01-01T05:51:00"
          }
        ],
        "profile": {
          "person_id": "6aa849d8-dd09-4932-bc6f-6fe3b585e87f",
          "email": "[email protected]",
          "created_at": "0001-01-01T05:51:00",
          "person": {
            "id": "6aa849d8-dd09-4932-bc6f-6fe3b585e87f",
            "first_name": "John",
            "last_name": "Doe",
            "created_at": "0001-01-01T05:51:00"
          }
        }
      },
      {
        "id": "07abc67f-bf7d-4865-b2c0-76013dc2811f",
        "first_name": "Jane",
        "last_name": "Buck",
        "created_at": "0001-01-01T05:51:00",
        "movie": [
          {
            "id": "68722a22-6a6b-4410-a955-b4eb8ca7953f",
            "name": "Supabase in Action",
            "created_at": "0001-01-01T05:51:00"
          }
        ],
        "profile": {
          "person_id": "07abc67f-bf7d-4865-b2c0-76013dc2811f",
          "email": "[email protected]",
          "created_at": "0001-01-01T05:51:00",
          "person": {
            "id": "07abc67f-bf7d-4865-b2c0-76013dc2811f",
            "first_name": "Jane",
            "last_name": "Buck",
            "created_at": "0001-01-01T05:51:00"
          }
        }
      }
    ]
  }
]

Top Level Filtering

By default relations expect to be used as top level filters on a query. If following the models above, this would mean that a Movie with no Person relations on it would not return on a query unless the Relation has useInnerJoin set to false:

The following model would return any movie, even if there are no Person models associated with it:

[Table("movie")]
public class Movie : BaseModel
{
    [PrimaryKey("id")] 
    public string Id { get; set; }

    [Column("name")] 
    public string? Name { get; set; }

    [Reference(typeof(Person), useInnerJoin: false)]
    public List<Person> People { get; set; } = new();
}

Further Notes:

  • Postgrest does not support nested inserts or upserts. Relational keys on models will be ignored when attempting to insert or upsert on a root model.
  • The Relation attribute uses reflection to only select the attributes specified on the Class Model (i.e. the Profile model has a property only for email, only the property will be requested in the query).

Status

  • Connects to PostgREST Server
  • Authentication
  • Basic Query Features
    • CRUD
    • Single
    • Range (to & from)
    • Limit
    • Limit w/ Foreign Key
    • Offset
    • Offset w/ Foreign Key
  • Advanced Query Features
    • Filters
    • Ordering
  • Custom Serializers
    • Postgres Range
      • int4range, int8range
      • numrange
      • tsrange, tstzrange, daterange
  • Models
    • BaseModel to derive from
    • Coercion of data into Models
  • Unit Testing
  • Nuget Package and Release

Package made possible through the efforts of:

acupofjose elrhomariyounes

Contributing

We are more than happy to have contributions! Please submit a PR.

postgrest-csharp's People

Contributors

acupofjose avatar corrideat avatar dependabot[bot] avatar devpikachu avatar elrhomariyounes avatar fantasyteddy avatar hunsra avatar jonathanvelkeneers avatar kolosovpetro avatar sameeromar avatar wiverson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

postgrest-csharp's Issues

Internal usage classes

Chore

Describe the chore

Hello, I was thinking if we should change the access modifier of some classes to internal like MapToAttribute, RangeConverter, CustomContratResolver and EnumExtensions we don't want the user to interact with them.

Any thoughts @acupofjose

Ignoring `null` in a JSONB column using `record`

Feature request

Is your feature request related to a problem? Please describe.

I have a JSONB column that I'm writing to, defined like this:

[Table("games")]
public class Game : BaseModel
{
    [PrimaryKey]
    public string? id { get; set; }

    [Column]
    public GameState? state { get; set; }
}

public record GameState(Boolean? some_value)

// ... elsewhere ...

var data = new GameState(null)l;

await client
    .From<Game>()
    .Where(game => game.id == gameId)
    .Set(game => game.state!, data)
    .Update();

This will write the following JSON to the JSONB column:

{ "some_value": null }

Describe the solution you'd like

This is mostly fine, but it would be nice to be able to ignore null values when serializing to JSONB.

Describe alternatives you've considered

I tried to define the column like so:

    [Column("state", NullValueHandling = NullValueHandling.Ignore)]
    public GameState? state { get; set; }

But that doesn't have an effect. I assume because it applies to the entire column, and not the individual fields in the serialized JSON value.

Get remote db model based on local model

Feature request

I'm not sure this is already possible (easily) and there's no discussions tab for this repo sadly so I figured I'd open an issue.

Basically I'm looking for a way to get the Model that's stored in postgres based on a model that already exists (read from local json or whatever). There's PrimaryKey annotations which I thought would be used for this sort of usecase but can't figure out how.

Describe the solution you'd like

MyModel localModel = ; // read from somewhere

MyModel remoteModel = client.Table<MyModel>.GetByPkey(localModel);

something along those lines would be nice

Update method - no Route matched with those values

Bug report

Describe the bug

I followed the instruction of the docs. When I try to use the Update method I get this error message:

{Postgrest.RequestException: no Route matched with those values
   at Postgrest.Helpers.MakeRequest(ClientOptions clientOptions, HttpMethod method, String url, JsonSerializerSettings serializerSettings, Object data, Dictionary`2 headers, CancellationToken cancellationToken)

I have no issue with inserting or selecting. Also turned of my policies to test it.

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

ModeledResponse<Product> response = await Client.From<Product>().Get();
Product model = response.Models.FirstOrDefault();
model.Note = "Test";
ModeledResponse<Product> result = await model.Update<Product>();

Expected behavior

Value should have changed.

System information

  • OS: Windows / Android
  • Executing with .Net Maui (.Net 6)
  • Version of supabase-csharp: 0.6.2
  • Version of postgrest-csharp: 3.0.4

Using LINQ expression to handle DateTime comparisons produces inconsistent results.

Referenced Lines:

/// <summary>
/// An instantiated class parser (i.e. x => x.CreatedAt &lt;= new DateTime(2022, 08, 20) &lt;- where `new DateTime(...)` is an instantiated expression.
/// </summary>
/// <param name="column"></param>
/// <param name="op"></param>
/// <param name="newExpression"></param>
private void HandleNewExpression(string column, Operator op, NewExpression newExpression)
{
var argumentValues = new List<object>();
foreach (var argument in newExpression.Arguments)
{
var lambda = Expression.Lambda(argument);
var func = lambda.Compile();
argumentValues.Add(func.DynamicInvoke());
}
var constructor = newExpression.Constructor;
var instance = constructor.Invoke(argumentValues.ToArray());
if (instance is DateTime dateTime)
{
Filter = new QueryFilter(column, op, dateTime.ToUniversalTime());
}
else if (instance is Guid guid)
{
Filter = new QueryFilter(column, op, guid.ToString());
}
else if (instance.GetType().IsEnum)
{
Filter = new QueryFilter(column, op, instance);
}
}

Issue Report:
Re: supabase-community/supabase-csharp#121

Reproducing this issue [from @aquatyk here]

DateTime day1 = DateTime.Now.AddDays(-1);
string day = day1.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);

// Produces incorrect result.
var result = await _client.From<Item>().Where(x => x.Created_at >= day1).Get();

// Produces correct result.
var result = await _client.From<Item>().Filter("created_at", Operator.GreaterThanOrEqual, day).Get();

[Bug] "infinity" date fails to deserialize

Inifinity is insterted when using npgsql https://www.npgsql.org/doc/types/datetime.html , then postgrest fails to desterilize it

System.FormatException : String '-infinity' was not recognized as a valid DateTime.
   at Postgrest.Converters.DateTimeConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
   at Postgrest.Responses.ModeledResponse`1..ctor(BaseResponse baseResponse, JsonSerializerSettings serializerSettings, Boolean shouldParse)
   at Postgrest.Helpers.MakeRequest[T](HttpMethod method, String url, JsonSerializerSettings serializerSettings, Object data, Dictionary`2 headers)

Calls to `Insert` and `Update` fail when a `Reference` is specified on a Model

Bug report

Describe the bug

Trying to insert new rows into a table with a Reference (many-to-one) relationship throws a PostgresException.

PostgrestException: {"code":"PGRST204","details":null,"hint":null,"message":"Column '[table]' of relation '[table]' does not exist"}

To Reproduce

Take the following two simple models:

    [Table("people")]
    public class Person : BaseModel
    {
        [PrimaryKey("id")]
        public string id { get; set; }
        public string first_name { get; set; }
        public string last_name { get; set; }

        [Reference(typeof(Quote))]
        public List<Quote> quotes { get; set; } = new();
    }

    [Table("quotes")]
    public class Quote : BaseModel
    {
        [PrimaryKey("id")]
        public string id { get; set; }
        [Column("quote")]
        public string quote { get; set; }
        [Column("people_id")]
        public string people_id { get; set; }
        [Column("created_at")]
        public DateTime created_at { get; set; }
    }

Now try to insert data:

await supabaseClient.From<Person>().Insert(new Person { first_name = "Leeroy", last_name = "Jenkins"});

Results in: PostgrestException:
{"code":"PGRST204","details":null,"hint":null,"message":"Column 'quotes' of relation 'people' does not exist"}

Expected behavior

I would expect a row to be inserted as normal.

System information

  • OS: Windows, Unity
  • Version of supabase-core: 0.0.3
  • Version of supabase-csharp: 0.14.0

Additional context

Without the reference, people data inserts fine! It's only after adding the reference that the exception arises. Interestingly, I can't find any examples of one-to-many relationships here. Perhaps it isn't supported?

Here is the schema:

CREATE TABLE IF NOT EXISTS sandbox.people
(
    id uuid NOT NULL DEFAULT gen_random_uuid(),
    first_name character varying(255) COLLATE pg_catalog."default",
    last_name character varying(255) COLLATE pg_catalog."default",
    CONSTRAINT people_pkey PRIMARY KEY (id)
)

CREATE TABLE IF NOT EXISTS sandbox.quotes
(
    id uuid NOT NULL DEFAULT gen_random_uuid(),
    quote character varying(255) COLLATE pg_catalog."default" NOT NULL,
    created_at timestamp with time zone NOT NULL DEFAULT now(),
    people_id uuid NOT NULL,
    CONSTRAINT quotes_pkey PRIMARY KEY (id),
    CONSTRAINT quotes_people_id_fkey FOREIGN KEY (people_id)
        REFERENCES sandbox.people (id) MATCH SIMPLE
        ON UPDATE CASCADE
        ON DELETE CASCADE
)

Bulk Insert

Feature request

Is your feature request related to a problem? Please describe.

  • Perform a bulk insert

Describe the solution you'd like

  • Overload the Insert method to take ICollection to perform a bulk insert on the Table T

Idea

  1. Overloading Insert method
public Task<ModeledResponse<T>> Insert(ICollection<T> models, InsertOptions options = null)
  1. Perform request
  • Change the signature of the PrepareRequestData to return object instead of a dictionary and check if the data parameter is it an object or a collection to avoid the JsonSerializer Exception and serialize properly the collection if it's the case or serialize to dictionary<string,string> if not
public object PrepareRequestData(object data)
  • Change Helpers.MakeRequest to expect the data to be object instead of a dictionary the PrepareRequestData will return an object
public static async Task<ModeledResponse<T>> MakeRequest<T>(HttpMethod method, string url, object data = null, Dictionary<string, string> headers = null)
  • Add an if case in Helpers.MakeRequest to check data object type if dictionary, then cast it and build the Uri for the Request
    to preserve the same behavior

I would love to work on it and if there is a better approach let me know. Thank you very much

Add overload with CancellationToken parameter

Hello!

Currently the Table object doesn't support CancellationTokens when making requests.

This should be an easy enhancement, probably added to this line.

This is particularly useful when the library is embedded in an API or WebServer, where requests might get cancelled prematurely. Enabling support for CancellationTokens can save resources.

XML documentation comments

Feature request

Describe the solution you'd like

Hello, I was wondering if we can add XML documentation comments for methods and object properties to show up in the intellisense ?

Thank you.

Filter method throws exception with FullTextSearch criteria

Bug report

Describe the bug

Hello, when trying to filter using a full text search criteria with one the operators : fts, plfts, phfts, wfts an exception is thrown by the filter method.

System.Exception: Unknown criterion type, is it of type `string`, `List`, `Dictionary<string, object>`, or `Range`?
  Stack Trace: 
    Table`1.Filter(String columnName, Operator op, Object criterion) line 129

The issue is the Filter method is not expecting the criteria to be a FullTextSearchConfig object. I solved the issue by adding a new case for it. Please check PR

To Reproduce

            var client = Client.Instance.Initialize(baseUrl, new ClientAuthorization(AuthorizationType.Open, null));

            var config = new FullTextSearchConfig("'fat' & 'cat'", "english");
            var filteredResponse = await client.Table<User>().Filter("catchphrase", Operator.FTS, config).Get();

Expected behavior

  • Get a filtered response

System information

-OS: [Windows 10 Pro 1909]
-.Net core 3.1.x
-Visual Studio 16.8
-Version of supabase-postgrest-csharp: master branch of current repo

Postgrest - Where filter with nullable field doesn't work

Bug report

Describe the bug

A call to the database using Where or Filter method with a nullable field throws the following exception:

**System.ArgumentException: Unable to parse the supplied predicate, did you return a predicate where each left hand of the condition is a Model property?**
   at Postgrest.Table`1[[MaterialeShop.Admin.Src.Dtos.ListaItem, MaterialeShop.Admin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].Where(Expression`1 predicate)

If the field is not nullable it works fine.

To Reproduce

Example:

await client
            .From<ListaItem>()
            // .Filter(nameof(ListaItem.ListaId), Postgrest.Constants.Operator.Equals, id)
            // .Where(x => x.ListaId == id)
            // .Order(nameof(ListaItem.CreatedAt), Postgrest.Constants.Ordering.Ascending)
            // .Where(x => x.SoftDeleted == false)
            .Where(x => x.CreatedAt == DateTime.Now)
            // .Order(x => x.CreatedAt, Postgrest.Constants.Ordering.Ascending)
            .Get();

CreatedAt is defined as:

[Column("CreatedAt")]
public DateTime? CreatedAt { get; set; } = DateTime.Now;

Expected behavior

Filter as it already works with a not nullable field.

Project information

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Blazored.LocalStorage" Version="4.3.0" />
    <PackageReference Include="gotrue-csharp" Version="3.0.4" />
    <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="7.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.2" PrivateAssets="all" />
    <PackageReference Include="MudBlazor" Version="6.1.2" />
    <PackageReference Include="supabase-csharp" Version="0.7.1" />
    <!-- <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> -->
  </ItemGroup>

  <ItemGroup>
    <ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />
  </ItemGroup>

</Project>

About Insert Method

Bug report

About Insert Method

Describe the bug

Get a Exception when at use.

##Error
Unhandled exception. Postgrest.RequestException: null value in column "id" of relation "favorites" violates not-null constraint
at Postgrest.Helpers.MakeRequest(ClientOptions clientOptions, HttpMethod method, String url, JsonSerializerSettings serializerSettings, Object data, Dictionary2 headers, CancellationToken cancellationToken) at Postgrest.Helpers.MakeRequest[T](ClientOptions clientOptions, HttpMethod method, String url, JsonSerializerSettings serializerSettings, Object data, Dictionary2 headers, Func`1 getHeaders, CancellationToken cancellationToken)
at SupabaseExample.Program.Main(String[] args) in D:\Development\Example\Supabase SRC\supabase-csharp-0.6.2\Examples\SupabaseExample\Program.cs:line 39
at SupabaseExample.Program.

(String[] args)

System information

  • OS: [Windows]
  • Version of supabase-js: [e.g. 6.0.2]

Additional context

I try change the value, always is null.

Contains filter @>

Bug report

Describe the bug

Hello, as @acupofjose mentioned in #16 postgrest seems to not recognize the operator. I went to read the docs and I found out that this operator is used in this cases

ARRAY[1,4,3] @> ARRAY[3,1,3]
int4range(2,4) @> int4range(2,3)
circle '<(0,0),2>' @> point '(1,1)'
'{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb

So I give a first try with ranges. I I encountered several issues.

  1. An exceptionthrown in the QueryFilter constructor because not allowing a range criteria and contains filter operator, I solved it by adding the operator to the switch case
  2. After solving 1. a request exception by Postgrest the url was not formed correctly after debugging the range criteria was not added by the PrepareFilter method in Table class. I fixed by correctly formatting the url if a range criteria is the case please check PR ☺️

I would love to check the others cases.

To Reproduce

var filteredResponse = await client.Table<User>().Filter("age_range", Operator.Contains, new Range(1,2)).Get(); 

Expected behavior

  • Get a filtered response all the users with age range in the criteria

System information

-OS: [Windows 10 Pro 1909]
-.Net core 3.1.x
-Visual Studio 16.8
-Version of supabase-postgrest-csharp: master branch of current repo

Stored procedure

Feature request

Is your feature request related to a problem? Please describe.

Hello, I saw in the PostgREST docs that we can call a stored procedure, I checked the postgrest-js repo and it's implemented.

I would love to work on it ☺️

Describe the solution you'd like

  • Call a stored procedure
public Task<BaseResponse> Rpc(string procedureName, Dictionary<string, object> parameters);

Questions

  1. Should the method be called from Table object or Client ?

Leverage LINQ while selecting columns from Table

Feature request

Is your feature request related to a problem? Please describe.

Yes, Currently while selecting columns from database we have to pass column names as magic string comma sperated in Select method.
This is error prone, even if we miss a letter unwillingly.

Describe the solution you'd like

We can leverage the linq while selecting columns (check below code for refrence)

var user = await client.Table<User>().Select(x => new { x.Username,x.Data }).Get();

Even if our column name changes in db table, we will have to just update the value in Column Attribute.
It will also be provide type safety.

Describe alternatives you've considered

No alternative

Additional context


Using multiple `.Order()` methods when querying doesn't work as expected.

Bug report

Describe the bug

Using more than one .Order() method in a query doesn't seem to work. Only the first .Order() method is honored.

To Reproduce

Given a model with the following structure (and a corresponding table in a Supabase project):

[Table("Contacts")]
public class Contact : BaseModel
{
	[Column("first_name")]
	public string FirstName { get; set; }

	[Column("middle_name")]
	public string MiddleName { get; set; }

	[Column("last_name")]
	public string LastName { get; set; }
}

Making the following query:

response = await client.From<Contact>()
            .Order("last_name", Ordering.Ascending)
            .Order("first_name", Ordering.Ascending)
            .Order("middle_name", Ordering.Ascending)
            .Get();

Results in a list of Contact models that are only ordered by the "last_name" column.

Expected behavior

The resulting list should be ordered by the "last_name" column, then by the "first_name" column, then by the "middle_name" column.

Screenshots

The Supabase Edge API Network Log seems to indicate the proper ?order= query parameter is being created, but it is not returning records int he correct order, as described above:

"search": "?order=last_name.asc.nullsfirst&order=first_name.asc.nullsfirst&order=middle_name.asc.nullsfirst",

System information

  • OS: iOS 17.0 simulator (also happens on Windows 11)
  • Version of supabase-csharp: 0.14.0

Additional context

This is happening in a .NET MAUI application targeting iOS, Android, Windows, and Mac Catalyst.

Not supplying value to column auto inserts 0 values

Bug Report

Describe the bug

Not supplying a value to a column that is defined in a table results in the value being set to 0.
I can see this being some kind of error prevention mechanism but columns like "created_at" become pretty hard to handle due to this behaviour.
Given that both the zero value as well as column name have to be sent to the supabase instance also adds quite a bit of network overhead that could be avoided.

Reproduction

Add a column to a table, when creating the object don't supply an object to the column and upsert the entry.

System Info

  • OS: Windows 10 Pro
  • .NET 6
  • Visual Studio 17.0.4
  • Versions: supabase: 0.2.11; postgrest: 2.0.5

Contains and Contained operators

Feature request

Is your feature request related to a problem? Please describe.

Hello, the contains (@>) and contained (<@) operators only supports for now Range types. In the docs they can be used also with Array types, Geometric types and Json

ARRAY[1,4,3] @> ARRAY[3,1,3]
ARRAY[2,2,7] <@ ARRAY[1,7,4,2,6]
circle '<(0,0),2>' @> point '(1,1)'
point '(1,1)' <@ circle '<(0,0),2>'
'{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb
'{"b":2}'::jsonb <@ '{"a":1, "b":2}'::jsonb

I think when using List the Postgrest the request failed, doesn't recognize the operator as @acupofjose mentioned in #15

Describe the solution you'd like

If you can support those types specially arrays. Thank you very much.

Not filter method signature

Feature request

Is your feature request related to a problem? Please describe.

Hello, I was wondering for the Not filter if we can pass the QueryFilter parameters directly to the Not method instead of passing a QueryFilter object.

The current behavior we should do like this :

var filter = new QueryFilter("username", Constants.Operator.Equals, "supabot");
var response = await client.Table<User>().Not(filter).Get();

Describe the solution you'd like

To do the same thing as postgrest-js not filter.

var response = await client.Table<User>().Not("status",Constants.Operators.Equals,"ONLINE").Get();

Thank you.

Filter method compilation error

Bug report

Describe the bug

Hello,

When trying to perform a filter request to check if a column value is null

await client.Table<User>().Filter("data", Operator.Equals, null).Get();

A compilation error due to an ambiguity between Filter with string third parameter and List of objects overload

public Table<T> Filter(string columnName, Operator op, List<object> criteria)
public Table<T> Filter(string columnName, Operator op, string criteria)

To Reproduce

  1. Try to perform an Filter request to check if a column is null
await client.Table<User>().Filter("data", Operator.Equals, null).Get();

Update a record not working

Bug report

Describe the bug

  • Hello, while trying to update a record an Exception of type NullReferenceException.
    After debugging the exception is thrown when calling the Update method in Builder class while initializing the QueryFilter object whit a null criteria the Helper method GetPropertyValue doesn't retrieve the value of the primary key property it returns null.
    I figured it out instead of using the Helper method I used the PrimaryKeyValue Property of the model and worked perfectly. I submitted a PR ☺️ for this case if is it ok.

  • Second thing is when trying to update a non-existing user another exception is thrown of type JsonSerializationException. The issue is the Postgrest returns an empty array "[]" when trying to update a non-existing record and the Helper method MakeRequest when not a success status code is returned tries to deserialize the response content to an ErrorResponse object, an empty array cannot be deserialized to ErrorResponse.

Thank you.

To Reproduce

  1. Retrieve a user
var user= await client.Builder<User>().Filter("username", Postgrest.Constants.Operator.Equals, "supabot").Single();
  1. Update if not null
user.Status = "OFFLINE";
await user.Update<User>();
  1. To reproduce second case (Not Found user)
var user = new User
{
   Username = "ghost",
   Status = "ONLINE"
};
await client.Builder<User>().Update(user);

Expected behavior

  1. User updated if not null
  2. When trying to update a not existing user a RequestException to be thrown instead of JsonSerializationException

System information

  • OS: [Windows 10 Pro 1909]
  • .Net core 3.1.x
  • Visual Studio 16.8
  • Version of supabase-postgrest-csharp: master branch of current repo

QueryOptions Returning prop crashes json parser

Bug Report

Describe the bug

QueryOptions Returning prop makes the response crash

Reproduction

Setting the Returning prop in QueryOptions to "QueryOptions.ReturnType.Minimal" causes an exception while parsing the db response.

Screenshots:
image
image

System Info

  • OS: Windows 10 Pro
  • .NET 6
  • Visual Studio 17.0.4
  • Versions: supabase: 0.2.11; postgrest: 2.0.4

Bulk Insert and Upsert don't work

Bug Report

Describe the bug

Bulk inserts and upserts currently don't seem to work.
The column thats labeled as primary key is inserted successfully, all other columns get set to zero values.

Reproduction

Unfortunately I'm not sure how to setup the tests locally so I couldn't go and debug myself.
Though I setup a minimal example with two columns in supabase and it did not work.
Screenshot:
image

System Info

  • OS: Windows 10 Pro
  • .NET 6
  • Visual Studio 17.0.4
  • Versions: supabase: 0.2.11; postgrest: 2.0.3

Get not working when PK is Text Type

Create a table with a PK of type TEXT (weird I know, but It is based on a legacy app)
Create a class which implements BaseModel class.
Try to fetch data and you will see no results.

System information

.NET CORE 7
supabase-csharp: 0.13.1

Additional context

Add any other context about the problem here.

Can't use "Contains" collection method to filter tables

I'm trying to filter a database with a method like this:

var test = await _client
            .From<TestTable>()
            .Where(u => listOfInts.Contains(u.fieldOfTypeInt))
            .Get();

However, this give me an error like this:

System.InvalidOperationException: variable 'u' of type 'Database.Models.TestTable' referenced from scope '', but it is not defined
   at System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpression node, VariableStorageKind storage)
   at System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterExpression node)
   at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(ReadOnlyCollection`1 nodes)
   at System.Linq.Expressions.Compiler.VariableBinder.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression lambda)
   at Postgrest.Linq.WhereExpressionVisitor.GetArgumentValues(MethodCallExpression methodCall)
   at Postgrest.Linq.WhereExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
   at Postgrest.Table`1.Where(Expression`1 predicate)
   ...

If i try a filter method that does not use the Contains collection method, there is no problem. Like:

var test = _client
            .From<TestTable>()
            .Where(u=> user.fieldOfTypeInt == 10);

(this works)

X-Client-Info header

we're rolling out a header similar to Stripe's App-Data in all of the client libs so issues can be debugged more easily between the client and the backend

the javascript libs are done already: supabase/supabase-js#238

the format is: X-Client-Info: supabase-js/1.11.0

for client libs that wrap others (like how supabase-js wraps gotrue-js) we allow the wrapper lib to overwrite the wrapped lib's header (given that we can infer the gotrue-js version based on which supabase-js was used to make the call)

any help with rolling this out here would be incredible

Refactoring utils methods

Chore

Describe the chore

Hello @acupofjose, Your thoughts about moving utils methods in the Table class like (PrepareFilter, PrepareData, GenerateUrl)
to static methods in Helpers class and make it internal?

Specify top-level filtering

Hi -- the new changes in the latest release that enable resource embedding have been working great, thanks again!

I had one request though. It seems that by default, the postgrest-csharp library is adding top-level row filtering by inserting !inner in the query a la https://postgrest.org/en/stable/api.html#embedding-with-top-level-filtering. This ignores any top level rows for which there are no nested resources. It would be great to have more fine grained control over this. In my use case I would like all top level rows returned even if the nested resources contains no rows. Is there currently a way to control this, and if not could it be added?

Conditionally ignore column on insert or update?

Hi,

Thanks for the great library, I'm enjoying using it.
I'm struggling with something that seems like it should have an obvious answer.
When I define a model describing a one-to-many relationship through a foreign-key, in the parent class I include a column definition to catch all the children items in a List structure. So imagine backing models like this:

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

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

    [Column("created_at")]
    public DateTime CreatedAt { get; set; }

    [Column("intervention")]
    public List<Intervention> Interventions { get; set; }
}

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

    [Column("description")]
    public string Description { get; set; }

    [Column("created_at")]
    public DateTime CreatedAt { get; set; }
}

Querying the user table using :

var users = await client.Table<User>() .Select("*,intervention(*)") .Get();

This works great with anticipated automatic population of the User's "Interventions".

The problem is that if I try to Insert a new User or Update an existing User with these models it complains that there is no column matching "intervention".

So I'm wondering if it's possible to have something similar to how the PrimaryKey attribute works, but broaden it so that any Column attribute includes the option to specify shouldInsert and shouldUpdate boolean flags. If those are set to false the column is ignored when writing to the DB, but included in the model during reads.

There may also be a better solution that I'm missing entirely. Thoughts?

Random issues when sending data to API

Bug report

Describe the bug

In a random manner, POST calls seems to fall in error :

With :

await SupabaseManager.app.From<SchemaProgression>().Upsert(_progression);

I get :
image

Full stacktrace :

NullReferenceException: Object reference not set to an instance of an object
Postgrest.RequestException..ctor (System.Net.Http.HttpResponseMessage response, Postgrest.Responses.ErrorResponse error) (at Assets/Plugins/Supabase/Postgrest/Helpers.cs:172)
Postgrest.Helpers.MakeRequest (Postgrest.ClientOptions clientOptions, System.Net.Http.HttpMethod method, System.String url, Newtonsoft.Json.JsonSerializerSettings serializerSettings, System.Object data, System.Collections.Generic.Dictionary`2[TKey,TValue] headers, System.Threading.CancellationToken cancellationToken) (at Assets/Plugins/Supabase/Postgrest/Helpers.cs:118)
Postgrest.Helpers.MakeRequest[T] (Postgrest.ClientOptions clientOptions, System.Net.Http.HttpMethod method, System.String url, Newtonsoft.Json.JsonSerializerSettings serializerSettings, System.Object data, System.Collections.Generic.Dictionary`2[TKey,TValue] headers, System.Func`1[TResult] getHeaders, System.Threading.CancellationToken cancellationToken) (at Assets/Plugins/Supabase/Postgrest/Helpers.cs:46)
NodeProgression.DefineSaveLogicAsync () (at Assets/Scripts/Supabase/Schemas/SchemaProgression.cs:99)
UnityEngine.Debug:LogException(Exception)
SupabaseNode`1:HandleErrors(Task) (at Assets/Scripts/Supabase/Schemas/Schema.cs:85)
Firebase.Extensions.<>c__DisplayClass2_1`1:<ContinueWithOnMainThread>b__1() (at /home/runner/work/firebase-unity-sdk/firebase-unity-sdk/app/task_extension/TaskExtension.cs:87)
Firebase.<>c__DisplayClass5_0`1:<RunAsync>b__0() (at /home/runner/work/firebase-unity-sdk/firebase-unity-sdk/app/platform/Dispatcher.cs:77)
Firebase.ExceptionAggregator:Wrap(Action) (at /home/runner/work/firebase-unity-sdk/firebase-unity-sdk/app/platform/ExceptionAggregator.cs:112)
Firebase.Dispatcher:PollJobs() (at /home/runner/work/firebase-unity-sdk/firebase-unity-sdk/app/platform/Dispatcher.cs:123)
Firebase.Platform.FirebaseHandler:Update() (at /home/runner/work/firebase-unity-sdk/firebase-unity-sdk/app/platform/Unity/FirebaseHandler.cs:207)
Firebase.Platform.FirebaseMonoBehaviour:Update() (at /home/runner/work/firebase-unity-sdk/firebase-unity-sdk/app/platform/Unity/FirebaseMonoBehaviour.cs:45)

image

Seems to also happen with DELETEs

To Reproduce

It really happens randomly, I can't reproduce it on purpose

Expected behavior

No 500 code returned from server

System information

  • OS: Windows

Range request not working

Bug report

Describe the bug

Hello, while trying to perform a Range request an exception is thrown as described in the StackTrace below

StackTrace" at 
System.Net.Http.Headers.HttpHeaderParser.ParseValue(String value, Object storeValue, Int32& index)\r\n   at
System.Net.Http.Headers.HttpHeaders.ParseAndAddValue(HeaderDescriptor descriptor, HeaderStoreItemInfo info, String value)\r\n   at System.Net.Http.Headers.HttpHeaders.Add(String name, String value)\r\n   at 
Postgrest.Helpers.<MakeRequest>d__5.MoveNext() 

After debugging I found that issue is when performing a Range request with from parameter only or both from and to the headers expected by Postgrest are :

  • Range-Unit : items
  • Content-Range : 1-3 (we suppose 1 is the from parameter and 3 is to)

The second header will throw the exception when trying to add it to the HttpRequestMessage.Headers because is not a valid one
Exception line

To Reproduce

  1. Perform a Range request
var response = await client.Builder<User>().Range(2).Get();

Expected behavior

  • Get a response with the requested size

System information

-OS: [Windows 10 Pro 1909]
-.Net core 3.1.x
-Visual Studio 16.8
-Version of supabase-postgrest-csharp: master branch of current repo

Can't use `Table<TModel>.Where(Expression<Func<TModel, bool>> predicate)` with more than 2 or'ed conditions in the predicate expression

Bug report

Describe the bug

An exception occurs in Table<TModel> Where(Expression<Func<TModel, bool>> predicate) if the predicate includes more than 2 or'ed conditions.

To Reproduce

Given the following model (and corresponding table in a Supabase project):

[Table("Contacts")]
public class Contact : BaseModel
{
	[Column("first_name")]
	public string FirstName { get; set; }

	[Column("middle_name")]
	public string MiddleName { get; set; }

	[Column("last_name")]
	public string LastName { get; set; }
}

Use of the following code:

string filter = "Ran";
var response = await supabase.From<Contact>().Where(c => c.FirstName.Contains(filter) || c.MiddleName.Contains(filter) || c.LastName.Contains(filter)).Get();

Results in the following PostgrestException exception:

Message:
{"code":"PGRST100","details":"unexpected \".\" expecting \"(\"","hint":null,"message":"\"failed to parse logic tree ((or.(first_name.like.*Ran*,middle_name.like.*Ran*),last_name.like.*Ran*))\" (line 1, column 6)"}

Source: "Supabase.Postgrest"

StackTrace:
   at Postgrest.Helpers.<MakeRequest>d__3.MoveNext()
   at Postgrest.Helpers.<MakeRequest>d__2`1.MoveNext()
   at Concord.Services.SupabaseService.<GetContactsByName>d__33.MoveNext() in C:\Repos\Concord\Concord\Services\Supabase.cs:line 328
   at Concord.ViewModels.MainViewModel.<SearchContacts>d__40.MoveNext() in D:\Repos\Concord\Concord\ViewModels\MainViewModel.cs:line 272

If the code is modified to use fewer or'ed conditions, such as in:

string filter = "Ran";
var response = await supabase.From<Contact>().Where(c => c.FirstName.Contains(filter) || c.LastName.Contains(filter)).Get();

The filter succeeds and behaves as expected.

It appears from the exception message that the underlying Postgrest code is improperly formatting the logic tree:
((or.(first_name.like.*Ran*,middle_name.like.*Ran*),last_name.like.*Ran*))

Perhaps it should have been:
((or.(first_name.like.*Ran*,middle_name.like.*Ran*,last_name.like.*Ran*)))

Expected behavior

The query should successfully select records whose first_name, middle_name, or last_name fields contain the specified filter string.

System information

  • OS: iOS 17.0 simulator (also happens on Windows 11)
  • Version of supabase-csharp: 0.14.0

Additional context

This is happening in a .NET MAUI application targeting iOS, Android, Windows, and mac Catalyst.

URI too long when filtering with big lists

Bug report

Describe the bug

Had a headache through this one. Tried to delete entries that no longer exists :
Made a list of Guid of [still existing] entities, and used a filter as following :

        // Deletion of no longer exists
        List<object> entityIdList = _careItemDataList.Select(x => (object)x.EntityId.ToString()).ToList();
        QueryFilter exists = new("entity_id", Postgrest.Constants.Operator.In, entityIdList);
        await SupabaseManager.app.From<SchemaCareItemData>().Not(exists).Delete();

But i got :
image

After further research, turns out the URI is too long : (Helpers file in Supabase Core, line 107)
image

To Reproduce

Create a filter with a big list (388 Guids as for me)

Expected behavior

Complete properly the request

System information

  • OS: Windows

Range type Exception when Inserting

Bug report

Describe the bug

Hello, while trying to insert a new record in the database using the project PostgrestExample in the current repo an Exception is thrown from Newtonsoft.Json.JsonReaderException.

After Debugging the Exception is thrown in PrepareRequestData Method in the Builder class. I think the reason why is because the User table contains an AgeRange property of type Sytem.Range So when calling the JsonConvert.Deserialize to get a dictionary of string key and a string value is not possible because the System.Range contains multiple properties as Start and End that are also objects of type Index so the when deserializing the JsonReader will find curly brackets and this cannot be deserialized to dictionnary<string, string>

In the Reproduce step with the current values the json object will be in this shape :

{
  "Username":"younes",
  "Data":null,
  "AgeRange":
                      {
                         "Start":
                                      {
                                          "Value":1,
                                          "IsFromEnd":false
                                       },
                          "End":
                                       {
                                          "Value":3,
                                          "IsFromEnd":false
                                        }
                        },
"Catchphrase":"Hii",
"Status":null,
"InsertedAt":"0001-01-01T00:00:00",
"UpdatedAt":"0001-01-01T00:00:00"
}

This json will throw an exception when trying to the call

public Dictionary<string, string> PrepareRequestData(object data) => JsonConvert.DeserializeObject<Dictionary<string,string>>(JsonConvert.SerializeObject(data));

To Reproduce

  1. Initiliaze a new User
var supaYounes = new User
{
      Username = "younes",
      Catchphrase = "Hii",
      AgeRange = new Range(1, 3)
};
  1. Insert
var response = await client.Builder<User>().Insert(supaYounes);

Expected behavior

User Inserted successfully in the Database

System information

  • OS: [Windows 10 Pro 1909]
  • .Net core 3.1.x
  • Visual Studio 16.8
  • Version of supabase-postgrest-csharp: master branch of current repo

BaseModel has default properties

Bug report

Describe the bug

Hello, To bind a Table to a Model we should inherit from BaseModel, I think we should remove the default properties like InsertedAt, UpdatedAt and Status

To Reproduce

  1. Create a new table without the date timestamps (inserted_at, updated_at) and status
  2. Create a model inherit from BaseModel

Expected behavior

  • Model without the extra properties (InsertedAt, UpdatedAt, Status)

Thank you very much

Ordering not working

Bug report

Describe the bug

Hello, a RequestException is thrown when trying to perform an order request on a table. After debugging I found out that the Postgrest was returning a Bad Request (400) because the GenerateUrl method in the Builder was generating an URL not corresponding to the expected one by Postgrest.

The issue was caused by the Enum Ordering the value was not mapped. The expected URL /table?order=column.desc.nullsfirst, the generated url by the method is /table?order=column.Descending.nullsfirst

Expected values from Postgrest are asc and desc.

I fixed the issue by mapping the ordering enum value and add it to the query. I submitted a PR ☺️

Thank you very much.

To Reproduce

  • Perform an order request
await client.Builder<User>().Order("catchphrase", Constants.Ordering.Descending).Get();

Expected behavior

  • Get a response with rows ordered by the specified column

System information

-OS: [Windows 10 Pro 1909]
-.Net core 3.1.x
-Visual Studio 16.8
-Version of supabase-postgrest-csharp: master branch of current repo

Issue sending non-string data through RPC

Bug report

Describe the bug

Line 100 in Postgrest/Client.cs seems wrong, but also intentional.

It is taking a Dictionary<string, object> in, serialising it as JSON and then deserialising it as a Dictionary<string, string>.

Why is this being done? Forcing things to be a string seems to break nested structures, with no good alternative, unless I'm missing something.

To Reproduce

For example, the following gets a deserialization error:

await db.Rpc("foo", new Dictionary<string, object> {
	{
		"bar",
		new Dictionary<string, object> { { "baz", "qux" } }
	}
});

Expected behavior

Objects should work. I believe the fix could be as simple as (1) not serialising and deserialising or (2) deserialising as Dictionary<string, object>.

Screenshots

NA

System information

  • OS: Windows
  • Browser (if applies): NA
  • Version of supabase-js: NA
  • Version of Node.js: NA

Additional context

NA

Getting System.NotSupportedException on Azure App Service

Bug report

Describe the bug

In supabase have a Recipes table with 2 columns: id (int8 and primary key) and Name (text)

So in my C# code I have a Recipe class:

using Postgrest.Attributes;
using Postgrest.Models;

namespace Planner.Classes
{
    [Table("Recipes")]
    public class Recipe : BaseModel
    {
        [PrimaryKey("id")]
        public int Id { get; set; }
        [Column("Name")]
        public string Name { get; set; }
        [Reference(typeof(Ingredient))]
        public List<Ingredient> Ingredients { get; set; }
    }
}

where Ingredients is coming from a many-to-many join table (which I believe shouldn't be important now...I think)

In a separate project in the same solution I have created a .NET Core 6.0 API with a controller.
To make it extremely simple right now for reporting, the controller is this:

using Microsoft.AspNetCore.Mvc;
using WeeklyMealPlanner.Classes;

namespace PlannerApi.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class RecipesController : ControllerBase
    {
        [HttpGet(Name = "GetRecipes")]
        public List<Recipe> GetRecipes()
        {
            return new List<Recipe> { new Recipe { Name = "A" } };
        }
    }
}

When I test this locally with a console app, everything works normally and I get a list of all my recipes stored in supabase, their ingredients, etc.
But when I publish this to Azure App Service then I get a 500 error in the browser and if I check Application Insights I see that it's reporting a System.NotSupportedException at System.Text.Json.ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported with this message:
"The type 'Postgrest.Attributes.PrimaryKeyAttribute' is not a supported dictionary key using converter of type 'System.Text.Json.Serialization.Converters.SmallObjectWithParameterizedConstructorConverter5[Postgrest.Attributes.PrimaryKeyAttribute,System.String,System.Boolean,System.Object,System.Object]'. Path: $.PrimaryKey. The type 'Postgrest.Attributes.PrimaryKeyAttribute' is not a supported dictionary key using converter of type 'System.Text.Json.Serialization.Converters.SmallObjectWithParameterizedConstructorConverter5[Postgrest.Attributes.PrimaryKeyAttribute,System.String,System.Boolean,System.Object,System.Object]'. "

I have upgraded all packages including supabase-csharp, postgrest-csharp and Newtonsoft.Json

I also tried to modify the Recipe class so that it doesn't use any postgrest or supabase library and my controller returns correctly a recipe of name "A" in Chrome.

namespace WeeklyMealPlanner.Classes
{
    public class Recipe
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Ingredient> Ingredients { get; set; }
    }
}

I'm really out of ideas right now.

Am I doing something wrong??

PS. Thanks for supabase!!. I just recently discovered it and it awesome!! Keep up the good work!

System information

  • OS: Windows
  • Browser: chrome

Calling Count() query raises ProtocolViolationException

Hi,

I want to get a count of all the rows in my table with this query:

return await Supabase.Client.Instance.From<MyModel>()
     .Select("id")
     .Count(Postgrest.Constants.CountType.Exact);

And I get a ProtocolViolationException. The top of the stack trace looks like this:

ProtocolViolationException: Cannot send a content-body with this verb-type.
System.Net.HttpWebRequest.MyGetRequestStreamAsync (System.Threading.CancellationToken cancellationToken) (at <f326fc0a10e542b298005d1c9ab100aa>:0)
System.Net.HttpWebRequest.RunWithTimeout[T] (System.Func`2[T,TResult] func) (at <f326fc0a10e542b298005d1c9ab100aa>:0)
System.Net.HttpWebRequest.GetRequestStreamAsync () (at <f326fc0a10e542b298005d1c9ab100aa>:0)
System.Net.Http.MonoWebRequestHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) (at <d4a37b33de154aa9a7a1a042eef9b39c>:0)
System.Net.Http.HttpClient.SendAsyncWorker (System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpCompletionOption completionOption, System.Threading.CancellationToken cancellationToken) (at <d4a37b33de154aa9a7a1a042eef9b39c>:0)
Postgrest.Helpers.MakeRequest (System.Net.Http.HttpMethod method, System.String url, Newtonsoft.Json.JsonSerializerSettings serializerSettings, System.Object data, System.Collections.Generic.Dictionary`2[TKey,TValue] headers) (at Library/PackageCache/com.supabase.unity@ada643f912/supabase-cloned/supabase-csharp/modules/postgrest-csharp/Postgrest/Helpers.cs:80)
Postgrest.Table`1+<>c__DisplayClass44_0[T].<Count>b__0 () (at Library/PackageCache/com.supabase.unity@ada643f912/supabase-cloned/supabase-csharp/modules/postgrest-csharp/Postgrest/Table.cs:474)

If I look in Table.cs at the Count() function (line 455 in my version) I see this:

        public Task<int> Count(CountType type)
        {
            var tsc = new TaskCompletionSource<int>();

            Task.Run(async () =>
            {
                method = HttpMethod.Head;

                var attr = type.GetAttribute<MapToAttribute>();

From the error message, it looks like the Head HTTP request method doesn't allow a body to be sent. So if I change HttpMethod.Head to HttpMethod.Get, everything works fine. Something in here is including a message body when it's not supposed to, I guess?

I think you can reproduce this by trying to make a Count() request on any given query. If not, I am using this postgrest-csharp bundled in unity-supabase cloned from the repo three days ago. I'd be happy to provide additional information if necessary.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.