Skip to content

Commit 787002a

Browse files
committed
fix: review
1 parent 9ac7456 commit 787002a

File tree

5 files changed

+117
-100
lines changed

5 files changed

+117
-100
lines changed

src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using JsonApiDotNetCoreExample.Data;
66
using Microsoft.EntityFrameworkCore;
77
using System;
8-
using System.Linq;
98
using JsonApiDotNetCore;
109
using JsonApiDotNetCore.Configuration;
1110
using JsonApiDotNetCore.QueryStrings;

src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs

Lines changed: 95 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,13 @@ namespace JsonApiDotNetCore.Builders
3434
internal sealed class JsonApiApplicationBuilder
3535
{
3636
private readonly JsonApiOptions _options = new JsonApiOptions();
37-
private IResourceGraphBuilder _resourceGraphBuilder;
38-
private Type _dbContextType;
39-
private Action<IResourceGraphBuilder> _configureResources;
40-
private Action<IServiceDiscoveryFacade> _configureAutoDiscovery;
4137
private readonly IServiceCollection _services;
4238
private IServiceDiscoveryFacade _serviceDiscoveryFacade;
39+
private IResourceGraphBuilder _resourceGraphBuilder;
4340
private readonly IMvcCoreBuilder _mvcBuilder;
4441

45-
public JsonApiApplicationBuilder(IServiceCollection services, IMvcCoreBuilder mvcBuilder)
42+
public JsonApiApplicationBuilder(IServiceCollection services,
43+
IMvcCoreBuilder mvcBuilder)
4644
{
4745
_services = services;
4846
_mvcBuilder = mvcBuilder;
@@ -51,19 +49,11 @@ public JsonApiApplicationBuilder(IServiceCollection services, IMvcCoreBuilder mv
5149
/// <summary>
5250
/// Executes the action provided by the user to configure <see cref="JsonApiOptions"/>
5351
/// </summary>
54-
public void ConfigureJsonApiOptions(Action<JsonApiOptions> options)
55-
{
56-
options?.Invoke(_options);
57-
}
58-
59-
public void RegisterResourceSources(Type dbContextType, Action<IServiceDiscoveryFacade> autoDiscovery,
60-
Action<IResourceGraphBuilder> resources)
52+
public void ConfigureJsonApiOptions(Action<JsonApiOptions> configureOptions)
6153
{
62-
_dbContextType = dbContextType;
63-
_configureAutoDiscovery = autoDiscovery;
64-
_configureResources = resources;
54+
configureOptions?.Invoke(_options);
6555
}
66-
56+
6757
/// <summary>
6858
/// Configures built-in .NET Core MVC (things like middleware, routing). Most of this configuration can be adjusted for the developers' need.
6959
/// Before calling .AddJsonApi(), a developer can register their own implementation of the following services to customize startup:
@@ -72,24 +62,16 @@ public void RegisterResourceSources(Type dbContextType, Action<IServiceDiscovery
7262
/// </summary>
7363
public void ConfigureMvc()
7464
{
75-
RegisterJsonApiStartupServices();
76-
77-
using (var resourceConfigurationIntermediateProvider = _services.BuildServiceProvider())
78-
{
79-
_resourceGraphBuilder = resourceConfigurationIntermediateProvider.GetRequiredService<IResourceGraphBuilder>();
80-
_serviceDiscoveryFacade = resourceConfigurationIntermediateProvider.GetRequiredService<IServiceDiscoveryFacade>();
81-
_services.AddSingleton(BuildResourceGraph(resourceConfigurationIntermediateProvider));
82-
}
83-
8465
IJsonApiExceptionFilterProvider exceptionFilterProvider;
8566
IJsonApiTypeMatchFilterProvider typeMatchFilterProvider;
8667
IJsonApiRoutingConvention routingConvention;
8768

88-
using (var middlewareConfigurationIntermediateProvider = _services.BuildServiceProvider())
69+
using (var intermediateProvider = _services.BuildServiceProvider())
8970
{
90-
exceptionFilterProvider = middlewareConfigurationIntermediateProvider.GetRequiredService<IJsonApiExceptionFilterProvider>();
91-
typeMatchFilterProvider = middlewareConfigurationIntermediateProvider.GetRequiredService<IJsonApiTypeMatchFilterProvider>();
92-
routingConvention = middlewareConfigurationIntermediateProvider.GetRequiredService<IJsonApiRoutingConvention>();
71+
exceptionFilterProvider = intermediateProvider.GetRequiredService<IJsonApiExceptionFilterProvider>();
72+
typeMatchFilterProvider = intermediateProvider.GetRequiredService<IJsonApiTypeMatchFilterProvider>();
73+
routingConvention = intermediateProvider.GetRequiredService<IJsonApiRoutingConvention>();
74+
_services.AddSingleton<IControllerResourceMapping>(routingConvention);
9375
}
9476

9577
_mvcBuilder.AddMvcOptions(options =>
@@ -107,20 +89,41 @@ public void ConfigureMvc()
10789
{
10890
_mvcBuilder.AddDataAnnotations();
10991
}
110-
111-
_services.AddSingleton<IControllerResourceMapping>(routingConvention);
11292
}
11393

94+
/// <summary>
95+
/// Configures and build the resource graph with resources from the provided sources and adds it to the DI container.
96+
/// </summary>
97+
public void AddResourceGraph(Type dbContextType, Action<IResourceGraphBuilder> configureResources)
98+
{
99+
using var intermediateProvider = _services.BuildServiceProvider();
100+
AddResourcesFromDbContext(dbContextType, intermediateProvider, _resourceGraphBuilder);
101+
AutoDiscoverResources(_serviceDiscoveryFacade);
102+
UserConfigureResources(configureResources, _resourceGraphBuilder);
103+
_services.AddSingleton(_resourceGraphBuilder.Build());
104+
}
105+
106+
public void ConfigureAutoDiscovery(Action<IServiceDiscoveryFacade> configureAutoDiscovery)
107+
{
108+
using var intermediateProvider = _services.BuildServiceProvider();
109+
_serviceDiscoveryFacade = intermediateProvider.GetRequiredService<IServiceDiscoveryFacade>();
110+
_resourceGraphBuilder = intermediateProvider.GetRequiredService<IResourceGraphBuilder>();
111+
RegisterDiscoverableAssemblies(configureAutoDiscovery, _serviceDiscoveryFacade);
112+
}
113+
114+
private void RegisterDiscoverableAssemblies(Action<IServiceDiscoveryFacade> configureAutoDiscovery, IServiceDiscoveryFacade serviceDiscoveryFacade)
115+
{
116+
configureAutoDiscovery?.Invoke(serviceDiscoveryFacade);
117+
}
118+
114119
/// <summary>
115120
/// Registers the remaining internals.
116121
/// </summary>
117-
public void ConfigureServices()
122+
public void ConfigureServices(Type dbContextType)
118123
{
119-
_serviceDiscoveryFacade.DiscoverServices();
120-
121-
if (_dbContextType != null)
124+
if (dbContextType != null)
122125
{
123-
var contextResolverType = typeof(DbContextResolver<>).MakeGenericType(_dbContextType);
126+
var contextResolverType = typeof(DbContextResolver<>).MakeGenericType(dbContextType);
124127
_services.AddScoped(typeof(IDbContextResolver), contextResolverType);
125128
}
126129
else
@@ -129,12 +132,56 @@ public void ConfigureServices()
129132
_services.AddSingleton(new DbContextOptionsBuilder().Options);
130133
}
131134

135+
AddRepositoryLayer();
136+
AddServiceLayer();
137+
138+
_services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
139+
_services.AddSingleton<IResourceContextProvider>(sp => sp.GetRequiredService<IResourceGraph>());
140+
_services.AddSingleton<IExceptionHandler, ExceptionHandler>();
141+
142+
_services.AddScoped<ICurrentRequest, CurrentRequest>();
143+
_services.AddScoped<IScopedServiceProvider, RequestScopedServiceProvider>();
144+
_services.AddScoped<IJsonApiWriter, JsonApiWriter>();
145+
_services.AddScoped<IJsonApiReader, JsonApiReader>();
146+
_services.AddScoped<IGenericServiceFactory, GenericServiceFactory>();
147+
_services.AddScoped(typeof(RepositoryRelationshipUpdateHelper<>));
148+
_services.AddScoped<ITargetedFields, TargetedFields>();
149+
_services.AddScoped<IResourceDefinitionProvider, ResourceDefinitionProvider>();
150+
_services.AddScoped<IFieldsToSerialize, FieldsToSerialize>();
151+
_services.AddScoped(typeof(IResourceChangeTracker<>), typeof(ResourceChangeTracker<>));
152+
_services.AddScoped<IResourceFactory, ResourceFactory>();
153+
_services.AddScoped<IPaginationContext, PaginationContext>();
154+
_services.AddScoped<IQueryLayerComposer, QueryLayerComposer>();
155+
156+
AddServerSerialization();
157+
AddQueryStringParameterServices();
158+
if (_options.EnableResourceHooks)
159+
{
160+
AddResourceHooks();
161+
}
162+
163+
_services.AddScoped<IInverseRelationships, InverseRelationships>();
164+
}
165+
166+
/// <summary>
167+
/// Discovers DI registrable services in the assemblies marked for discovery.
168+
/// </summary>
169+
public void DiscoverServices()
170+
{
171+
_serviceDiscoveryFacade.DiscoverServices();
172+
}
173+
174+
private void AddRepositoryLayer()
175+
{
132176
_services.AddScoped(typeof(IResourceRepository<>), typeof(EntityFrameworkCoreRepository<>));
133177
_services.AddScoped(typeof(IResourceRepository<,>), typeof(EntityFrameworkCoreRepository<,>));
134178

135179
_services.AddScoped(typeof(IResourceReadRepository<,>), typeof(EntityFrameworkCoreRepository<,>));
136180
_services.AddScoped(typeof(IResourceWriteRepository<,>), typeof(EntityFrameworkCoreRepository<,>));
181+
}
137182

183+
private void AddServiceLayer()
184+
{
138185
_services.AddScoped(typeof(ICreateService<>), typeof(JsonApiResourceService<>));
139186
_services.AddScoped(typeof(ICreateService<,>), typeof(JsonApiResourceService<,>));
140187

@@ -161,33 +208,6 @@ public void ConfigureServices()
161208

162209
_services.AddScoped(typeof(IResourceQueryService<,>), typeof(JsonApiResourceService<,>));
163210
_services.AddScoped(typeof(IResourceCommandService<,>), typeof(JsonApiResourceService<,>));
164-
165-
_services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
166-
_services.AddSingleton<IResourceContextProvider>(sp => sp.GetRequiredService<IResourceGraph>());
167-
_services.AddSingleton<IExceptionHandler, ExceptionHandler>();
168-
169-
_services.AddScoped<ICurrentRequest, CurrentRequest>();
170-
_services.AddScoped<IScopedServiceProvider, RequestScopedServiceProvider>();
171-
_services.AddScoped<IJsonApiWriter, JsonApiWriter>();
172-
_services.AddScoped<IJsonApiReader, JsonApiReader>();
173-
_services.AddScoped<IGenericServiceFactory, GenericServiceFactory>();
174-
_services.AddScoped(typeof(RepositoryRelationshipUpdateHelper<>));
175-
_services.AddScoped<ITargetedFields, TargetedFields>();
176-
_services.AddScoped<IResourceDefinitionProvider, ResourceDefinitionProvider>();
177-
_services.AddScoped<IFieldsToSerialize, FieldsToSerialize>();
178-
_services.AddScoped(typeof(IResourceChangeTracker<>), typeof(ResourceChangeTracker<>));
179-
_services.AddScoped<IResourceFactory, ResourceFactory>();
180-
_services.AddScoped<IPaginationContext, PaginationContext>();
181-
_services.AddScoped<IQueryLayerComposer, QueryLayerComposer>();
182-
183-
AddServerSerialization();
184-
AddQueryStringParameterServices();
185-
if (_options.EnableResourceHooks)
186-
{
187-
AddResourceHooks();
188-
}
189-
190-
_services.AddScoped<IInverseRelationships, InverseRelationships>();
191211
}
192212

193213
private void AddQueryStringParameterServices()
@@ -244,7 +264,10 @@ private void AddServerSerialization()
244264
_services.AddScoped<IResourceObjectBuilder, ResponseResourceObjectBuilder>();
245265
}
246266

247-
private void RegisterJsonApiStartupServices()
267+
/// <summary>
268+
/// Registers services that are required for the configuration of JsonApiDotNetCore during the start up.
269+
/// </summary>
270+
public void RegisterJsonApiStartupServices()
248271
{
249272
_services.AddSingleton<IJsonApiOptions>(_options);
250273
_services.TryAddSingleton<IJsonApiRoutingConvention, JsonApiRoutingConvention>();
@@ -254,43 +277,33 @@ private void RegisterJsonApiStartupServices()
254277
_services.TryAddScoped<IJsonApiTypeMatchFilterProvider, JsonApiTypeMatchFilterProvider>();
255278
}
256279

257-
private IResourceGraph BuildResourceGraph(ServiceProvider intermediateProvider)
258-
{
259-
AddResourceTypesFromDbContext(intermediateProvider);
260-
AutoDiscoverResources();
261-
ConfigureResources();
262-
263-
return _resourceGraphBuilder.Build();
264-
}
265-
266-
private void AddResourceTypesFromDbContext(ServiceProvider intermediateProvider)
280+
private void AddResourcesFromDbContext(Type dbContextType, ServiceProvider intermediateProvider, IResourceGraphBuilder builder)
267281
{
268-
if (_dbContextType != null)
282+
if (dbContextType != null)
269283
{
270-
var dbContext = (DbContext) intermediateProvider.GetRequiredService(_dbContextType);
284+
var dbContext = (DbContext) intermediateProvider.GetRequiredService(dbContextType);
271285

272286
foreach (var entityType in dbContext.Model.GetEntityTypes())
273287
{
274-
_resourceGraphBuilder.AddResource(entityType.ClrType);
288+
builder.AddResource(entityType.ClrType);
275289
}
276290
}
277291
}
278292

279293
/// <summary>
280294
/// Performs auto-discovery of JsonApiDotNetCore services.
281295
/// </summary>
282-
private void AutoDiscoverResources()
296+
private void AutoDiscoverResources(IServiceDiscoveryFacade serviceDiscoveryFacade)
283297
{
284-
_configureAutoDiscovery?.Invoke(_serviceDiscoveryFacade);
285-
_serviceDiscoveryFacade.DiscoverResources();
298+
serviceDiscoveryFacade.DiscoverResources();
286299
}
287300

288301
/// <summary>
289302
/// Executes the action provided by the user to configure the resources using <see cref="IResourceGraphBuilder"/>
290303
/// </summary>
291-
private void ConfigureResources()
304+
private void UserConfigureResources(Action<IResourceGraphBuilder> configureResources, IResourceGraphBuilder resourceGraphBuilder)
292305
{
293-
_configureResources?.Invoke(_resourceGraphBuilder);
306+
configureResources?.Invoke(resourceGraphBuilder);
294307
}
295308
}
296309
}

src/JsonApiDotNetCore/Extensions/ServiceCollectionExtensions.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,19 @@ public static IServiceCollection AddJsonApi<TDbContext>(this IServiceCollection
4747
return services;
4848
}
4949

50-
private static void SetupApplicationBuilder(IServiceCollection services, Action<JsonApiOptions> options,
51-
Action<IServiceDiscoveryFacade> autoDiscovery,
52-
Action<IResourceGraphBuilder> resources, IMvcCoreBuilder mvcBuilder, Type dbContextType)
50+
private static void SetupApplicationBuilder(IServiceCollection services, Action<JsonApiOptions> configureOptions,
51+
Action<IServiceDiscoveryFacade> configureAutoDiscovery,
52+
Action<IResourceGraphBuilder> configureResources, IMvcCoreBuilder mvcBuilder, Type dbContextType)
5353
{
5454
var applicationBuilder = new JsonApiApplicationBuilder(services, mvcBuilder ?? services.AddMvcCore());
5555

56-
applicationBuilder.ConfigureJsonApiOptions(options);
57-
applicationBuilder.RegisterResourceSources(dbContextType, autoDiscovery, resources);
56+
applicationBuilder.ConfigureJsonApiOptions(configureOptions);
57+
applicationBuilder.RegisterJsonApiStartupServices();
58+
applicationBuilder.ConfigureAutoDiscovery(configureAutoDiscovery);
59+
applicationBuilder.AddResourceGraph(dbContextType, configureResources);
5860
applicationBuilder.ConfigureMvc();
59-
applicationBuilder.ConfigureServices();
61+
applicationBuilder.DiscoverServices();
62+
applicationBuilder.ConfigureServices(dbContextType);
6063
}
6164

6265
private static void ResolveInverseRelationships(IServiceCollection services)

src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public class ServiceDiscoveryFacade : IServiceDiscoveryFacade
4949
private readonly IServiceCollection _services;
5050
private readonly IResourceGraphBuilder _resourceGraphBuilder;
5151
private readonly IdentifiableTypeCache _typeCache = new IdentifiableTypeCache();
52-
private readonly Dictionary<Assembly, IEnumerable<ResourceDescriptor>> _discoverableAssemblies = new Dictionary<Assembly, IEnumerable<ResourceDescriptor>>();
52+
private readonly Dictionary<Assembly, IList<ResourceDescriptor>> _resourceDescriptorsPerAssemblyCache = new Dictionary<Assembly, IList<ResourceDescriptor>>();
5353

5454
public ServiceDiscoveryFacade(IServiceCollection services, IResourceGraphBuilder resourceGraphBuilder)
5555
{
@@ -63,18 +63,17 @@ public ServiceDiscoveryFacade(IServiceCollection services, IResourceGraphBuilder
6363
/// <inheritdoc/>
6464
public ServiceDiscoveryFacade AddAssembly(Assembly assembly)
6565
{
66-
_discoverableAssemblies.Add(assembly, null);
66+
_resourceDescriptorsPerAssemblyCache.Add(assembly, null);
6767

6868
return this;
6969
}
7070

7171
/// <inheritdoc/>
7272
void IServiceDiscoveryFacade.DiscoverResources()
7373
{
74-
75-
foreach (var (assembly, discoveredResourceDescriptors) in _discoverableAssemblies.ToArray())
74+
foreach (var (assembly, discoveredResourceDescriptors) in _resourceDescriptorsPerAssemblyCache.ToArray())
7675
{
77-
var resourceDescriptors = GetOrSetResourceDescriptors(discoveredResourceDescriptors, assembly);
76+
var resourceDescriptors = GetResourceDescriptorsFromCache(discoveredResourceDescriptors, assembly);
7877

7978
foreach (var descriptor in resourceDescriptors)
8079
{
@@ -86,11 +85,11 @@ void IServiceDiscoveryFacade.DiscoverResources()
8685
/// <inheritdoc/>
8786
void IServiceDiscoveryFacade.DiscoverServices()
8887
{
89-
foreach (var (assembly, discoveredResourceDescriptors) in _discoverableAssemblies.ToArray())
88+
foreach (var (assembly, discoveredResourceDescriptors) in _resourceDescriptorsPerAssemblyCache.ToArray())
9089
{
9190
AddDbContextResolvers(assembly);
9291

93-
var resourceDescriptors = GetOrSetResourceDescriptors(discoveredResourceDescriptors, assembly);
92+
var resourceDescriptors = GetResourceDescriptorsFromCache(discoveredResourceDescriptors, assembly);
9493

9594
foreach (var descriptor in resourceDescriptors)
9695
{
@@ -124,7 +123,9 @@ private void AddResourceDefinition(Assembly assembly, ResourceDescriptor identif
124123
.SingleOrDefault();
125124

126125
if (resourceDefinition != null)
126+
{
127127
_services.AddScoped(typeof(ResourceDefinition<>).MakeGenericType(identifiable.ResourceType), resourceDefinition);
128+
}
128129
}
129130
catch (InvalidOperationException e)
130131
{
@@ -163,13 +164,13 @@ private void RegisterServiceImplementations(Assembly assembly, Type interfaceTyp
163164
}
164165
}
165166

166-
private IEnumerable<ResourceDescriptor> GetOrSetResourceDescriptors(IEnumerable<ResourceDescriptor> discoveredResourceDescriptors, Assembly assembly)
167+
private IList<ResourceDescriptor> GetResourceDescriptorsFromCache(IList<ResourceDescriptor> discoveredResourceDescriptors, Assembly assembly)
167168
{
168-
IEnumerable<ResourceDescriptor> resourceDescriptors;
169+
IList<ResourceDescriptor> resourceDescriptors;
169170
if (discoveredResourceDescriptors == null)
170171
{
171-
resourceDescriptors = _typeCache.GetIdentifiableTypes(assembly);
172-
_discoverableAssemblies[assembly] = resourceDescriptors;
172+
resourceDescriptors = (IList<ResourceDescriptor>)_typeCache.GetIdentifiableTypes(assembly);
173+
_resourceDescriptorsPerAssemblyCache[assembly] = resourceDescriptors;
173174
}
174175
else
175176
{

test/JsonApiDotNetCoreExampleTests/Factories/ResourceHooksApplicationFactory.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Reflection;
22
using JsonApiDotNetCore;
3+
using JsonApiDotNetCoreExample.Data;
34
using Microsoft.AspNetCore.Hosting;
45
using Microsoft.AspNetCore.TestHost;
56

0 commit comments

Comments
 (0)