GithubHelp home page GithubHelp logo

edgedb / edgedb-net Goto Github PK

View Code? Open in Web Editor NEW
78.0 9.0 8.0 2.32 MB

The official .NET client library for EdgeDB

Home Page: https://edgedb.com

License: Apache License 2.0

C# 97.55% EdgeQL 2.45%
cs csharp database dotnet edgedb efcore fs fsharp

edgedb-net's Introduction

EdgeDB

Stars license discord

Quickstart   •   Website   •   Docs   •   Playground   •   Blog   •   Discord   •   Twitter



What is EdgeDB?

EdgeDB is a new kind of database
that takes the best parts of
relational databases, graph
databases, and ORMs. We call it
a graph-relational database.



🧩 Types, not tables 🧩


Schema is the foundation of your application. It should be something you can read, write, and understand.

Forget foreign keys; tabular data modeling is a relic of an older age, and it isn't compatible with modern languages. Instead, EdgeDB thinks about schema the same way you do: as object types containing properties connected by links.

type Person {
  required name: str;
}

type Movie {
  required title: str;
  multi actors: Person;
}

This example is intentionally simple, but EdgeDB supports everything you'd expect from your database: a strict type system, indexes, constraints, computed properties, stored procedures...the list goes on. Plus it gives you some shiny new features too: link properties, schema mixins, and best-in-class JSON support. Read the schema docs for details.


🌳 Objects, not rows 🌳


EdgeDB's super-powered query language EdgeQL is designed as a ground-up redesign of SQL. EdgeQL queries produce rich, structured objects, not flat lists of rows. Deeply fetching related objects is painless...bye, bye, JOINs.

select Movie {
  title,
  actors: {
    name
  }
}
filter .title = "The Matrix"

EdgeQL queries are also composable; you can use one EdgeQL query as an expression inside another. This property makes things like subqueries and nested mutations a breeze.

insert Movie {
  title := "The Matrix Resurrections",
  actors := (
    select Person
    filter .name in {
      'Keanu Reeves',
      'Carrie-Anne Moss',
      'Laurence Fishburne'
    }
  )
}

There's a lot more to EdgeQL: a comprehensive standard library, computed properties, polymorphic queries, with blocks, transactions, and much more. Read the EdgeQL docs for the full picture.


🦋 More than a mapper 🦋


While EdgeDB solves the same problems as ORM libraries, it's so much more. It's a full-fledged database with a powerful and elegant query language, a migrations system, a suite of client libraries in different languages, a command line tool, and—coming soon—a cloud hosting platform. The goal is to rethink every aspect of how developers model, migrate, manage, and query their database.

Here's a taste-test of EdgeDB's next-level developer experience: you can install our CLI, spin up an instance, and open an interactive EdgeQL shell with just three commands.

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.edgedb.com | sh
$ edgedb project init
$ edgedb
edgedb> select "Hello world!"

Windows users: use this Powershell command to install the CLI.

PS> iwr https://ps1.edgedb.com -useb | iex

Get started

To start learning about EdgeDB, check out the following resources:

  • The quickstart. If you're just starting out, the 10-minute quickstart guide is the fastest way to get up and running.
  • EdgeDB Cloud 🌤️. The best most effortless way to host your EdgeDB database in the cloud.
  • The interactive tutorial. For a structured deep-dive into the EdgeQL query language, try the web-based tutorial— no need to install anything.
  • The e-book. For the most comprehensive walkthrough of EdgeDB concepts, check out our illustrated e-book Easy EdgeDB. It's designed to walk a total beginner through EdgeDB in its entirety, from the basics through advanced concepts.
  • The docs. Jump straight into the docs for schema modeling or EdgeQL!

Contributing

PRs are always welcome! To get started, follow this guide to build EdgeDB from source on your local machine.

File an issue 👉
Start a Discussion 👉
Join the discord 👉


License

The code in this repository is developed and distributed under the Apache 2.0 license. See LICENSE for details.

edgedb-net's People

Contributors

conaticus avatar csmir avatar github-actions[bot] avatar goerch avatar i0bs avatar jaclarke avatar justslon avatar kubaz2 avatar kylejuliandev avatar quinchs avatar raddevon avatar syzuna avatar yuuuxt 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

edgedb-net's Issues

EdgeDBConnection.FromDSN creates critical failure in built client

When attempting to create a client using a DSN, the following error is thrown:

System.ArgumentOutOfRangeException
  HResult=0x80131502
  Message=The maximumCount argument must be a positive number. If a maximum is not required, use the constructor without a maxCount parameter. (Parameter 'maxCount')
Actual value was 0.
  Source=System.Private.CoreLib
  StackTrace:
   at System.Threading.SemaphoreSlim..ctor(Int32 initialCount, Int32 maxCount)
   at EdgeDB.ClientPoolHolder.<ResizeAsync>d__6.MoveNext()
   at EdgeDB.EdgeDBClient.<>c__DisplayClass50_0.<<CreateClientAsync>g__OnConnect|0>d.MoveNext()
   at EdgeDB.EventExtensions.<InvokeAsync>d__2`1.MoveNext()
   at EdgeDB.EdgeDBBinaryClient.<ConnectAsync>d__59.MoveNext()
   at EdgeDB.EdgeDBBinaryClient.<ReconnectAsync>d__61.MoveNext()
   at EdgeDB.EdgeDBBinaryClient.<ExecuteInternalAsync>d__47.MoveNext()
   at EdgeDB.EdgeDBBinaryClient.<QuerySingleAsync>d__50`1.MoveNext()
   at EdgeDB.EdgeDBClient.<QuerySingleAsync>d__43`1.MoveNext()
   at EdgeDB.EdgeDBClient.<QuerySingleAsync>d__43`1.MoveNext()
   at Program.<<Main>$>d__0.MoveNext() in C:\Users\{me}\source\repos\edgeql-test\Program.cs:line 12

Reproduction

using EdgeDB;

var edgeConnection = EdgeDBConnection.FromDSN("valid_dsn");
edgeConnection.TLSSecurity = TLSSecurityMode.Insecure;
var client = new EdgeDBClient(edgeConnection);

// any query produces the same result
await client.QuerySingleAsync<int>("SELECT 2");

DSN was testing using both the Node.js client and the CLI, and was able to execute queries.

Versions

  • OS: Windows 10
  • EdgeDB version: 2.11+3f34965
  • edgedb-net version: 1.1.2
  • .NET version: 7.0.20

EdgeQLSyntaxError: Unexpected '+='

Summary

Seems like the member init translator has decided to break the EdgeQL syntax rules again.. 🤷🏻

Standard libs funcs with contextual assignment operators (+=, -=, etc) NEED to preserve their set operators, which when I rewrote the init translator, got left out of the new link path.

Exception

EdgeQLSyntaxError: Unexpected '+='
EdgeQLSyntaxError: Unexpected '+='
   |
 1 | with BMVZTQOFIRIR := (insert UserSchema { created_at := <datetime>$DCDLEUGSEZHB, schema := <str>$FDBHZNBORAEF }) update User filter .user_id ?= <int64>$SZUBJXNCKYMM set { schema_history := += BMVZTQOFIRIR }
   |                                                                                                                                                                                              ^^

QueryBuilder code:

await QueryBuilder
    .Update<User>(old => new User
    {
        // this line here!
        SchemaHistory = EdgeQL.AddLink(QueryBuilder.Insert(migration, false))
    })
    .Filter(x => x.UserId == id)
    .ExecuteAsync(_edgedb, token: token);

Create abstract type inheritance class

Proposing us to add some form of an abstract class that expects a Id prop of Guid. The motive behind this is that having a separate class means we could provide helper methods for handling things with the base object and ID.

For regular .NET usage, users can inherit it if they wish to tap in for more helper methods and robustness.

For code generation, I propose we inherit it by default for ease of use.

Driver wants to convert shape to wrong type

Describe the bug
A select query with links to shapes of different types but the of the same base type, like author and director of movies being all persons, results in conversion errors as long as the shapes' fields are queried in exactly the same way (same fields in the same order):

Exception source: EdgeDB.Net.Driver
EdgeDB.EdgeDBException: Failed to deserialize object to DB.Model.Movies.Movie
 ---> System.ArgumentException: Cannot convert DB.Model.Movies.Director to type DB.Model.Movies.Actor
   at EdgeDB.ObjectBuilder.ConvertTo(Type type, Object value)
   at ...
   --- End of inner exception stack trace ---
   at EdgeDB.Binary.Codecs.ObjectCodec.Deserialize(PacketReader& reader, CodecContext context)
   at EdgeDB.TypeBuilder.BuildObject(EdgeDBBinaryClient client, Type type, ObjectCodec codec, Data& data)
  ...

Reproduction
The following query, where actors and directors are defined in the same way, cause the exception. As soon as the order of one shape is changed, everything is working.

        var query = $$"""
            select Movie {
                id,
                title,
                year,
                description,
                actors: {
                    id,
                    full_name,
                    first_name,
                    middle_name,
                    last_name,
                    image,
                    dod,
                    dob,
                    bio,
                    @list_order
                },
                directors: {
                    id,
                    full_name,
                    first_name,
                    middle_name,
                    last_name,
                    image,
                    dod,
                    dob,
                    bio,
                    @list_order
                }
            };
            """;
        await _Client.QueryAsync<Movie?>(query);

A full example with schema and db content can be found here.

Expected behavior
Shapes should be converted to the correct object type, regardless of "field randomisation".

Versions (please complete the following information):

  • OS: macOS Monterey 12.5.1
  • EdgeDB version: 2.14+5561c9b
  • EdgeDB CLI version: 2.3.1+5d93f42
  • edgedb-net version: 1.1.3
  • .NET version: 7.0.203

Feature: configure state in transactions

Currently, there's no way to configure state within transactions. The API for state on the client should be accessible on the tx object like so:

var result = await client.TransactionAsync(async (tx) =>
    tx.WithGlobals(new Dictionary<string, object>
    {
        { "abc": 123L }    
    })
    .QuerySingleAsync<long>("select global abc")
);

Is it possible to embed this database?

I was looking for embedded database (like SQLite or LiteDB). I don't want to install a database core (cluster or something) to target machine.

Is is possible to use EdgeDB in "portable" mode?

QueryBuilder For with Links throws EdgeDBErrorException

Summary

When trying to do a bulk insert with a "for" the query builder creates a faulty query that results in an EdgeDBErrorException.

Code

return await QueryBuilder
        .For(factions, faction => QueryBuilder.Insert(faction, false))
        .ExecuteAsync(_edgeDb, token: cancellationToken);

Stacktrace

EdgeDB[1] Failed to execute query EdgeDB.EdgeDBErrorException: object type or alias 'default::TLIZEJSNTVCU_d1' does not exist    at EdgeDB.EdgeDBBinaryClient.<>c__DisplayClass49_1.<ExecuteInternalAsync>g__parseHandlerPredicate|2(IReceiveable packet)    at EdgeDB.ClientPacketDuplexer.<>c__DisplayClass31_0.<NextAsync>b__0(IReceiveable t) --- End of stack trace from previous location ---    at EdgeDB.ClientPacketDuplexer.NextAsync(Predicate`1 predicate, Boolean alwaysReturnError, CancellationToken token)    at EdgeDB.ClientPacketDuplexer.DuplexAsync(Predicate`1 predicate, Boolean alwaysReturnError, CancellationToken token, Sendable[] packets)    at EdgeDB.EdgeDBBinaryClient.ExecuteInternalAsync(String query, IDictionary`2 args, Nullable`1 cardinality, Nullable`1 capabilities, IOFormat format, Boolean isRetry, CancellationToken token)

Generated Query

for faction in json_array_unpack(
  <json>$YAZBVRJPGFFH
)
union (
  insert Faction {
    name := <str>json_get(
      faction,
      'Name'
    ),
    is_player_faction := <bool>json_get(
      faction,
      'IsPlayerFaction'
    ),
    allegiance := <str>json_get(
      faction,
      'Allegiance'
    ),
    government := <str>json_get(
      faction,
      'Government'
    ),
    home_star_system := (
      select TLIZEJSNTVCU_d1 offset <int64>json_get(
        faction,
        'HomeStarSystem',
        'TLIZEJSNTVCU_depth_index'
      )
      limit 1
    )
    if json_typeof(
      json_get(
        faction,
        'HomeStarSystem'
      )
    )
    != 'null' else <StarSystem>{}
  }
)

Original report on archived repo: quinchs/EdgeDB.Net#32

Document .EdgeQL CLI tool

Related to #3, documentation is preferred for users wanting to find out about the CLI through the EdgeDB website. We can most likely use the :cli:synposis: Sphinx callable for all of the documentation.

This means we can also add admonition notes in areas where we want to inform users that there's a way they could perform an action through the CLI instead, such as automatically generating classes for query execution.

Bug: Tuple cannot be serialized

Summary

Tuples can be defined as globals as well as arguments as per edgedb/edgedb#4489, currently, the .NET binding doesn't implement a way to encode tuples.

The tuple codec will have to become a complex codec to support ValueTuple<>, Tuple<>, and TransientTuple during serialization, following the pipeline defined in #28

Type confusion when retrieving data typed as option

Describe the bug
There seems to be a type confusion bug when retrieving data typed as an option.

Reproduction
With the following schema:

module default {
  type Settings {
    required uniqueId: str {
      default := "settings"; 
      constraint exclusive;
    };
    required baz: int32 {
      default := 2;
      constraint min_value(1);
    };
    required bar: bool {
      default := true;
    };
  };
  type Foo {
    required uniqueId: str {
      default := "foo"; 
      constraint exclusive;
    };
    required thing: bool {
      default := true;
    };
  }
}

And the following code:

type Settings =
    { uniqueId: string
      baz: int
      bar: bool }

type Foo = { uniqueId: string; thing: bool }

do! dbClient.ExecuteAsync("""insert Settings unless conflict on .uniqueId;""")
do! dbClient.ExecuteAsync("""insert Foo unless conflict on .uniqueId;""")

let! settings = dbClient.QuerySingleAsync<Option<Settings>>("""select Settings limit 1""")

let! foo = dbClient.QuerySingleAsync<Option<Foo>>("""select Foo limit 1""")

I get an error message of:

Error: EdgeDB.EdgeDBException: Failed to deserialize object to Microsoft.FSharp.Core.FSharpOption`1[Program+Foo]
 ---> System.ArgumentException: Object of type 'Program+Foo' cannot be converted to type 'Program+Settings'.
   at System.RuntimeType.CheckValue(Object& value, ParameterCopyBackAction& copyBack, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.MethodBase.CheckArguments(Span`1 copyOfParameters, IntPtr* byrefParameters, Span`1 shouldCopyBack, ReadOnlySpan`1 parameters, RuntimeType[] sigTypes, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at EdgeDB.Binary.Builders.Wrappers.FSharpOptionWrapper.WrapReferenceOption(Type target, Object value)
   at EdgeDB.Binary.Builders.Wrappers.FSharpOptionWrapper.Wrap(Type target, Object value)
   at EdgeDB.EdgeDBTypeDeserializeInfo.<>c__DisplayClass28_0.<.ctor>b__0(ObjectEnumerator& enumerator)
   at EdgeDB.Binary.Codecs.TypeInitializedObjectCodec.Deserialize(PacketReader& reader, CodecContext context)
   --- End of inner exception stack trace ---
   at EdgeDB.Binary.Codecs.TypeInitializedObjectCodec.Deserialize(PacketReader& reader, CodecContext context)
   at EdgeDB.TypeBuilder.BuildObject(EdgeDBBinaryClient client, Type type, ObjectCodec codec, Data& data)
   at EdgeDB.ObjectBuilder.BuildResult[TType](EdgeDBBinaryClient client, ICodec codec, Data& data)
   at EdgeDB.EdgeDBBinaryClient.QuerySingleAsync[TResult](String query, IDictionary`2 args, Nullable`1 capabilities, CancellationToken token)
   at EdgeDB.EdgeDBClient.QuerySingleAsync[TResult](String query, IDictionary`2 args, Nullable`1 capabilities, CancellationToken token)
   at EdgeDB.EdgeDBClient.QuerySingleAsync[TResult](String query, IDictionary`2 args, Nullable`1 capabilities, CancellationToken token)
   at Program.Pipe #1 input at line [email protected]() in /home/coop/Coding/scratch/tempdelete/Program.fs:line 24


Basic repro: https://github.com/Darkle/edgedb-issue-5

Versions (please complete the following information):

  • OS: Debian Linux
  • EdgeDB version: 3.2+bb17a05
  • EdgeDB CLI version: 3.4.0+160d07d
  • edgedb-net version: 1.2.3-build-167
  • .NET version: 7.0.306

[Feature request] Ability to print generated query string with inlined parameters to aid debugging

It would be super handy if it were possible to be able to log/print the query string Edgedb.Net constructs, including inlined parameters.

e.g. SELECT <str>$param with "abc" as the parameter would be SELECT "abc"

Being able to see the completed constructed query string (including the inlined parameters) would aid greatly in debugging.

Relevant thread: https://discord.com/channels/841451783728529451/950503041889628200/1158240973093093417

Cheers.

Testing on Windows 11 is broken

Just running the tests on a freshly created fork results in

  Nachricht: 
Es kann keine Instanz der Klasse 'EdgeDB.Tests.Integration.ClientTests' erstellt werden. Fehler: System.IO.DirectoryNotFoundException: Couldn't find project directory for [redacted].

  Stapelüberwachung: 
EdgeDBConnection.FromProjectFile(String path) Zeile 371
EdgeDBConnection.ResolveEdgeDBTOML() Zeile 407
EdgeDBClient.ctor(EdgeDBClientPoolConfig clientPoolConfig) Zeile 120
ClientProvider.get_EdgeDB() Zeile 13
ClientTests.ctor() Zeile 21
RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
ConstructorInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)

Sorry for the German language settings.

Fix rounding when encoding timestamps

According to edgedb/edgedb#4885 conversion should round up to nearest even.

This should happen when encoding .Net native time format into a millisecond precision:

1970-01-01T00:00:00.0000005 -> 1970-01-01T00:00:00.000000
1970-01-01T00:00:00.0000015 -> 1970-01-01T00:00:00.000002

Also check LocalTime, Duration and LocalDatetime types.

This should happen across all the range of the values, see Rust tests for more comprehensive set of tests (but adjust precision conversion and time format accordingly). And the docs PR for more info.

Proposal: Better type control for codegen

The Problem

Code generation is a neat tool which alleviates the hassle of query strings while providing full fledge types for query results. The issue arises when you want to build from the generated types.

Let's give an example scenario where this problem arises, lets imagine you have a User type in edgedb and you create 4 different edgeql files:

  • CreateUser.edgeql
  • GetUser.edgeql
  • UpdateUser.edgeql
  • DeleteUser.edgeql

The actual edgeql within these files doesn't matter, as long as their all targeting the User type.

Currently, the code generator will generate a result type for each of the files. This leaves us with 4 different dotnet types representing 1 edgeql one; not good! In our dotnet codebase consuming the generated files, we'd have to model a type wrapping the generated ones like so:

public class UserModel
{
    public Guid Id { get; set; }
    public string Email { get; set; }
    public string Name { get; set; }

    public UserModel(CreateUserResult model)
    {
        Id = model.Id;
        Name = model.Name;
        Email = model.Email;
    }

    public UserModel(GetUserResult model)
    {
        Id = model.Id;
        Name = model.Name;
        Email = model.Email;
    }

    public void Update(UpdateUserResult model)
    {
        Id = model.Id;
        Name = model.Name;
        Email = model.Email;
    }
}

The amount of code scales with the amount of files that use the User type. Other bindings don't seem to have this problem due to less restrictive type systems.

The Proposal

My idea is to add an additional file to the output, called TypeManifest.yaml, which maps generated types to the return type of the generated functions, the code generator will then use this file to generate result types.

Example

Let's use this type manifest idea to solve the above problem:

types:
  - name: UserModel
    functions:
      - GetUser
      - UpdateUser
      - CreateUser
      - DeleteUser

The code generator can then create the UserModel type, infer the properties by the codecs of each query function, and apply it as the result each function defined on the functions property.

The result of the generation will look something like this:

// UserModel.g.cs
public partial class UserModel
{
    public Guid Id { get; set; }
    public string Email { get; set; }
    public string Name { get; set; }
}

// GetUser.g.cs
public class GetUser
{
    public static async Task<UserModel> ExecuteAsync(IEdgeDBQueryable client, Guid id, CancellationToken token)
    { 
         ...
    }
}

// UpdateUser.g.cs
public class UpdateUser
{
    public static async Task<UserModel> ExecuteAsync(IEdgeDBQueryable client, Guid id, CancellationToken token)
    { 
         ...
    }
}

...

We can also use interfaces, if the name of the type starts with an "I" the code generator can create an interface which has only properties shared between all functions using the model. This system can be extended by preforming introspection of the entire schema to build abstract types if present.

A default type manifest will be generated with the current logic if one isn't present, mapping out the default generated types. If one is present, the code generator will read the type manifest and then build the types using it as the template.

With this approach we can also include support for type converters by defining a way to change property types via the type manifest.

How do I use tuple in C# call to _EdgeDBclient.QuerySingleAsync()

Describe the bug

I submitted a question in Discord -> EdgeDb -> edgedb-dotnet on Mon, 1/15/24
but no one has responded as of 1/16/24 14:58 PAC.

I've tried multiple options for using a tuple in an INSERT statement, but I keep getting different warnings returned from EdgeDB.
For Visual Studio 2022 C# .NET 7 code
$ edgedb --version
EdgeDB CLI 4.0.2+b26c502

Reproduction
Include the code that is causing the error:

Here is the default.esdl

	type Artist {
		required                   artistname: str {
				constraint exclusive;
				constraint min_len_value(3);
								}
		                           othernames: array<str>;
		required              wikipediapageid: int32;
		                            birthdate: datetime;
		                           familyname: tuple<first: str, middle: str, last: str>;
		required multi                 genres: Genre;
		         multi                 labels: MusicCompanyLabel;
		required                    timestamp: datetime;
		}

Here is the C# code (below code is the multiple iterations I tried and the EdgeDB errors that were returned):

		string familyNameFirst = "";
		string familyNameMiddle = "";
		string familyNameLast = "";
		if (artist.FamilyName != null) {
			familyNameFirst  = string.Join(", ", artist.FamilyName.Item1);
			familyNameMiddle = string.Join(", ", artist.FamilyName.Item2);
			familyNameLast   = string.Join(", ", artist.FamilyName.Item3);
		}

            try
            {
                var result = await _EdgeDBclient.QuerySingleAsync<Artist>(
                    " INSERT Artist " +
                    " { artistname      := <str>$name, " + 
                    "   othernames      := <array<str>>$onam, " +
                    "   wikipediapageid := <int32>$wpid, " +
                    "   birthdate       := <datetime>$bdat, " +
                    "   familyname      := <tuple< first: <str>$fname, middle: <str>$mname, last: <str>$lname >>, " +
                    "   genres          := <array<str>>$genr, " +
                    "   labels          := <array<str>>$labl, " +
                    "   timestamp       := <datetime>$ptst } ",
                                        new
                                        {
                                            name = artist.ArtistName,
                                            onam = otherNamesString,
                                            wpid = artist.WikipediaPageID,
                                            bdat = artist.BirthDate,
                                            fname = familyNameFirst,
                                            mname = familyNameMiddle,
                                            lname = familyNameLast,
                                            genr = genreString,
                                            labl = labelString,
                                            ptst = artist.PageTimeStamp
                                        });
            }
            catch (EdgeDB.EdgeDBErrorException ex)
            {
                Console.WriteLine($"{DateTime.Now} BandServer InsertArtistAsync for {artist.ArtistName}, WikiPageID {artist.WikipediaPageID} error from EdgeDB: ( {ex.Message} )");
                return false;
            }

Here are the various versions I've tried and the error returned:

// "   familyname      := tuple{ first := <str>$fname, middle := <str>$mname, last := <str>$lname }, " +
//  object type or alias 'default::tuple' does not exist
//
// "   familyname      := { first := <str>$fname, middle := <str>$mname, last := <str>$lname }, " +
// invalid target for property 'familyname' of object type 'default::Artist': 'std::FreeObject'
//         (expecting 'tuple<first: std::str, middle: std::str, last: std::str>')
//
// "   familyname      := tuple<first: <str>$fname, middle: <str>$mname, last: <str>$lname>, " +
// Unexpected ':'
//
// "   familyname      := tuple<first <str>$fname, middle <str>$mname, last <str>$lname>, " +
// Missing '['
//
// "   familyname          := { first := <str>$fname, middle := <str>$mname, last := <str>$lname }, " +
// invalid target for property 'familyname' of object type 'default::Artist': 
// 'std::FreeObject' (expecting 'tuple<first: std::str, middle: std::str, last: std::str>')
//
// "   familyname          := { first := <str>$fname, middle := <str>$mname, last := <str>$lname }, " +
//
// "   familyname      := <tuple< first := $fname, middle := $mname, last := $lname >>, " +
// Unexpected ':='
//
// "   familyname      := <tuple< first := <str>$fname, middle := <str>$mname, last := <str>$lname >>, " +
// Unexpected ':='
//
//"   familyname      := <tuple< first : <str>$fname, middle : <str>$mname, last : <str>$lname >>, " +
// Missing keyword 'TYPE'

Expected behavior

Trying to not get and error and INSERT the C# tuple properly into the INSERT statement.

Versions (please complete the following information):

$ dotnet --version

8.0.100

Frameworks
MicrosoftAspNetCoreApp
MicrosoftNETCoreApp
Packages
EdgeDB.Net.Driver (1.3.0)
MicrosoftAspNetCore.OpenApi (7.0.10)
Swashbuckle.AspNetCore (6.5.0)
System.Net.Http.Json (8.0.0)

  • OS: Windows 11
  • EdgeDB version:
  • EdgeDB CLI version:
  • edgedb-net version:
  • .NET version:

Additional context

Please add an example with serialization/deserialization of tuples

It is not clear how to correctly access edgedb functions that return or accept unnamed tuples of values as input

For example in schema I define the function function getRepayment(id: uuid, amount: decimal) -> optional tuple<decimal, decimal, decimal, decimal, decimal, decimal> how to query it?

System.Security.Authentication.AuthenticationException on Insert

Describe the bug

  • On insert I get a System.Security.Authentication.AuthenticationException and no data is persisted to the database.
  • The Auth object is default with priority 1 and method set to Trust.
  • EdgeDB has crashed in the last few tests after experiencing this as well but I know that's out of the scope of this issue.

Connection and Query code below.

Reproduction
Connection Code

private readonly EdgeDBClient _edgeDB;

public Edge() 
{
    var connection = new EdgeDBConnection();
    connection.Hostname = "localhost";
    connection.Database = "edgedb";
    connection.Port = 10700;
    //connection.Password = "password";

    _edgeDB = new EdgeDBClient(connection); 
}

Function being called:

public async Task AddItem(Item item)
{
    var insertItemQuery = $"insert Item {{ Id := \"{item.Id}\", Name := \"{item.Name}\", Description := \"{item.Description}\", User := (insert User {{ Id := \"{item.User.Id}\", Name := \"{item.User.Name}\", Password := \"{item.User.Password}\", UserName := \"{item.User.UserName}\", Email := \"{item.User.Email}\" }}) }} unless conflict on .Email";

    // Add item
    await _edgeDB.ExecuteAsync(insertItemQuery);
}

Screenshot of caller and error msg:
error

Expected behavior
I expect the item to be added to the database according to the insert command and schema. I at least expect the connection to be made and any errors with my query to be displayed if that's the case.

Versions (please complete the following information):

  • EdgeDB Version: 2.9
  • EdgeDB CLI Version: 2.2.6
  • OS Version: Windows 10 Home 21H2 19044.2364
  • EdgeDB.Net.Driver version: 1.0.4
  • .NET version: .Net 7.0

Additional context
If you need more information let me know!

QueryBuilder does not translate type converted properties correctly

Summary

With the introduction of type converters, the bridge between Expressions and EdgeQL starts to bend. Currently, the querybuilder correctly translates assignment/initializations expressions and shapes, but it falls short at expressions that are directly influenced by type converted properties.

Example:

public class User
{
    [EdgeDBTypeConverter(typeof(UlongToStringConverter))]
    public ulong SnowflakeId { get; set; }
}

...

ulong targetId = 12345;

var result = await QueryBuilder
    .Select<User>()
    .Filter(x => x.SnowflakeId  == targetId)
    .ExecuteAsync();

The expression x => x.SnowflakeId == targetId does not take into account that SnowflakeId is actually a string in EdgeDB, and treats targetId as a number, producing the following snippet:

.snowflake_id = <int64>$random_var_name

which errors due to the type difference of str and int64.

Potential solution

We can look to quantum mechanics for a solution, taking inspiration from the principle of entanglement. The query builder can find all expressions entangled with a particular type converted property and put the entangled references thru the same type converter to build the EdgeQL query.

With this approach, our above expression follows this logic:

  1. Binary expression is translated
  2. The member expression x.SnowflakeId is discovered as a type converted property.
  3. The expression tree is walked backwards from the MemberExpression and any values with the same shared type as the member expression are updated with the converted value.

I believe that a ExpressionVisitor can be used to achieve this, if not we can simply just rebuild the effected expressions.

Cannot deserialize data to Person[] / Index was outside the bounds of the array.

Running links-example results in an System.InvalidOperationException: Cannot deserialize data to Person[] exception.

Changing property Actors in class Movie to type List<Person>?:

public class Movie
{
	...

	[EdgeDBProperty("actors")]
	public List<Person>? Actors { get; set; } // multi link, can also use List<Person> here
}

results in an System.IndexOutOfRangeException: Index was outside the bounds of the array. exception.

  • EdgeDB version: Docker edgedb/edgedb:2.12
  • edgedb-net version: 1.1.0
  • .NET version: net6.0

Data gets deserialized into wrong type

Describe the bug
When the type of two links on the same entity is similar (that is, the two types share field names), the deserializer attempts to create objects of the wrong type for one of them.

Reproduction
Project repo that includes schema and data: https://github.com/thedjdoorn/edgedb-demo

If you visit a Person-page (/person/a1fddd58-c18d-11ed-8e59-03e4e3f563dc for instance), and place a breakpoint in both the Interest and Company deserializer, you'll notice that the deserializer for Company is called for data in Interests.

Expected behavior
A Person object with filled Companies and Interests fields is created.

Versions:

  • OS: Arch Linux (6.2.5-arch1-1)
  • EdgeDB version: 2.12+5454e58
  • EdgeDB CLI version: 2.3.1+ef99779
  • edgedb-net version: 1.1.1
  • .NET version: 7.0.103

Additional context
Does the deserializer use the name property passed to the EdgeDBType annotation? Or is that ignored completely?

Issue with query parameters when using optionals with different underlying types

With the following schema:

module default {
  type Settings {
    required uniqueId: str {
      default := "settings"; 
      constraint exclusive;
    };
    required numberMediaDownloadsAtOnce: int32 {
      default := 2;
      constraint min_value(1);
    };
    required updateAllDay: bool {
      default := true;
    };
  };
}

And the following code:

let queryParams =
    {| numberMediaDownloadsAtOnce = Some 3
       updateAllDay = Some false |}

dbClient.ExecuteAsync(
    """
        update Settings 
        filter .uniqueId = "settings"
        set {
            numberMediaDownloadsAtOnce := <optional int32>$numberMediaDownloadsAtOnce ?? .numberMediaDownloadsAtOnce,
            updateAllDay := <optional bool>$updateAllDay ?? .updateAllDay       
        }         
        """,
    queryParams
)

I get an error of:

Error: EdgeDB.EdgeDBException: Failed to execute query
 ---> System.ArgumentException: Object of type 'Microsoft.FSharp.Core.FSharpOption`1[System.Int32]' cannot be converted to type 'Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]'.
   at System.RuntimeType.CheckValue(Object& value, ParameterCopyBackAction& copyBack, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.MethodBase.CheckArguments(Span`1 copyOfParameters, IntPtr* byrefParameters, Span`1 shouldCopyBack, ReadOnlySpan`1 parameters, RuntimeType[] sigTypes, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at EdgeDB.Utils.FSharp.FSharpOptionInterop..ctor(Object obj)
   at EdgeDB.Utils.FSharp.FSharpOptionInterop.TryGet(Object value, FSharpOptionInterop& option)
   at EdgeDB.Binary.Codecs.ObjectCodec.Serialize(PacketWriter& writer, Object value, CodecContext context)
   at EdgeDB.Binary.Codecs.ObjectCodec.SerializeArguments(PacketWriter& writer, Object value, CodecContext context)
   at EdgeDB.Binary.Codecs.BaseArgumentCodec`1.EdgeDB.Binary.Codecs.IArgumentCodec.SerializeArguments(PacketWriter& writer, Object value, CodecContext context)
   at EdgeDB.CodecExtensions.SerializeArguments(IArgumentCodec codec, EdgeDBBinaryClient client, Object value)
   at EdgeDB.EdgeDBBinaryClient.ExecuteInternalAsync(String query, IDictionary`2 args, Nullable`1 cardinality, Nullable`1 capabilities, IOFormat format, Boolean isRetry, Boolean implicitTypeName, CancellationToken token)
   --- End of inner exception stack trace ---
   at EdgeDB.EdgeDBBinaryClient.ExecuteInternalAsync(String query, IDictionary`2 args, Nullable`1 cardinality, Nullable`1 capabilities, IOFormat format, Boolean isRetry, Boolean implicitTypeName, CancellationToken token)
   at EdgeDB.EdgeDBBinaryClient.ExecuteAsync(String query, IDictionary`2 args, Nullable`1 capabilities, CancellationToken token)
   at EdgeDB.EdgeDBClient.ExecuteAsync(String query, IDictionary`2 args, Nullable`1 capabilities, CancellationToken token)
   at EdgeDB.EdgeDBClient.ExecuteAsync(String query, IDictionary`2 args, Nullable`1 capabilities, CancellationToken token)
   at Program.Pipe #1 input at line [email protected]() in /home/coop/Coding/edgedb-issue-4/Program.fs:line 38

Simple repro: https://github.com/Darkle/edgedb-issue-4

It seems to only happen if the optionals in the queryParams are of different types (eg option<int32> and option<bool>), if they are of the same type the error does not occur.

Versions :

  • OS: Debian Linux
  • EdgeDB version: 3.2+bb17a05.
  • EdgeDB CLI version: 3.4.0+160d07d
  • edgedb-net version: 1.2.3
  • .NET version:7.0.306

[Feature Request] Add support for Some type in parameters

It would be very handy if edgedb-net supported passing in a Some type in query parameters.

Since the edgedb-net client doesn't support passing in Some values, I have to manually unwrap the optionals to get the value or set them to null. Since you can already pass in a None value as a parameter, it would make things a lot simpler if you could also pass in a Some value too. That would mean I wouldn't have to unwrap them or set them to null.

A somewhat contrived example:
This doesnt work:

let getSinglePost (postId: string option) =
  let queryParams = new Dictionary<string, obj>()
  queryParams.Add("postId", postId)

  task {
    try
      let! results =
        dbClient.QuerySingleAsync<Post>(
          "select Post{*} filter .postId = <optional str>$postId",
          queryParams
        )

      return Ok(results)
    with err ->
      Logger.Error(message = "Error with getSinglePost", error = err)
      return Error err
  }

So I have to do:

let getSinglePost (postId: string option) =
  let pId = Option.defaultValue null postId
  let queryParams = new Dictionary<string, obj>()
  queryParams.Add("postId", pId)

  task {
    try
      let! results =
        dbClient.QuerySingleAsync<Post>(
          "select Post{*} filter .postId = <optional str>$postId",
          queryParams
        )

      return Ok(results)
    with err ->
      Logger.Error(message = "Error with getSinglePost", error = err)
      return Error err
  }

Issue with string[] option type

Describe the bug
The client errors when using an array type and an option type together.

Reproduction
With the following schema:

module default {
  type Post {
    myOptionalArrayOfStrings: array<str>;
  }
}

and the following code:

[<CLIMutable>]
type Post =
    { myOptionalArrayOfStrings: string[] option }

dbClient.QuerySingleAsync<Post>("select Post{*} limit 1")

the client errors with the following message:

Error: System.NotSupportedException: Cannot find inner type of Microsoft.FSharp.Core.FSharpOption`1[System.String[]]
   at EdgeDB.TypeExtensions.GetWrappingType(Type type)
   at EdgeDB.Binary.Codecs.TypeVisitor.VisitCodec(ICodec& codec)
   at EdgeDB.Binary.Codecs.CodecVisitor.Visit(ICodec& codec)
   at EdgeDB.Binary.Codecs.TypeVisitor.VisitCodec(ICodec& codec)
   at EdgeDB.Binary.Codecs.CodecVisitor.Visit(ICodec& codec)
   at EdgeDB.ObjectBuilder.BuildResult[TType](EdgeDBBinaryClient client, ICodec codec, Data& data)
   at EdgeDB.EdgeDBBinaryClient.QuerySingleAsync[TResult](String query, IDictionary`2 args, Nullable`1 capabilities, CancellationToken token)
   at EdgeDB.EdgeDBClient.QuerySingleAsync[TResult](String query, IDictionary`2 args, Nullable`1 capabilities, CancellationToken token)
   at EdgeDB.EdgeDBClient.QuerySingleAsync[TResult](String query, IDictionary`2 args, Nullable`1 capabilities, CancellationToken token)
   at Program.Pipe #2 input at line [email protected]() in /home/coop/Coding/edgedb-temp/Program.fs:line 25

Minimal repro here: https://github.com/Darkle/edgedb-temp

Versions (please complete the following information):

  • OS: Debian Linux
  • EdgeDB version: 3.0+e7d38e9
  • EdgeDB CLI version: 3.3.0+071876c
  • edgedb-net version: 1.2.1
  • .NET version: 7.0.305

Type builder always requires __tname__

Summary

__tname__ slows down query performance as it requires computation on the server to get. Currently, the dotnet driver always requests this property:

ImplicitTypeNames = true, // used for type builder
ImplicitTypeIds = true, // used for type builder

Where as the only time these properties are used is when the deserialized type is abstract:

if (!enumerator.Next(out var name, out var value) || name != "__tname__")
throw new ConfigurationException("Type introspection is required for abstract types, this is a bug.");

Proposal

The most effective way to fix this is to build the type deserialization factory before running the query, and check whether or not the factory requires type names to deserialize.

Querybuilder For with selectInserted set to true throws InvalidOperationException

Summary
When trying to do a bulk insert with a for and asking to select the inserted objects the query builder throws an InvalidOperationException because it cant wrap a global query.

Code

        return await QueryBuilder
            .For(factions, faction => QueryBuilder.Insert(faction, true))
            .ExecuteAsync(_edgeDb, token: cancellationToken);

Stacktrace

System.InvalidOperationException: Cannot wrap a global-defined query    at EdgeDB.QueryNodes.SelectNode.FinalizeQuery()    at EdgeDB.QueryNodes.ForNode.<FinalizeQuery>b__3_0(QueryNode x)    at System.Linq.Enumerable.SelectListIterator`2.MoveNext()    at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()    at System.Linq.Enumerable.Aggregate[TSource](IEnumerable`1 source, Func`3 func)    at EdgeDB.QueryNodes.ForNode.FinalizeQuery()    at EdgeDB.QueryBuilder`2.InternalBuild(Boolean includeGlobalsInQuery, Action`1 preFinalizerModifier)    at EdgeDB.QueryBuilder`2.IntrospectAndBuildAsync(IEdgeDBQueryable edgedb, CancellationToken token)    at EdgeDB.QueryBuilder`2.EdgeDB.Interfaces.IMultiCardinalityExecutable<TType>.ExecuteAsync(IEdgeDBQueryable edgedb, Nullable`1 capabilities, CancellationToken token)    at SyzunaPrograms.ED.EDCIA.DataImport.Data.Repositories.FactionRepository.InsertFactionsAsync(Faction[] factions, CancellationToken cancellationToken)

Original issue report in old archived repo: quinchs/EdgeDB.Net#33

Inserting issue. Task Get cancelled without proper error message while seeding large model

Describe the bug
I have wrote code to seed data. But for seeding large model, I am getting error that task get canceled. But don't get why. If I re-run the code again it works without any issue.

Reproduction
Error is random and don't appear all the time so don't know what to include.

let seedAgent = MailboxProcessor.Start(fun inbox  ->
    let client =
        if EnvConfig.isDevelopment then 
            EdgeDBClient()
        else 
            printfn $"conn str : {EnvConfig.dsn}"
            let conn = EdgeDBConnection.FromDSN(EnvConfig.dsn)
            conn.TLSSecurity <- TLSSecurityMode.Insecure
            EdgeDBClient(conn)
    let rec messageLoop() = async {
        let! (replyChannel: AsyncReplyChannel<_>), (msg: Seed.Msg) = inbox.Receive()
        try 
          printfn $"Inserting {msg.Name}"
          let! r = (client.QueryJsonAsync msg.Query) |> Async.AwaitTask
          printfn $"Inserted {msg.Name} ! - {r.Value}"
        with
        | e -> printfn $"Inserting {msg.Name} error - {e.Message}"
        replyChannel.Reply () //To block the application to go out of scope 
        return! messageLoop()
      }

    // start the loop
    messageLoop()
  )

Expected behavior
It should seed data every time.

Versions (please complete the following information):
OS : Linux / MacOS
EdgeDB Version: "2.9+a3d80ef"
EdgeDB CLI : EdgeDB CLI 2.2.6+7eabbf9
EdgeDB.Net.Driver - 1.0.4
Dotnet - 7.0.101

Additional context
Running seed code using dotnet core and F#. Also having server project which also sometime take too much time for first load of data and then becomes comparatively fast.

I need to seed at least 5 to 6 different Models with nested data types and it will become more and more complex and large. So, need some solution that will hold off until execution works fully.

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.