Skip to content

Commit c51eea3

Browse files
committed
chore: review
1 parent f519342 commit c51eea3

17 files changed

+110
-122
lines changed

src/JsonApiDotNetCore/AssemblyInfo.cs

-7
This file was deleted.

src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using JsonApiDotNetCore.Formatters;
21
using JsonApiDotNetCore.Middleware;
32
using Microsoft.AspNetCore.Builder;
43
using Microsoft.Extensions.DependencyInjection;
@@ -29,9 +28,14 @@ public static void UseJsonApi(this IApplicationBuilder builder)
2928
var jsonApiApplicationBuilder = builder.ApplicationServices.GetRequiredService<IJsonApiApplicationBuilder>();
3029
jsonApiApplicationBuilder.ConfigureMvcOptions = options =>
3130
{
32-
options.InputFormatters.Insert(0, builder.ApplicationServices.GetRequiredService<IJsonApiInputFormatter>());
33-
options.OutputFormatters.Insert(0, builder.ApplicationServices.GetRequiredService<IJsonApiOutputFormatter>());
34-
options.Conventions.Insert(0, builder.ApplicationServices.GetRequiredService<IJsonApiRoutingConvention>());
31+
var inputFormatter = builder.ApplicationServices.GetRequiredService<IJsonApiInputFormatter>();
32+
options.InputFormatters.Insert(0, inputFormatter);
33+
34+
var outputFormatter = builder.ApplicationServices.GetRequiredService<IJsonApiOutputFormatter>();
35+
options.OutputFormatters.Insert(0, outputFormatter);
36+
37+
var routingConvention = builder.ApplicationServices.GetRequiredService<IJsonApiRoutingConvention>();
38+
options.Conventions.Insert(0, routingConvention);
3539
};
3640

3741
builder.UseMiddleware<JsonApiMiddleware>();

src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs

+14-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System;
2-
using JsonApiDotNetCore.Configuration;
3-
using JsonApiDotNetCore.Formatters;
42
using JsonApiDotNetCore.Hooks.Internal;
53
using JsonApiDotNetCore.Hooks.Internal.Discovery;
64
using JsonApiDotNetCore.Hooks.Internal.Execution;
@@ -28,22 +26,25 @@ namespace JsonApiDotNetCore.Configuration
2826
/// A utility class that builds a JsonApi application. It registers all required services
2927
/// and allows the user to override parts of the startup configuration.
3028
/// </summary>
31-
internal sealed class JsonApiApplicationBuilder : IJsonApiApplicationBuilder
29+
internal sealed class JsonApiApplicationBuilder : IJsonApiApplicationBuilder, IDisposable
3230
{
3331
private readonly JsonApiOptions _options = new JsonApiOptions();
3432
private readonly IServiceCollection _services;
3533
private readonly IMvcCoreBuilder _mvcBuilder;
3634
private readonly ResourceGraphBuilder _resourceGraphBuilder;
3735
private readonly ServiceDiscoveryFacade _serviceDiscoveryFacade;
3836
private readonly ServiceProvider _intermediateProvider;
37+
3938
public Action<MvcOptions> ConfigureMvcOptions { get; set; }
4039

4140
public JsonApiApplicationBuilder(IServiceCollection services, IMvcCoreBuilder mvcBuilder)
4241
{
4342
_services = services ?? throw new ArgumentNullException(nameof(services));
4443
_mvcBuilder = mvcBuilder ?? throw new ArgumentNullException(nameof(mvcBuilder));
44+
4545
_intermediateProvider = services.BuildServiceProvider();
4646
var loggerFactory = _intermediateProvider.GetService<ILoggerFactory>();
47+
4748
_resourceGraphBuilder = new ResourceGraphBuilder(_options, loggerFactory);
4849
_serviceDiscoveryFacade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, loggerFactory);
4950
}
@@ -62,16 +63,16 @@ public void ConfigureAutoDiscovery(Action<ServiceDiscoveryFacade> configureAutoD
6263
}
6364

6465
/// <summary>
65-
/// Configures and build the resource graph with resources from the provided sources and adds it to the DI container.
66+
/// Configures and builds the resource graph with resources from the provided sources and adds it to the DI container.
6667
/// </summary>
67-
public void AddResourceGraph(Type dbContextType, Action<ResourceGraphBuilder> configureResources)
68+
public void AddResourceGraph(Type dbContextType, Action<ResourceGraphBuilder> configureResourceGraph)
6869
{
6970
AutoDiscoverResources(_serviceDiscoveryFacade);
7071
if (dbContextType != null)
7172
{
7273
AddResourcesFromDbContext((DbContext)_intermediateProvider.GetService(dbContextType), _resourceGraphBuilder);
7374
}
74-
UserConfigureResources(configureResources, _resourceGraphBuilder);
75+
ExecuteManualConfigurationOfResources(configureResourceGraph, _resourceGraphBuilder);
7576
_services.AddSingleton(_resourceGraphBuilder.Build());
7677
}
7778

@@ -105,7 +106,6 @@ public void ConfigureMvc()
105106
public void DiscoverInjectables()
106107
{
107108
_serviceDiscoveryFacade.DiscoverInjectables();
108-
_intermediateProvider.Dispose();
109109
}
110110

111111
/// <summary>
@@ -140,6 +140,7 @@ public void ConfigureServices(Type dbContextType)
140140

141141
AddServerSerialization();
142142
AddQueryStringParameterServices();
143+
143144
if (_options.EnableResourceHooks)
144145
{
145146
AddResourceHooks();
@@ -288,11 +289,15 @@ private void AutoDiscoverResources(ServiceDiscoveryFacade serviceDiscoveryFacade
288289
/// <summary>
289290
/// Executes the action provided by the user to configure the resources using <see cref="ResourceGraphBuilder"/>
290291
/// </summary>
291-
private void UserConfigureResources(Action<ResourceGraphBuilder> configureResources,
292+
private void ExecuteManualConfigurationOfResources(Action<ResourceGraphBuilder> configureResources,
292293
ResourceGraphBuilder resourceGraphBuilder)
293294
{
294295
configureResources?.Invoke(resourceGraphBuilder);
295296
}
296-
297+
298+
public void Dispose()
299+
{
300+
_intermediateProvider?.Dispose();
301+
}
297302
}
298303
}

src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs

+35-15
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,24 @@ namespace JsonApiDotNetCore.Configuration
1111
{
1212
public class ResourceGraphBuilder
1313
{
14-
private readonly IJsonApiOptions _options;
1514
private readonly ILogger<ResourceGraphBuilder> _logger;
15+
private readonly IJsonApiOptions _options;
1616
private readonly List<ResourceContext> _resources = new List<ResourceContext>();
1717

1818
public ResourceGraphBuilder(IJsonApiOptions options, ILoggerFactory loggerFactory)
1919
{
20+
if (loggerFactory == null)
21+
{
22+
throw new ArgumentNullException(nameof(loggerFactory));
23+
}
24+
25+
_logger = loggerFactory.CreateLogger<ResourceGraphBuilder>();
2026
_options = options ?? throw new ArgumentNullException(nameof(options));
21-
_logger = loggerFactory?.CreateLogger<ResourceGraphBuilder>();
2227
}
2328

24-
/// <inheritdoc />
29+
/// <summary>
30+
/// Constructs the <see cref="ResourceGraph"/>.
31+
/// </summary>
2532
public IResourceGraph Build()
2633
{
2734
_resources.ForEach(SetResourceLinksOptions);
@@ -39,15 +46,28 @@ private void SetResourceLinksOptions(ResourceContext resourceContext)
3946
}
4047
}
4148

42-
/// <inheritdoc />
43-
public ResourceGraphBuilder Add<TResource>(string publicName = null) where TResource : class, IIdentifiable<int>
44-
=> Add<TResource, int>(publicName);
45-
46-
/// <inheritdoc />
49+
/// <summary>
50+
/// Adds a json:api resource to the resource graph.
51+
/// </summary>
52+
/// <typeparam name="TResource">The resource type.</typeparam>
53+
/// <typeparam name="TId">The identifier type of the resource</typeparam>
54+
/// <param name="publicName">
55+
/// The name under which the resource is publicly exposed by the API.
56+
/// If nothing is specified, the configured casing convention formatter will be applied.
57+
/// </param>
4758
public ResourceGraphBuilder Add<TResource, TId>(string publicName = null) where TResource : class, IIdentifiable<TId>
4859
=> Add(typeof(TResource), typeof(TId), publicName);
60+
61+
/// <summary>
62+
/// <see cref="Add{TResource,TId}(string)"/>
63+
/// </summary>
64+
public ResourceGraphBuilder Add<TResource>(string publicName = null) where TResource : class, IIdentifiable<int>
65+
=> Add<TResource, int>(publicName);
66+
4967

50-
/// <inheritdoc />
68+
/// <summary>
69+
/// <see cref="Add{TResource,TId}(string)"/>
70+
/// </summary>
5171
public ResourceGraphBuilder Add(Type resourceType, Type idType = null, string publicName = null)
5272
{
5373
if (resourceType == null) throw new ArgumentNullException(nameof(resourceType));
@@ -63,7 +83,7 @@ public ResourceGraphBuilder Add(Type resourceType, Type idType = null, string pu
6383
}
6484
else
6585
{
66-
_logger?.LogWarning($"Entity '{resourceType}' does not implement '{nameof(IIdentifiable)}'.");
86+
_logger.LogWarning($"Entity '{resourceType}' does not implement '{nameof(IIdentifiable)}'.");
6787
}
6888
}
6989

@@ -81,7 +101,7 @@ public ResourceGraphBuilder Add(Type resourceType, Type idType = null, string pu
81101
ResourceDefinitionType = GetResourceDefinitionType(resourceType)
82102
};
83103

84-
protected virtual IReadOnlyCollection<AttrAttribute> GetAttributes(Type resourceType)
104+
private IReadOnlyCollection<AttrAttribute> GetAttributes(Type resourceType)
85105
{
86106
if (resourceType == null) throw new ArgumentNullException(nameof(resourceType));
87107

@@ -122,7 +142,7 @@ protected virtual IReadOnlyCollection<AttrAttribute> GetAttributes(Type resource
122142
return attributes;
123143
}
124144

125-
protected virtual IReadOnlyCollection<RelationshipAttribute> GetRelationships(Type resourceType)
145+
private IReadOnlyCollection<RelationshipAttribute> GetRelationships(Type resourceType)
126146
{
127147
if (resourceType == null) throw new ArgumentNullException(nameof(resourceType));
128148

@@ -180,7 +200,7 @@ protected virtual IReadOnlyCollection<RelationshipAttribute> GetRelationships(Ty
180200
return attributes;
181201
}
182202

183-
private static Type TryGetThroughType(PropertyInfo throughProperty)
203+
private Type TryGetThroughType(PropertyInfo throughProperty)
184204
{
185205
if (throughProperty.PropertyType.IsGenericType)
186206
{
@@ -198,7 +218,7 @@ private static Type TryGetThroughType(PropertyInfo throughProperty)
198218
return null;
199219
}
200220

201-
protected virtual Type GetRelationshipType(RelationshipAttribute relationship, PropertyInfo property)
221+
private Type GetRelationshipType(RelationshipAttribute relationship, PropertyInfo property)
202222
{
203223
if (relationship == null) throw new ArgumentNullException(nameof(relationship));
204224
if (property == null) throw new ArgumentNullException(nameof(property));
@@ -231,7 +251,7 @@ private IReadOnlyCollection<EagerLoadAttribute> GetEagerLoads(Type resourceType,
231251
return attributes;
232252
}
233253

234-
private static Type TypeOrElementType(Type type)
254+
private Type TypeOrElementType(Type type)
235255
{
236256
var interfaces = type.GetInterfaces()
237257
.Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)).ToArray();

src/JsonApiDotNetCore/Configuration/ServiceCollectionExtensions.cs

+9-15
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@ public static IServiceCollection AddJsonApi<TDbContext>(this IServiceCollection
3636
Action<JsonApiOptions> options = null,
3737
Action<ServiceDiscoveryFacade> discovery = null,
3838
Action<ResourceGraphBuilder> resources = null,
39-
IMvcCoreBuilder mvcBuilder = null,
40-
ILoggerFactory loggerFactory = null)
39+
IMvcCoreBuilder mvcBuilder = null)
4140
where TDbContext : DbContext
4241
{
43-
if (services == null) throw new ArgumentNullException(nameof(services));
42+
if (services == null)
43+
{
44+
throw new ArgumentNullException(nameof(services));
45+
}
4446

4547
SetupApplicationBuilder(services, options, discovery, resources, mvcBuilder, typeof(TDbContext));
4648

@@ -49,27 +51,19 @@ public static IServiceCollection AddJsonApi<TDbContext>(this IServiceCollection
4951

5052
private static void SetupApplicationBuilder(IServiceCollection services, Action<JsonApiOptions> configureOptions,
5153
Action<ServiceDiscoveryFacade> configureAutoDiscovery,
52-
Action<ResourceGraphBuilder> configureResources, IMvcCoreBuilder mvcBuilder, Type dbContextType)
54+
Action<ResourceGraphBuilder> configureResourceGraph, IMvcCoreBuilder mvcBuilder, Type dbContextType)
5355
{
5456
var applicationBuilder = new JsonApiApplicationBuilder(services, mvcBuilder ?? services.AddMvcCore());
5557

5658
applicationBuilder.ConfigureJsonApiOptions(configureOptions);
5759
applicationBuilder.ConfigureAutoDiscovery(configureAutoDiscovery);
58-
applicationBuilder.AddResourceGraph(dbContextType, configureResources);
60+
applicationBuilder.AddResourceGraph(dbContextType, configureResourceGraph);
5961
applicationBuilder.ConfigureMvc();
6062
applicationBuilder.DiscoverInjectables();
6163
applicationBuilder.ConfigureServices(dbContextType);
64+
applicationBuilder.Dispose();
6265
}
63-
64-
private static void ResolveInverseRelationships(IServiceCollection services)
65-
{
66-
using var intermediateProvider = services.BuildServiceProvider();
67-
using var scope = intermediateProvider.CreateScope();
68-
69-
var inverseRelationshipResolver = scope.ServiceProvider.GetService<IInverseRelationships>();
70-
inverseRelationshipResolver?.Resolve();
71-
}
72-
66+
7367
/// <summary>
7468
/// Enables client serializers for sending requests and receiving responses
7569
/// in json:api format. Internally only used for testing.

src/JsonApiDotNetCore/Configuration/ServiceDiscoveryFacade.cs

+18-8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
namespace JsonApiDotNetCore.Configuration
1414
{
15+
/// <summary>
16+
/// Scans for types like resources, services, repositories and resource definitions in an assembly and registers them to the IoC container.
17+
/// </summary>
1518
public class ServiceDiscoveryFacade
1619
{
1720
internal static readonly HashSet<Type> ServiceInterfaces = new HashSet<Type> {
@@ -46,32 +49,40 @@ public class ServiceDiscoveryFacade
4649
typeof(IResourceReadRepository<,>)
4750
};
4851

52+
private readonly ILogger<ResourceGraphBuilder> _logger;
4953
private readonly IServiceCollection _services;
5054
private readonly ResourceGraphBuilder _resourceGraphBuilder;
5155
private readonly IdentifiableTypeCache _typeCache = new IdentifiableTypeCache();
5256
private readonly Dictionary<Assembly, IList<ResourceDescriptor>> _resourceDescriptorsPerAssemblyCache = new Dictionary<Assembly, IList<ResourceDescriptor>>();
53-
private readonly ILogger<ResourceGraphBuilder> _logger;
5457

5558
public ServiceDiscoveryFacade(IServiceCollection services, ResourceGraphBuilder resourceGraphBuilder, ILoggerFactory loggerFactory)
5659
{
60+
if (loggerFactory == null)
61+
{
62+
throw new ArgumentNullException(nameof(loggerFactory));
63+
}
64+
65+
_logger = loggerFactory.CreateLogger<ResourceGraphBuilder>();
5766
_services = services ?? throw new ArgumentNullException(nameof(services));
5867
_resourceGraphBuilder = resourceGraphBuilder ?? throw new ArgumentNullException(nameof(resourceGraphBuilder));
59-
_logger = loggerFactory?.CreateLogger<ResourceGraphBuilder>();
6068
}
6169

62-
/// <inheritdoc />
70+
/// <summary>
71+
/// Scan the calling assembly.
72+
/// </summary>
6373
public ServiceDiscoveryFacade AddCurrentAssembly() => AddAssembly(Assembly.GetCallingAssembly());
6474

65-
/// <inheritdoc />
75+
/// <summary>
76+
/// Scan the specified assembly.
77+
/// </summary>
6678
public ServiceDiscoveryFacade AddAssembly(Assembly assembly)
6779
{
6880
_resourceDescriptorsPerAssemblyCache.Add(assembly, null);
69-
_logger?.LogDebug($"Registering assembly '{assembly.FullName}' for discovery of resources and injectables.");
81+
_logger.LogDebug($"Registering assembly '{assembly.FullName}' for discovery of resources and injectables.");
7082

7183
return this;
7284
}
73-
74-
/// <inheritdoc />
85+
7586
internal void DiscoverResources()
7687
{
7788
foreach (var (assembly, discoveredResourceDescriptors) in _resourceDescriptorsPerAssemblyCache.ToArray())
@@ -85,7 +96,6 @@ internal void DiscoverResources()
8596
}
8697
}
8798

88-
/// <inheritdoc />
8999
internal void DiscoverInjectables()
90100
{
91101
foreach (var (assembly, discoveredResourceDescriptors) in _resourceDescriptorsPerAssemblyCache.ToArray())

src/JsonApiDotNetCore/Middleware/ConvertEmptyActionResultFilter.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ public void OnResultExecuted(ResultExecutedContext context) { /* noop */ }
1212

1313
public void OnResultExecuting(ResultExecutingContext context)
1414
{
15-
if (context == null) throw new ArgumentNullException(nameof(context));
15+
if (context == null)
16+
{
17+
throw new ArgumentNullException(nameof(context));
18+
}
1619

1720
if (!context.HttpContext.IsJsonApiRequest())
1821
{
@@ -24,7 +27,7 @@ public void OnResultExecuting(ResultExecutingContext context)
2427
case ObjectResult objectResult when objectResult.Value != null:
2528
return;
2629
case IStatusCodeActionResult statusCodeResult:
27-
context.Result = new ObjectResult(null) {StatusCode = statusCodeResult.StatusCode};
30+
context.Result = new ObjectResult(null) { StatusCode = statusCodeResult.StatusCode };
2831
break;
2932
}
3033
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using Microsoft.AspNetCore.Mvc.Formatters;
22

3-
namespace JsonApiDotNetCore.Formatters
3+
namespace JsonApiDotNetCore.Middleware
44
{
55
public interface IJsonApiInputFormatter : IInputFormatter { }
66
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using Microsoft.AspNetCore.Mvc.Formatters;
22

3-
namespace JsonApiDotNetCore.Formatters
3+
namespace JsonApiDotNetCore.Middleware
44
{
55
public interface IJsonApiOutputFormatter : IOutputFormatter { }
66
}

src/JsonApiDotNetCore/Middleware/IJsonApiTypeMatchFilter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace JsonApiDotNetCore.Middleware
44
{
55
/// <summary>
6-
/// Action filter used to verify the incoming type matches the target type, else return a 409
6+
/// Action filter used to verify the incoming type matches the target type, else return a 409.
77
/// </summary>
88
public interface IJsonApiTypeMatchFilter : IActionFilter { }
99
}

0 commit comments

Comments
 (0)