I've setup a basic Web Api demonstrate the use of GraphQL using ASP.Net Core. I've Followed a tutorial, what feels like exactly but am getting an error I don't understand.
I'm using GraphQL for .NET v2.4.0
This is the error:
System.InvalidOperationException: No service for type 'Land.GraphQL.Queries.LandQuery' has been registered.
at at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at at GraphQL.FuncDependencyResolver.Resolve(Type type)
at at GraphQL.FuncDependencyResolver.Resolve[T]()
at Land.GraphQL.LandSchema..ctor(IDependencyResolver resolver) in .../LandSchema.cs:11
I'd be grateful for any light shed :)
Here's the code:
I created a LandType:ObjectGraphType to define the Type:
public class LandType : ObjectGraphType<Entities.Land>
{
public LandType(ILandDataAccess landDataAccess)
{
Name = "Land";
Field(land => land.Id, type: typeof(IdGraphType)).Description("Land Id in LandApi context");
Field(land => land.Apn)
.Description(
"Assessor's Parcel Number (APN) is a unique number that is assigned to each tract of land in a county by the Tax Assessor.");
Field(land => land.Address).Description("");
Field(land => land.ZipCode).Description("");
Field(land => land.City).Description("");
Field(land => land.County).Description("");
Field(land => land.State).Description("");
Field(land => land.Country).Description("");
Field(land => land.GisNumber).Description("");
Field(land => land.AssessedValue).Description("");
Field(land => land.LegalDescription).Description("");
Field(land => land.Acreage, type: typeof(FloatGraphType)).Description("Acreage of Land");
}
}
I created a LandQuery:ObjectGraphType to define the Query:
public class LandQuery : ObjectGraphType
{
public LandQuery(ILandDataAccess dataAccess)
{
Field<ListGraphType<LandType>>(
"Land",
resolve: context => dataAccess.GetLandsAsync());
}
}
I created a LandSchema:Schema to define the Schema:
public class LandSchema : Schema
{
public LandSchema(IDependencyResolver resolver) : base(resolver)
{
Query = resolver.Resolve<LandQuery>();
}
}
I added the service and middleware to the Startup file:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver(
s.GetRequiredService));
services.AddScoped<LandSchema>();
services.AddGraphQL(o => { o.ExposeExceptions = true; })
.AddGraphTypes(ServiceLifetime.Scoped);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseGraphQL<LandSchema>();
}
Edit:
Thanks to the commenters @NateBarbettini and @TonyNgo inspired me to find the answer. Turns out .AddGraphTypes
only searches the calling assembly. My GraphTypes are stored in a referenced Assembly. passing the referenced assembly fixed the problem: .AddGraphTypes(typeof(LandSchema).Assembly, ServiceLifetime.Scoped);
I think you are missing this line of code in your ConfigureServices.
public void ConfigureServices (IServiceCollection services) {
services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver (
s.GetRequiredService));
services.AddScoped<LandSchema>();
services.AddGraphQL(x => {
x.ExposeExceptions = true; //set true only in development mode. make it switchable.
})
.AddGraphTypes (ServiceLifetime.Scoped);
}
If that is not the case you can read my blog here
GraphQL.NET requires you to explicitly register a lot of things. Quick answer: you need to register LandQuery
.
You have:
services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver(s.GetRequiredService));
The IDependencyResolver
acts as a "glue" between the GraphQL.NET system (which needs to request a lot of service types) and ASP.NET Core's service collection (which contains the service types).
The object graph that you're giving to GraphQL.NET starts with LandSchema
, which is correctly registered:
services.AddScoped<LandSchema>();
But look at what LandSchema
is doing!
Query = resolver.Resolve<LandQuery>();
This line asks the IDependencyResolver
for a LandQuery
service type. Your FuncDependencyResolver
is pointing at the ASP.NET Core service collection, so this will succeed if LandQuery
is registered. It isn't, which is why you are getting the error
No service for type 'Land.GraphQL.Queries.LandQuery' has been registered.
To fix it, add a few new lines to ConfigureServices
:
services.AddScoped<LandQuery>();
services.AddScoped<LandType>();
Any type or service (including ILandDataAccess
) required by one of the GraphQL types will need to be registered in ConfigureServices
. If you don't want to hand-register every single GraphQL.NET type, use Scrutor to auto-register types that derive from GraphType
:
// Add all classes that represent graph types
services.Scan(scan => scan
.FromAssemblyOf<LandQuery>()
.AddClasses(classes => classes.AssignableTo<GraphQL.Types.GraphType>())
.AsSelf()
.WithSingletonLifetime());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With