A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://graphql-dotnet.github.io/docs/getting-started/dependency-injection/ below:

GraphQL .NET

Dependency Injection

GraphQL.NET supports dependency injection through a IServiceProvider interface that is passed to the Schema class. Internally when trying to resolve a type the library will call the methods on this interface.

The library resolves a GraphType only once and caches that type for the lifetime of the Schema.

The default implementation of IServiceProvider uses Activator.CreateInstance. Activator.CreateInstance requires that an object have a public parameterless constructor.

public sealed class DefaultServiceProvider : IServiceProvider
{
    public object GetService(Type serviceType)
    {
        if (serviceType == null)
            throw new ArgumentNullException(nameof(serviceType));

        try
        {
            return Activator.CreateInstance(serviceType);
        }
        catch (Exception exception)
        {
            throw new Exception($"Failed to call Activator.CreateInstance. Type: {serviceType.FullName}", exception);
        }
    }
}

You can override the default implementation by passing a IServiceProvider to the constructor of your Schema.

public class StarWarsSchema : GraphQL.Types.Schema
{
    public StarWarsSchema(IServiceProvider provider, StarWarsQuery query, StarWarsMutation mutation)
        : base(provider)
    {
        Query = query;
        Mutation = mutation;
    }
}

How you integrate this into your system will depend on the dependency injection framework you are using. FuncServiceProvider is provided for easy integration with multiple containers.

Dependency Injection Registration Helpers

GraphQL.NET provides an IGraphQLBuilder interface which encapsulates the configuration methods of a dependency injection framework, to provide an abstract method of configuring a dependency injection framework to work with GraphQL.NET. This interface is provided through a configuration delegate from a DI-provider-specific setup method (typically called AddGraphQL()), at which point you can call extension methods on the interface to configure this library. A simple example is below:

services.AddGraphQL(builder => builder
    .AddSystemTextJson()
    .AddSchema<MySchema>());

The interface also allows configuration of the schema during initialization, and configuration of the execution at runtime. In this manner, adding middleware, for example, is as simple as calling .AddMiddleware<MyMiddlware>() and does not require the middleware to be added into the schema configuration.

The AddGraphQL() method will register default implementations of the following services within the dependency injection framework:

These generic graph types are also registered:

A list of the available extension methods is below:

Method Description / Notes Library AddAutoClrMappings Configures unmapped CLR types to use auto-registering graph types AddAutoSchema Registers a schema based on CLR types AddClrTypeMappings Scans the specified assembly for graph types intended to represent CLR types and registers them within the schema AddComplexityAnalyzer Enables the complexity analyzer and configures its options AddDataLoader Registers classes necessary for data loader support GraphQL.DataLoader AddDocumentCache<> Registers the specified document caching service AddDocumentExecuter<> Registers the specified document executer; useful when needed to change the execution strategy utilized AddDocumentListener<> Registers the specified document listener and configures execution to use it AddErrorInfoProvider Registers a custom error info provider or configures the default error info provider AddExecutionStrategy Registers an ExecutionStrategyRegistration for the selected execution strategy and operation type AddExecutionStrategySelector Registers the specified execution strategy selector AddFederation Registers the federation types and configures the schema to support Apollo Federation AddGraphTypes Scans the specified assembly for graph types and registers them within the DI framework AddGraphTypeMappingProvider Registers a graph type mapping provider for unmapped CLR types AddLegacyComplexityAnalyzer Enables the v7 complexity analyzer and configures its options AddNewtonsoftJson Registers the serializer that uses Newtonsoft.Json as its underlying JSON serialization engine GraphQL.NewtonsoftJson AddSchema<> Registers the specified schema AddSchemaVisitor<> Registers the specified schema visitor and configures it to be used at schema initialization AddSelfActivatingSchema<> Registers the specified schema which will create instances of unregistered graph types during initialization AddSerializer<> Registers the specified serializer AddSystemTextJson Registers the serializer that uses System.Text.Json as its underlying JSON serialization engine GraphQL.SystemTextJson AddUnhandledExceptionHandler Configures the unhandled exception handler AddValidationRule<> Registers the specified validation rule and configures it to be used at runtime ConfigureExecution Configures execution middleware to monitor or modify both options and the result ConfigureExecutionOptions Configures execution options at runtime ConfigureSchema Configures schema options when the schema is initialized Configure<TOptions> Used by extension methods to configures an options class within the DI framework UseApolloTracing Registers and enables metrics depending on the supplied arguments, and adds Apollo Tracing data to the execution result UseAutomaticPersistedQueries Enables Automatic Persisted Queries support GraphQL.MemoryCache UseMemoryCache Registers the memory document cache and configures its options GraphQL.MemoryCache UseMiddleware<> Registers the specified middleware and configures it to be installed during schema initialization UsePersistedDocuments Registers the persisted document handler and configures its options UseTelemetry Creates telemetry events based on the System.Diagnostics.Activity API, primarily for use with OpenTelemetry .NET 5+ ValidateServices Verifies that all injected services can be created during GraphQL field execution WithTimeout Configures the execution timeout

The above methods will register the specified services typically as singletons unless otherwise specified. Graph types and middleware are registered as transients so that they will match the schema lifetime. So with a singleton schema, all services are effectively singletons.

Calls to ConfigureExecutionOptions and methods that start with Add will execute first, in the order they appear, followed by calls to ConfigureExecution and methods that start with Use. The order of the calls may be important. For instance, calling UseMemoryCache prior to UseAutomaticPersistedQueries would result in the memory cache being unable to cache any APQ queries.

Custom IGraphQLBuilder extension methods typically rely on the Services property of the builder in order to register services with the underlying dependency injection framework. The Services property returns a IServiceRegister interface which has these methods:

Method Description Register Registers a service within the DI framework replacing existing registration if needed TryRegister Registers a service within the DI framework if it has not already been registered

To use the AddGraphQL method, you will need to install the proper nuget package for your DI provider. See list below:

DI Provider Nuget Package Microsoft.Extensions.DependencyInjection GraphQL.MicrosoftDI ASP.NET Core

See this example.

Microsoft.Extensions.DependencyInjection package used in ASP.NET Core already has support for resolving IServiceProvider interface so no additional settings are required - just add your required dependencies:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDocumentExecuter, DocumentExecuter>();
    services.AddSingleton<IGraphQLSerializer, GraphQLSerializer>();
    services.AddSingleton<StarWarsData>();
    services.AddSingleton<StarWarsQuery>();
    services.AddSingleton<StarWarsMutation>();
    services.AddSingleton<HumanType>();
    services.AddSingleton<HumanInputType>();
    services.AddSingleton<DroidType>();
    services.AddSingleton<CharacterInterface>();
    services.AddSingleton<EpisodeEnum>();
    services.AddSingleton<ISchema, StarWarsSchema>();
}

To avoid having to register all of the individual graph types in your project, you can import the GraphQL.MicrosoftDI NuGet package package and utilize the SelfActivatingServiceProvider wrapper as follows:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<ISchema, StarWarsSchema>(services => new StarWarsSchema(new SelfActivatingServiceProvider(services)));
}

If you previously pulled in your query, mutation and/or subscription classes via dependency injection, you will need to manually pull in those dependencies from the SelfActivatingServiceProvider via GetRequiredService as follows:

public class StarWarsSchema : Schema
{
    public StarWarsSchema(IServiceProvider serviceProvider) : base(serviceProvider)
    {
        Query = serviceProvider.GetRequiredService<StarWarsQuery>();
        Mutation = serviceProvider.GetRequiredService<StarWarsMutation>();
    }
}

No other graph types will need to be registered. Graph types will only be instantiated once, during schema initialization as usual. Graph types can also pull in any services registered with dependency injection as usual.

Note that if any of the graph types directly or indirectly implement IDisposable, be sure to register those types with your dependency injection provider, or their Dispose methods will not be called. Any dependencies of graph types that implement IDisposable will be disposed of properly, regardless of whether the graph type is registered within the service provider.

You can also use the .AddGraphTypes() builder method to scan the calling or specified assembly for classes that implement IGraphType and register them all as transients within the service provider. Mark your class with DoNotRegisterAttribute if you want to skip registration.

Nancy TinyIoCContainer
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
    base.ConfigureApplicationContainer(container);

    container.Register((c, overloads) =>
    {
        return new StarWarsSchema(new FuncServiceProvider(c.Resolve));
    });
}
SimpleContainer
var container = new SimpleContainer();
container.Singleton(new StarWarsSchema(new FuncServiceProvider(container.Get)));
Autofac
protected override void Load(ContainerBuilder builder)
{
    builder
      .Register(c => new FuncServiceProvider(c.Resolve<IComponentContext>().Resolve))
      .As<IServiceProvider>()
      .InstancePerDependency();
}
Castle Windsor
public void Install(IWindsorContainer container, IConfigurationStore store)
{
    container.Register(
      Component
        .For<IServiceProvider>()
        .UsingFactoryMethod(k => k.Resolve)
    );
}
Schema Service Lifetime

Most dependency injection frameworks allow for specifying different service lifetimes for different services. Although they may have different names with different frameworks, the three most common lifetimes are as follows:

It is highly recommended that the schema is registered as a singleton. As all graph types are constructed at the

same time as the schema, all graph types will effectively have a singleton lifetime, regardless of how it is registered with the DI framework. This is most performant approach. Having a scoped schema can degrade performance by a huge margin. For instance, even a small schema execution can slow down by 100x, and much more with a large schema.

Scoped lifetime can be used to allow the schema and all its graph types access to the current DI scope. This is not recommended; please see Scoped Services below. With scoped schemas, it is required that all its graph types are registered within the DI framework as scoped or transient services.

Transient lifetime is also not recommended due to performance degradation. For schemas having a transient lifetime, it is required that all its graph types are also registered within the DI framework as transient services.

Scoped services with a singleton schema lifetime

For reasons described above, it is recommended that the schema is registered as a singleton within the dependency injection framework. However, this prevents including scoped services within the constructor of the schema or your custom graph types.

To use scoped services (e.g. HttpContext scoped services in ASP.NET Core) you will need to pass the scoped service provider into the ExecutionOptions.RequestServices property. Then within any field resolver or field middleware, you can access the IResolveFieldContext.RequestServices property to resolve types via the scoped service provider. Typical integration with ASP.NET Core might look like this:

var result = await _executer.ExecuteAsync(options =>
{
    options.Schema = _schema;
    options.Query = request.Query;
    options.Variables = _serializer.Deserialize<Inputs>(request.Variables); 
    options.RequestServices = context.RequestServices;
});

You could then call scoped services from within field resolvers as shown in the following example:

public class StarWarsQuery : ObjectGraphType
{
    public StarWarsQuery()
    {
        Field<DroidType>("hero")
            .Resolve(context => context.RequestServices.GetRequiredService<IDroidRepo>().GetDroid("R2-D2"));
    }
}
Thread safety with scoped services

When using scoped services, be aware that most scoped services are not thread-safe. Therefore you will likely need to use the SerialExecutionStrategy execution strategy, or write code to create a service scope for the duration of the execution of the field resolver that requires a scoped service. For instance, with Entity Framework Core, typically the database context is registered as a scoped service and obtained via dependency injection. To continue to use the database context in the same manner with a singleton schema, you would need to use a serial execution strategy, or create a scope within each field resolver that requires database access, as shown in the following example:

public class StarWarsQuery : ObjectGraphType
{
    public StarWarsQuery()
    {
        Field<DroidType>("hero")
            .Resolve(context =>
            {
                using var scope = context.RequestServices.CreateScope();
                var services = scope.ServiceProvider;
                return services.GetRequiredService<MyDbContext>().Droids.Find(1);
            });
    }
}

There are classes to assist with this within the GraphQL.MicrosoftDI NuGet package. Sample usage is as follows:

public class MyGraphType : ObjectGraphType<Category>
{
    public MyGraphType()
    {
        Field("Name").Resolve(context => context.Source.Name);
        Field<ListGraphType<ProductGraphType>>("Products")
            .ResolveScopedAsync(context => {
                var db = context.RequestServices.GetRequiredService<MyDbContext>();
                return db.Products.Where(x => x.CategoryId == context.Source.Id).ToListAsync();
            });
    }
}

In this case context.RequestServices will be an IServiceProvider in a newly created scope.

Be aware that using the service locator in this fashion described in this section could be considered an Anti-Pattern. See Service Locator is an Anti-Pattern. However, the performance benefits far outweigh the anti-pattern idealogy, when compared to creating a scoped schema.

Within the GraphQL.MicrosoftDI package, there is also a builder approach to adding scoped dependencies. This makes for a concise and declarative approach. Each field clearly states the services it needs and thereby, the anti-pattern argument does not apply anymore.

public class MyGraphType : ObjectGraphType<Category>
{
    public MyGraphType()
    {
        Field("Name").Resolve(context => context.Source.Name);
        Field<ListGraphType<ProductGraphType>>("Products")
            .Resolve()
            .WithScope() 
            .WithService<MyDbContext>()
            .ResolveAsync(async (context, db) => await db.Products.Where(x => x.CategoryId == context.Source.Id).ToListAsync());
    }
}

Another approach to resolve scoped services is to use the SteroidsDI project, as described below.

Using SteroidsDI

To use SteroidsDI with ASP.NET Core, add Defer<> and IScopeProvider in your Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    ...

    
    services.AddDefer();

    
    
    services.AddHttpScope();

    ...
}

Then in your query graph types you can request services using Defer<T> to be injected via DI, which will be evaluated at runtime to their relevant Scoped services, T has been registered with a Scoped DI lifetime:

public class StarWarsQuery : ObjectGraphType
{
  
  public StarWarsQuery(Defer<IDroidRepo> repoFactory)
  {
    Field<DroidType>("hero")
        
        .Resolve(context => repoFactory.Value.GetDroid("R2-D2"));
  }
}
  1. Add Defer<T> to be injected by the dependency injection container. This is a factory which upon calling Defer.Value will resolve the requested service using any currently registered scope provider (e.g. AspNetCoreHttpScopeProvider)
  2. Use the Defer<T> factory class to resolve the requested dependency using any currently registered scope provider. In our case it will attempt to use the IHttpContextAccessor.HttpContext.RequestServices which is the ASP.NET Core Scoped IServiceProvider in order to resolve the dependency.

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4