chkimes / graphql-net

Convert GraphQL to IQueryable
MIT License
892 stars 86 forks source link

Question - Mutation for adding values to the system? #39

Closed benhysell closed 8 years ago

benhysell commented 8 years ago

If one wanted to add a new User from the test project, to the system would you do it via a mutation? Feels like I'm missing something fundamental and I'm not kicking anything up on Google.

Can one do:

schema.AddMutation("addUser",
               new
               {
                   name = string.Empty,                   
                   active = false
               },
               (db, args) => db.Users.AsQueryable(),
               (db, args) =>
               {
                   var newUser = new User()
                   {
                       Name = args.name,
                       Active = args.active
                   };
                   db.Users.Add(newUser);
                   db.SaveChanges();
               }
               );
 var gql = CreateDefaultContext();
 var results = gql.ExecuteQuery("mutation { addUser(name:\"Bob\", active: true) { id } }");

Stack trace

System.ArgumentException : Expression of type 'System.Linq.IQueryable`1[Tests.EF.EntityFrameworkMsSqlTests+User]' cannot be used for parameter of type 'Tests.EF.EntityFrameworkMsSqlTests+User'
   at System.Linq.Expressions.Expression.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arg, ParameterInfo pi)
   at System.Linq.Expressions.Expression.ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ReadOnlyCollection`1& arguments)
   at System.Linq.Expressions.Expression.Invoke(Expression expression, IEnumerable`1 arguments)
   at GraphQL.Net.Executor`1.Execute(TContext context, GraphQLField field, ExecSelection`1 query) in C:\Jobs\graphql-net\GraphQL.Net\Executor.cs:line 70
   at GraphQL.Net.Executor`1.Execute(GraphQLSchema`1 schema, GraphQLField field, ExecSelection`1 query) in C:\Jobs\graphql-net\GraphQL.Net\Executor.cs:line 17
   at GraphQL.Net.GraphQL`1.ExecuteQuery(String queryStr) in C:\Jobs\graphql-net\GraphQL.Net\GraphQL.cs:line 44
   at Tests.EF.EntityFrameworkMsSqlTests.SimpleMutationAddUser() in C:\Jobs\graphql-net\Tests.EF\EntityFrameworkMsSqlTests.cs:line 211
chkimes commented 8 years ago

Hey Ben,

I can take a better look at this when I get home, however I suspect that the issue has to do with the type you're returning from the third argument (db, args) => db.Users.AsQueryable().

This argument is the query that is executed after a mutation executes. You're currently using the function that expects a single entity type (AddMutation) instead of the function that expects a list type (AddListMutation). Depending on what you want the results to return, you can do one of two things:

  1. Change the function from AddMutation to AddListMutation. This will return a list of all of your users after executing the mutation.

    or

  2. Change the executing function to return a single user. That is, change db.Users.AsQuerable() to db.Users.LastOrDefault() (or something similar). This will return the last user from the table.
chkimes commented 8 years ago

This issue made me think about something that I hadn't considered before, which is that there's no way to pass info to the query returned from a mutation based on the results of the mutation. For example, this could be used if you wanted to execute a query that returned the exact user that was created after a mutation.

I would need to make some changes to mutation execution and add an overload in order to support this. Example syntax would look something like:

schema.AddMutation("addUser",
               new
               {
                   name = string.Empty,                   
                   active = false
               },
               (db, args) =>
               {
                   var newUser = new User()
                   {
                       Name = args.name,
                       Active = args.active
                   };
                   db.Users.Add(newUser);
                   db.SaveChanges();
                   return newUser.Id;   // return information from the mutation
               },
                          // pass information to query as a third parameter
               (db, args, id) => db.Users.FirstOrDefault(u => u.Id == id)
               );

Note that in this example I've re-ordered the parameters to better represent their order of execution. I think this will be a likely breaking change in the next version.

benhysell commented 8 years ago

My initial issue was the query itself, @ckimes89 you were correct. Cleaned that up, and ran as expected.

Thank you again for all of the help while I muddle through!