|
|
@@ -0,0 +1,54 @@ |
|
|
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 |
|
|
{ |
|
|
/// <summary> |
|
|
/// This attribute allows us to tap into the field resolution middleware |
|
|
/// so that we can dynamically create an <see cref="IServiceScope"/> just for |
|
|
/// this field resolver method, and create an <see cref="IMediator"/> 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 <see cref="IMediator"/> 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. |
|
|
/// </summary> |
|
|
[AttributeUsage(AttributeTargets.Method)] |
|
|
public class UseResolverScopedMediatorAttribute : ObjectFieldDescriptorAttribute |
|
|
{ |
|
|
private static readonly string _injectedArgumentName = "mediator"; |
|
|
|
|
|
private static readonly HashSet<string> _localSchemaNames = new HashSet<string> |
|
|
{ |
|
|
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<Microsoft.AspNetCore.Http.IHttpContextAccessor>().HttpContext.RequestServices.CreateScope() |
|
|
: context.Service<IServiceProvider>().CreateScope(); |
|
|
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>(); |
|
|
context.ModifyScopedContext(c => c.SetItem(_injectedArgumentName, mediator)); |
|
|
|
|
|
await next(context); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |