using System; using System.Collections.Generic; using System.Reflection; using HotChocolate.Types; using HotChocolate.Types.Descriptors; using MediatR; using Microsoft.Extensions.DependencyInjection; namespace MyCompany.GraphQL { /// /// This attribute allows us to tap into the field resolution middleware /// so that we can dynamically create an just for /// this field resolver method, and create an instance inside it. /// /// This is used to solve a problem with using EF Core's DbContextPool, which scopes /// DbContext instances per http request and will cause HotChocolate resolvers running /// in parallel to execute on the same DbContext, which isn't allowed. /// /// By resolving our instance inside an isolated scope, /// we ensure that if it depends itself on a DbContext that it will be unique in that scope /// and there won't be multiple threads using it simultaneously. /// [AttributeUsage(AttributeTargets.Method)] public class UseResolverScopedMediatorAttribute : ObjectFieldDescriptorAttribute { private static readonly string _injectedArgumentName = "mediator"; private static readonly HashSet _localSchemaNames = new HashSet { SchemaNames.Content, SchemaNames.ShopTyre, SchemaNames.Task }; public override void OnConfigure( IDescriptorContext descriptorContext, IObjectFieldDescriptor descriptor, MemberInfo member) { descriptor.Use(next => async context => { // Temp workaround for: https://github.com/ChilliCream/hotchocolate/issues/2246 using var scope = _localSchemaNames.Contains(context.Schema.Name) ? context.Service().HttpContext.RequestServices.CreateScope() : context.Service().CreateScope(); var mediator = scope.ServiceProvider.GetRequiredService(); context.ModifyScopedContext(c => c.SetItem(_injectedArgumentName, mediator)); await next(context); }); } } }