Reputation: 7724
I am trying to retrieve the value of a property through an Expression
. However, when I run the code, I get the exception
Unhandled exception. System.InvalidOperationException: variable 'teacher' of type 'GraphQlMcve.Program+Teacher' referenced from scope '', but it is not defined
This occurs in the method below when I try to compile the expression.
protected FieldBuilder<T, object> PupilListField(string name,
Expression<Func<T, IReadOnlyCollection<Pupil>>> pupils)
{
return BaseAugmentedPupilListQuery(name)
.Resolve(context =>
{
IEnumerable<Pupil> pupilList =
Expression.Lambda<Func<IReadOnlyCollection<Pupil>>>(pupils.Body).Compile()();
return AugmentedPupilListQueryBaseResolver(context, pupilList);
});
}
The expression I am using is teacher => teacher.Pupils
. Why is this happening?
A runnable example is below.
The below code example uses the GraphQL NuGet package Install-Package GraphQL -Version 2.4.0
.
using GraphQL.Builders;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using GraphQL;
using GraphQL.Types;
namespace GraphQlMcve
{
internal class Program
{
private static void Main()
{
const string query = @"{ teachers { id, name, pupils(id: ""2"") { id, name } } }";
Schema schema = new Schema { Query = new SchoolQuery() };
Console.WriteLine(schema.Execute(_ => { _.Query = query; _.ExposeExceptions = true; _.ThrowOnUnhandledException = true; }));
}
private class Pupil
{
public string Id { get; set; }
public string Name { get; set; }
}
private class PupilType : ObjectGraphType
{
public PupilType()
{
Field<NonNullGraphType<IdGraphType>>(nameof(Pupil.Id));
Field<StringGraphType>(nameof(Pupil.Name));
}
}
private class Teacher
{
public string Id { get; set; }
public string Name { get; set; }
public List<Pupil> Pupils { get; set; }
}
private class TeacherType : BaseEntityGraphType<Teacher>
{
public TeacherType()
{
Field<NonNullGraphType<IdGraphType>>(nameof(Teacher.Id));
Field<StringGraphType>(nameof(Teacher.Name));
PupilListField(nameof(Teacher.Pupils), teacher => teacher.Pupils);
}
}
private class SchoolQuery : BaseEntityGraphType
{
public SchoolQuery()
{
List<Pupil> pupils = new List<Pupil>
{
new Pupil { Id = "1", Name = "Sarah" },
new Pupil { Id = "2", Name = "Adam" },
new Pupil { Id = "3", Name = "Gill" },
};
List<Teacher> teachers = new List<Teacher> { new Teacher { Id = "4", Name = "Sarah", Pupils = pupils} };
PupilListField("pupils", pupils);
Field<ListGraphType<TeacherType>>(
"teachers",
arguments: new QueryArguments(
new QueryArgument<IdGraphType> { Name = "id" }
),
resolve: context => teachers
);
}
}
private abstract class BaseEntityGraphType<T> : ObjectGraphType<T>
{
protected FieldBuilder<T, object> PupilListField(string name,
Expression<Func<T, IReadOnlyCollection<Pupil>>> pupils)
{
return BaseAugmentedPupilListQuery(name)
.Resolve(context =>
{
IEnumerable<Pupil> pupilList =
Expression.Lambda<Func<IReadOnlyCollection<Pupil>>>(pupils.Body).Compile()();
return AugmentedPupilListQueryBaseResolver(context, pupilList);
});
}
protected FieldBuilder<T, object> PupilListField(string name, IReadOnlyCollection<Pupil> pupils)
{
return BaseAugmentedPupilListQuery(name)
.Resolve(context => AugmentedPupilListQueryBaseResolver(context, pupils));
}
private FieldBuilder<T, object> BaseAugmentedPupilListQuery(string name)
{
return Field<ListGraphType<PupilType>>()
.Name(name)
.Description("")
.Argument<IdGraphType>("id", "");
}
private static IEnumerable<Pupil> AugmentedPupilListQueryBaseResolver(
ResolveFieldContext<T> context,
IEnumerable<Pupil> pupils)
{
string id = context.GetArgument<string>("id");
return string.IsNullOrWhiteSpace(id) ? pupils : pupils.Where(pupil => pupil.Id == id);
}
}
private abstract class BaseEntityGraphType : BaseEntityGraphType<object> { }
}
}
Upvotes: 0
Views: 250
Reputation: 10790
IEnumerable<Pupil> pupilList = Expression.Lambda<Func<IReadOnlyCollection<Pupil>>>(pupils.Body).Compile()();
This part of code is wrong. You pick a part of expression and you compile it. The part you compile have a teacher expression and you break the relation with it. What you can do is : Compile the main expression which is you already passed in your function.
var pupilList = puplis.Compile()(/* you need to pass here an actual object */);
When you compile your expression you create a function which processes an object but you are not passing the object. In your teachers example it has to be a teacher object.
Upvotes: 1