Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for initializing classes/records via constructor #222

Closed
Eli-Black-Work opened this issue Aug 25, 2022 · 9 comments
Closed

Support for initializing classes/records via constructor #222

Eli-Black-Work opened this issue Aug 25, 2022 · 9 comments
Milestone

Comments

@Eli-Black-Work
Copy link

Eli-Black-Work commented Aug 25, 2022

I'm building my own sorting argument:

#nullable enable

class SortArgument
{
    // CS8618 warning: Non-nullable property 'Path' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
    public string Path { get; set; }
    // CS8618 warning: Non-nullable property 'Direction' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
    public string Direction { get; set; }
}
...

schema.AddType<SortArgument>("sortArgument", null).AddAllFields();

schema.UpdateQuery(queryType =>
{
    queryType.ReplaceField(
        "users",
        new
        {
            sort = (SortArgument[]?)null
        },
        (db, args) => ...,
        "..."
    );
});

It'd be cool if EntityGraphQL supported initializing SortArgument by calling its constructor, so that I could easily do additional validation and ensure that Path and Direction are non-null:

class SortArgument
{
    public string Path { get; }
    public string Direction { get; }
   
   public SortArgument(string path = null, string direction = null)
   {
      Path = path ?? throw new ArgumentNullException(nameof(path));

      if(direction == null || direction.Length < 10)
         throw new ArgumentException("Direction must be at least 10 letters long", nameof(direction));

      Direction = direction;
   }
}

Just a suggestion 🙂 Thanks for the great library! ^_^

@lukemurray
Copy link
Collaborator

If you could split the required request into a seperate issue please we can follow that there. Note it will likely (if the reflection stuff lets us actually use it) come after 3.1 support ends in December as I plan to keep support for 3.1 for new releases until MS drop support.

@lukemurray
Copy link
Collaborator

Small thing in your example.

schema.AddType<SortArgument>("sortArgument", null).AddAllFields();

You are using SortArgument as an input type. You will need to use

schema.AddInputType<SortArgument>("sortArgument", null).AddAllFields();

@Eli-Black-Work Eli-Black-Work changed the title Support for initializing classes/records via constructor, and support for C# 11 required modifier Support for initializing classes/records via constructor Aug 26, 2022
@Eli-Black-Work
Copy link
Author

Eli-Black-Work commented Aug 26, 2022

Sounds good; I've split the request for supporting the required modifier out into #227 🙂

@Eli-Black-Work
Copy link
Author

Eli-Black-Work commented Aug 26, 2022

Small thing in your example.

schema.AddType<SortArgument>("sortArgument", null).AddAllFields();

You are using SortArgument as an input type. You will need to use

schema.AddInputType<SortArgument>("sortArgument", null).AddAllFields();

Thanks! 🙂

Interestingly, AddType() works fine, too, but I suppose if I use AddInputType(), then if I accidentally put a SortArgument object in my DbContext, I'd get an error about the type not being supported, which would be nice 🙂

I wonder if it'd help discoverability to merge AddType() and AddInputType() into a single method that takes an enum flagging whether the type is for use in the input, schema, or both.

@Eli-Black-Work
Copy link
Author

Eli-Black-Work commented Aug 31, 2022

By the way, I can't find any difference between these snippets of code:

// Works
schema.AddType<User>("users", null).AddAllFields();
schema.AddType<SortArgument>("sortArgument", null).AddAllFields();

and

// Also works
schema.AddInputType<User>("users", null).AddAllFields();
schema.AddInputType<SortArgument>("sortArgument", null).AddAllFields();

@lukemurray
Copy link
Collaborator

You highlight something

  1. When EntityGraphQL is looking up types it doesn't care if it is an input or non-input type
  2. The difference is in introspection and schema output - it will out the type types as Input or not. Tools (that compile, or check queries etc) will complain if you try to use a non-input type as an input. As the spec does not allow that (input types can't have arguments etc.)

Question is, I guess EnittyGraphQL should correctly raise an error if it see you do this?

@Eli-Black-Work
Copy link
Author

Honestly... I'm not sure 🙂

In theory, should it be allowed to use a type as both an input type and a normal type?

For example:

class Permission
{
   public string Id { get; set; }
}

Then we use User as both the filter and a piece of data:

{
   users(permission: { id: "test" })
   {
      permission
      {
         id
      }
   }
}

Probably not a case that would be worth supporting? 🙂

@lukemurray
Copy link
Collaborator

In an example like that it makes sense. The GraphQL spec does not support that. So other tools etc may complain.

The spec states in simple terms

  • each type needs a unique name
  • Types used as arguments are Input Types
  • Input types are different as they have more restrictions - no fields with arguments etc.

@Eli-Black-Work
Copy link
Author

In that case, I think it'd be appropriate to throw an error if the type was added with AddType() but is used as an input type, and vice versa 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants