GraphQL with .NET Core (Part - III: Dependency Injection)
Code samples used in this blog series have been updated to latest version of .NET Core (5.0.4) and GraphQL-Dotnet (4.2.0). Follow this link to get the updated samples.
The letter 'D' in SOLID stands for Dependency inversion principle
. The principle states,
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions. Wikipedia
Newing up instances cause strict coupling between code modules. To keep them decoupled from each other, we follow the 'Dependency Inversion Principle'. In this way the modules are not dependent on each other's concrete implementation rather they are dependent upon abstractions e.g. interfaces.
An abstraction can have many many implementations. So, whenever we encounter an abstraction, there should be some way of passing a specific implementation to that. A class is held responsible for this kind of servings and it should be configured in such a way so. We call it a dependency injection container.
ASP.Net Core has a built-in dependency injection container. It's simple and can serve our purpose very well. Not only it can be configured to serve implementations to abstractions but it also can control the lifetime of the created instances.
Instead of using the concrete DocumentWriter
and DocumentExecutor
, we can use their abstractions i.e. IDocumentWriter
and DocumentExecutor
. And for this purpose we have to configure the built-in dependency container as follows,
They both have Singleton
lifetimes since we want to create instances of them only once and use it for the rest of the application's lifecycle.
For the GameStoreQuery
, we don't have any abstraction. We will just use the raw implementation but with a Transient
lifetime so that instance on the type is created per request,
The schema contains the query
and later in the series it will also have mutation
and subscription. We better make a separate class for it. The class is extended from the Schema
type from GraphQL.Types
. GameStoreSchema
is made constructor injectable so that we can inject and pass down IServiceProvider
to the base type i.e. Schema
. IServiceProvider.GetRequiredService<T>()
is an utility function available in GraphQL.Utilities
that uses Activator.CreateInstance()
to create an instance of the generic type.
Finally, it's time to configure the GameStoreSchema
in the ConfigureServices
method as following,
The ISchema
is coming from the GraphQL.Types
as well.
Now, we can shift the middleware code to its middleware class (special type having a `Invoke/InvokeAsync(HttpContext httpContext)` method. Following is the middleware class named GraphQLMiddleware
,
Notice, how we replace all the concrete type initializations with abstractions and make our code loosely coupled. Every dependency injectable service is injected via the constructor (constructor injection) at this moment. A middleware sits in the request pipeline and have a Singleton
lifetime. But it doesn't mean that we can't work with services with Transient
or Scoped
lifetime. That's where Invoke/InvokeAsync
method comes into play where we can inject services of Transient
and Scoped
lifetime. To know more about DI in ASP.NET Core middleware, refer to this link.
The last but not least, we must attach the middleware in the application startup pipeline. IApplicationBuilder
has an extension method called UseMiddleware
which is used to attach middleware classes. So, the final look of the Configure
method is as follows,
This next section is all about refactoring and making the GraphQLMiddleware
more configurable.
AddGraphQL
is an extension method that takes various options like a configurable service endpoint. The type of the object we are using to pass the options is named GraphQLOptions
. We can pass options from the ConfigureServices()
as follows,
For the time being, GraphQLOptions
only have a single property called EndPoint
.
To use the configuration options in the GraphQLMiddleware
, we inject the constructor with IOptions<GraphQLOptions>
UseGraphQL
is another one that is just a wrapper around app.UseMiddleware()
to make it look more meaningful like app.UseGraphQL()
.