conventions icon indicating copy to clipboard operation
conventions copied to clipboard

Support for scoped services being injected into schema types constructor

Open ampersandd opened this issue 6 years ago • 7 comments

For now it is possible to set own dependency injector for executor using WithDependencyInjector method. Then using InjectAttribute for parameters of Query methods we can inject scoped services using provided dependency container. But what if we need to share scoped services within whole Query class injecting them into constructor? For now services injected into constructor of schema type are resolved using typeResolutionDelegate provided into GraphQLEngine constructor. Engine mainly constructed one time and that's why there is no option to provide it with scoped services resolution (used in Microsoft ASP.NET Core dependency container). Is there any solution to use injector provided for executor by WithDependencyInjector method to resolve services for injection into constructor of schema types?

ampersandd avatar Feb 28 '19 08:02 ampersandd

I guess it is not possible at the moment. The source is taken of the GraphQL.Dotnet infos this library gets. Maybe it would be possible to hand in an custom FieldResolver with some modifications.

The logic which that implementation would need to adjust is the method private object GetSource(GraphFieldInfo fieldInfo, IResolutionContext context) in FieldResolver. It could always trigger source = context.DependencyInjector?.Resolve(declaringType.GetTypeInfo()) ?? fieldInfo.SchemaInfo.TypeResolutionDelegate(declaringType);

To do this the engine would need some kind of method where you can handin the resolver or maybe even better a strategy for getting the source of a context or a fieldinfo.

dmg-hamann avatar Apr 10 '19 10:04 dmg-hamann

I guess it is even way easier and without any code change. The constructor of GraphQLEngine accepts a delegate for type resolution. Just add a delegate like (type) => IoCContainer.Resolve(type) .

It worked for me in a very small test. Maybe it solves your issue.

dmg-hamann avatar Apr 10 '19 11:04 dmg-hamann

@dmg-hamann, in you solution IoCContainer should be singleton/static instance, which do not allow usage of scoped (per request or manually created scopes) or child containers without hacks (e.g. marking with [ThreadStatic] which will not cover all use cases)

BilyachenkoOY avatar Apr 26 '19 06:04 BilyachenkoOY

Anything else which is resolved by the container can be scoped. I dont know how an asp service or prims currently works but at some point you register your dependencies at the container. Isn't the container always a singleton? Or where are the dependencies store?

dmg-hamann avatar Apr 26 '19 06:04 dmg-hamann

Yes the container (dependencies storage) is singleton. But some services are scoped which means they will be created only inside scope (create manually or by ASP.NET per HTTP request) and they can be recreated inside another scope.

// this will not work
// var test = _serviceProvider.GetService<ISomeScopedService>();

var engine= new GraphQLEngine(_serviceProvider.GetService)
using (var scope = _serviceProvider.CreateScope())
{
    // e.g. here is how scoped service can be retrieved
    // var service = scope.ServiceProvider.GetService<ISomeScopedService>();

    var exec = engine.NewExecutor()
        // this allow to receive scoped service in Query methods but not in Query  ctor
        .WithDependencyInjector(new DependencyInjector(scope.ServiceProvider)) 
        .WithQueryString(query.Query);

    var result = await exec.Execute();
}

BilyachenkoOY avatar Apr 30 '19 06:04 BilyachenkoOY

Hmm... I was going to try to fix this. But I've just rechecked the issue and found that typeResolutionDelegate is not invoked when IDependencyInjector is specified for executor.

BilyachenkoOY avatar May 31 '19 12:05 BilyachenkoOY

Also see graphql-dotnet/graphql-dotnet#1151

sungam3r avatar May 31 '19 12:05 sungam3r