Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 3878da9

Browse filesBrowse files
authored
Add SchemaInjectorCollection per ServiceCollection (#144) (#145)
* Fixed #144 race condition when calling AddGraphQL multiple times before UseGraphQL is called
1 parent 8ae48c9 commit 3878da9
Copy full SHA for 3878da9

File tree

4 files changed

+138
-8
lines changed
Filter options

4 files changed

+138
-8
lines changed

‎src/graphql-aspnet/Configuration/GraphQLSchemaBuilderExtensions.cs

Copy file name to clipboardExpand all lines: src/graphql-aspnet/Configuration/GraphQLSchemaBuilderExtensions.cs
+55-8Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,43 @@ namespace GraphQL.AspNet.Configuration
2828
/// </summary>
2929
public static class GraphQLSchemaBuilderExtensions
3030
{
31-
private static readonly Dictionary<Type, ISchemaInjector> SCHEMA_REGISTRATIONS;
31+
private static readonly Dictionary<IServiceCollection, ISchemaInjectorCollection> SCHEMA_REGISTRATIONS;
3232

3333
/// <summary>
3434
/// Initializes static members of the <see cref="GraphQLSchemaBuilderExtensions"/> class.
3535
/// </summary>
3636
static GraphQLSchemaBuilderExtensions()
3737
{
38-
SCHEMA_REGISTRATIONS = new Dictionary<Type, ISchemaInjector>();
38+
SCHEMA_REGISTRATIONS = new Dictionary<IServiceCollection, ISchemaInjectorCollection>();
3939
}
4040

4141
/// <summary>
4242
/// Helper method to null out the schema registration references. Useful in testing and after setup is complete there is no
4343
/// need to keep the reference chain in tact.
4444
/// </summary>
45+
/// <param name="serviceCollection">The service collection to clear.</param>
46+
public static void Clear(IServiceCollection serviceCollection)
47+
{
48+
if (!SCHEMA_REGISTRATIONS.TryGetValue(serviceCollection, out var value))
49+
{
50+
return;
51+
}
52+
53+
value.Clear();
54+
SCHEMA_REGISTRATIONS.Remove(serviceCollection);
55+
}
56+
57+
/// <summary>
58+
/// Helper method to null out all schema injector collections. Useful in testing and after setup is complete there is no
59+
/// need to keep the reference chain in tact.
60+
/// </summary>
4561
public static void Clear()
4662
{
63+
foreach (var collection in SCHEMA_REGISTRATIONS.Values)
64+
{
65+
collection.Clear();
66+
}
67+
4768
SCHEMA_REGISTRATIONS.Clear();
4869
}
4970

@@ -75,7 +96,8 @@ public static ISchemaBuilder<TSchema> AddGraphQL<TSchema>(
7596
where TSchema : class, ISchema
7697
{
7798
Validation.ThrowIfNull(serviceCollection, nameof(serviceCollection));
78-
if (SCHEMA_REGISTRATIONS.ContainsKey(typeof(TSchema)))
99+
var injectorCollection = GetSchemaInjectorCollection(serviceCollection);
100+
if (injectorCollection.ContainsKey(typeof(TSchema)))
79101
{
80102
throw new GraphTypeDeclarationException(
81103
$"The schema type {typeof(TSchema).FriendlyName()} has already been registered. " +
@@ -84,7 +106,7 @@ public static ISchemaBuilder<TSchema> AddGraphQL<TSchema>(
84106

85107
var schemaOptions = new SchemaOptions<TSchema>(serviceCollection);
86108
var injector = new GraphQLSchemaInjector<TSchema>(schemaOptions, options);
87-
SCHEMA_REGISTRATIONS.Add(typeof(TSchema), injector);
109+
injectorCollection.Add(typeof(TSchema), injector);
88110

89111
injector.ConfigureServices();
90112
return injector.SchemaBuilder;
@@ -111,12 +133,13 @@ public static ISchemaBuilder<GraphSchema> AddGraphQL(
111133
/// <param name="app">The application being constructed.</param>
112134
public static void UseGraphQL(this IApplicationBuilder app)
113135
{
114-
foreach (var injector in SCHEMA_REGISTRATIONS.Values)
136+
var injectorCollection = app.ApplicationServices.GetRequiredService<ISchemaInjectorCollection>();
137+
foreach (var injector in injectorCollection.Values)
115138
{
116139
injector.UseSchema(app);
117140
}
118141

119-
Clear();
142+
Clear(injectorCollection.ServiceCollection);
120143
}
121144

122145
/// <summary>
@@ -133,12 +156,36 @@ public static void UseGraphQL(this IApplicationBuilder app)
133156
/// graphql runtime.</param>
134157
public static void UseGraphQL(this IServiceProvider serviceProvider)
135158
{
136-
foreach (var injector in SCHEMA_REGISTRATIONS.Values)
159+
var injectorCollection = serviceProvider.GetRequiredService<ISchemaInjectorCollection>();
160+
foreach (var injector in injectorCollection.Values)
137161
{
138162
injector.UseSchema(serviceProvider);
139163
}
140164

141-
Clear();
165+
Clear(injectorCollection.ServiceCollection);
166+
}
167+
168+
/// <summary>
169+
/// Get or create schema injector collection for the service collection.
170+
/// </summary>
171+
/// <param name="serviceCollection">The service collection to create schema injector collection for</param>
172+
/// <returns>Existing or new schema injector collection</returns>
173+
private static ISchemaInjectorCollection GetSchemaInjectorCollection(IServiceCollection serviceCollection)
174+
{
175+
if (SCHEMA_REGISTRATIONS.TryGetValue(serviceCollection, out var value))
176+
{
177+
return value;
178+
}
179+
180+
var injectorCollection = new SchemaInjectorCollection()
181+
{
182+
ServiceCollection = serviceCollection,
183+
};
184+
serviceCollection.AddSingleton<ISchemaInjectorCollection>(injectorCollection);
185+
value = injectorCollection;
186+
SCHEMA_REGISTRATIONS.Add(serviceCollection, value);
187+
188+
return value;
142189
}
143190
}
144191
}
+25Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// *************************************************************
2+
// project: graphql-aspnet
3+
// --
4+
// repo: https://github.com/graphql-aspnet
5+
// docs: https://graphql-aspnet.github.io
6+
// --
7+
// License: MIT
8+
// *************************************************************
9+
10+
namespace GraphQL.AspNet.Configuration
11+
{
12+
using System;
13+
using System.Collections.Generic;
14+
using GraphQL.AspNet.Interfaces.Configuration;
15+
using Microsoft.Extensions.DependencyInjection;
16+
17+
/// <summary>
18+
/// A schema injector collection implementation.
19+
/// </summary>
20+
public class SchemaInjectorCollection : Dictionary<Type, ISchemaInjector>, ISchemaInjectorCollection
21+
{
22+
/// <inheritdoc />
23+
public IServiceCollection ServiceCollection { get; set; }
24+
}
25+
}
+28Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// *************************************************************
2+
// project: graphql-aspnet
3+
// --
4+
// repo: https://github.com/graphql-aspnet
5+
// docs: https://graphql-aspnet.github.io
6+
// --
7+
// License: MIT
8+
// *************************************************************
9+
10+
namespace GraphQL.AspNet.Interfaces.Configuration
11+
{
12+
using System;
13+
using System.Collections.Generic;
14+
using Microsoft.Extensions.DependencyInjection;
15+
16+
/// <summary>
17+
/// An interface used by the injector to track multiple schema injectors
18+
/// used for injecting.
19+
/// </summary>
20+
public interface ISchemaInjectorCollection : IDictionary<Type, ISchemaInjector>
21+
{
22+
/// <summary>
23+
/// Gets the service collection to which this collection is attached.
24+
/// </summary>
25+
/// <value>The attached service collection.</value>
26+
public IServiceCollection ServiceCollection { get; }
27+
}
28+
}

‎src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationSetupTests.cs

Copy file name to clipboardExpand all lines: src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationSetupTests.cs
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace GraphQL.AspNet.Tests.Configuration
1111
{
12+
using System;
1213
using System.Linq;
1314
using System.Reflection;
1415
using System.Threading.Tasks;
@@ -323,5 +324,34 @@ public void ChangingGlobalConfig_ChangesHowControllersAreRegistered()
323324

324325
Assert.AreEqual(ServiceLifetime.Singleton, descriptor.Lifetime);
325326
}
327+
328+
[Test]
329+
public void AddGraphQL_UseGraphQL_SameSchema()
330+
{
331+
var service1Collection = new ServiceCollection();
332+
var service2Collection = new ServiceCollection();
333+
334+
service1Collection.AddGraphQL<CandleSchema>(options =>
335+
{
336+
options.AddGraphType<Candle>();
337+
});
338+
service2Collection.AddGraphQL<CandleSchema>(options =>
339+
{
340+
options.AddGraphType<Candle>();
341+
});
342+
343+
var sp1 = service1Collection.BuildServiceProvider();
344+
var sp2 = service2Collection.BuildServiceProvider();
345+
sp1.UseGraphQL();
346+
sp2.UseGraphQL();
347+
348+
var schema1 = sp1.GetService(typeof(CandleSchema)) as ISchema;
349+
var schema2 = sp2.GetService(typeof(CandleSchema)) as ISchema;
350+
Assert.IsNotNull(schema1);
351+
Assert.IsNotNull(schema2);
352+
353+
Assert.IsTrue(schema1.KnownTypes.Contains(typeof(Candle)));
354+
Assert.IsTrue(schema2.KnownTypes.Contains(typeof(Candle)));
355+
}
326356
}
327357
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.