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);
});
}
}
}