I'm building a GraphQL API in Asp Net Core 3.1. I'm trying to add a subscription that send a new entity to subscriber when it is added. When I execute the subscription from the /ui/playground the handshake with server seems to be successed:
GraphQL http request websocket:
When I execute the mutation that adds a review it successed and create a record on the db, but the above screen reamins as it is, no updates, no data receveived from the socket.
This is the mutation:
Field<ReviewType>(
"createReview",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<ReviewInput>>
{
Name = "reviewInput"
}
),
resolve: context =>
{
var review = context.GetArgument<Review>("reviewInput");
var rev = reviewService.Add(review);
return rev;
}
);
ShopSubscrition.cs
public partial class ShopSubscription : ObjectGraphType
{
private readonly IReviewService _reviewService;
public ShopSubscription(IReviewService reviewService)
{
_reviewService = reviewService;
AddField(new EventStreamFieldType
{
Name = "reviewAdded",
Type = typeof(ReviewType),
Resolver = new FuncFieldResolver<Review>((context) => context.Source as Review),
Subscriber = new EventStreamResolver<Review>((context) => _reviewService.ReviewAdded())
});
}
}
ReviewService.cs
public class ReviewService : IReviewService
{
private readonly IReviewRepository _reviewRepository;
private readonly ISubject<Review> _sub = new ReplaySubject<Review>(1);
public ReviewService(IReviewRepository reviewRepository)
{
_reviewRepository = reviewRepository;
}
public Review Add(Review review)
{
var addedEntity = _reviewRepository.Add(review);
_sub.OnNext(review);
return review;
}
public IObservable<Review> ReviewAdded()
{
return _sub.AsObservable();
}
}
I post the Startup.cs too, maybe it can helps.
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddDbContext<ShopDbContext>(opt =>
opt.UseSqlServer(Configuration.GetConnectionString("Default")));
services.AddScoped<ISupplierRepository, SupplierRepository>();
services.AddScoped<IProductRepository, ProductRepository>();
services.AddScoped<IReviewRepository, ReviewRepository>();
services.AddScoped<IReviewService, ReviewService>();
services.AddSingleton<IDataLoaderContextAccessor, DataLoaderContextAccessor>();
services.AddSingleton<DataLoaderDocumentListener>();
services.AddScoped<ShopSchema>();
services.AddGraphQL(options =>
{
options.EnableMetrics = false;
})
.AddWebSockets()
.AddSystemTextJson()
.AddGraphTypes(typeof(ShopSchema), ServiceLifetime.Scoped).AddDataLoader();
services.AddControllers()
.AddNewtonsoftJson(o => o.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ShopDbContext dbContext)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("Cors");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseWebSockets();
app.UseGraphQLWebSockets<ShopSchema>("/graphql");
app.UseGraphQL<ShopSchema>();
app.UseGraphQLPlayground(options: new GraphQLPlaygroundOptions { GraphQLEndPoint ="/graphql", SchemaPollingEnabled = false });
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
dbContext.SeedData();
}
}
Thanks in advance for help.
After a deeper investigation I found out the problem. I post the solution, maybe could help someone.
The problem was about Dependency Injection: the class containing the subscription "notification logic" (in my case ReviewService.cs) must be registered as Singleton instead of Scoped. This caused a sort of chain reaction that bring the repositories classes to be registered as Transient. This is the working code: