diff --git a/Build.ps1 b/Build.ps1
index 7cdc82e3d9..33cc65f4d9 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -66,4 +66,4 @@ Else {
Write-Output "RUNNING dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=alpha1-$revision"
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=alpha1-$revision --include-symbols
CheckLastExitCode
-}
+}
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Graph/IdentifiableTypeCache.cs b/src/JsonApiDotNetCore/Graph/IdentifiableTypeCache.cs
new file mode 100644
index 0000000000..cc45386e9e
--- /dev/null
+++ b/src/JsonApiDotNetCore/Graph/IdentifiableTypeCache.cs
@@ -0,0 +1,35 @@
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using JsonApiDotNetCore.Models;
+
+namespace JsonApiDotNetCore.Graph
+{
+ ///
+ /// Used to cache and locate types, to facilitate auto-resource discovery
+ ///
+ internal sealed class IdentifiableTypeCache
+ {
+ private readonly ConcurrentDictionary> _typeCache = new ConcurrentDictionary>();
+
+ ///
+ /// Get all implementations of in the assembly
+ ///
+ public IEnumerable GetIdentifiableTypes(Assembly assembly)
+ {
+ return _typeCache.GetOrAdd(assembly, asm => FindIdentifiableTypes(asm).ToList());
+ }
+
+ private static IEnumerable FindIdentifiableTypes(Assembly assembly)
+ {
+ foreach (var type in assembly.GetTypes())
+ {
+ if (TypeLocator.TryGetResourceDescriptor(type, out var descriptor))
+ {
+ yield return descriptor;
+ }
+ }
+ }
+ }
+}
diff --git a/src/JsonApiDotNetCore/Graph/ResourceDescriptor.cs b/src/JsonApiDotNetCore/Graph/ResourceDescriptor.cs
index 7fd97d3059..aac9824c56 100644
--- a/src/JsonApiDotNetCore/Graph/ResourceDescriptor.cs
+++ b/src/JsonApiDotNetCore/Graph/ResourceDescriptor.cs
@@ -13,6 +13,6 @@ public ResourceDescriptor(Type resourceType, Type idType)
public Type ResourceType { get; }
public Type IdType { get; }
- internal static ResourceDescriptor Empty => new ResourceDescriptor(null, null);
+ internal static ResourceDescriptor Empty { get; } = new ResourceDescriptor(null, null);
}
}
diff --git a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs
index a9a4e022d6..0a2e34ab6c 100644
--- a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs
+++ b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs
@@ -47,6 +47,7 @@ public class ServiceDiscoveryFacade : IServiceDiscoveryFacade
};
private readonly IServiceCollection _services;
private readonly IResourceGraphBuilder _resourceGraphBuilder;
+ private readonly IdentifiableTypeCache _typeCache = new IdentifiableTypeCache();
public ServiceDiscoveryFacade(IServiceCollection services, IResourceGraphBuilder resourceGraphBuilder)
{
@@ -67,7 +68,7 @@ public ServiceDiscoveryFacade AddAssembly(Assembly assembly)
{
AddDbContextResolvers(assembly);
- var resourceDescriptors = TypeLocator.GetIdentifiableTypes(assembly);
+ var resourceDescriptors = _typeCache.GetIdentifiableTypes(assembly);
foreach (var resourceDescriptor in resourceDescriptors)
{
AddResource(assembly, resourceDescriptor);
@@ -77,7 +78,6 @@ public ServiceDiscoveryFacade AddAssembly(Assembly assembly)
return this;
}
-
public IEnumerable FindDerivedTypes(Type baseType)
{
return baseType.Assembly.GetTypes().Where(t =>
@@ -108,9 +108,9 @@ private void AddDbContextResolvers(Assembly assembly)
/// The assembly to search for resources in.
public ServiceDiscoveryFacade AddResources(Assembly assembly)
{
- var identifiables = TypeLocator.GetIdentifiableTypes(assembly);
- foreach (var identifiable in identifiables)
- AddResource(assembly, identifiable);
+ var resourceDescriptors = _typeCache.GetIdentifiableTypes(assembly);
+ foreach (var resourceDescriptor in resourceDescriptors)
+ AddResource(assembly, resourceDescriptor);
return this;
}
@@ -148,7 +148,7 @@ private void AddResourceToGraph(ResourceDescriptor identifiable)
/// The assembly to search for resources in.
public ServiceDiscoveryFacade AddServices(Assembly assembly)
{
- var resourceDescriptors = TypeLocator.GetIdentifiableTypes(assembly);
+ var resourceDescriptors = _typeCache.GetIdentifiableTypes(assembly);
foreach (var resourceDescriptor in resourceDescriptors)
{
AddServices(assembly, resourceDescriptor);
@@ -170,7 +170,7 @@ private void AddServices(Assembly assembly, ResourceDescriptor resourceDescripto
/// The assembly to search for resources in.
public ServiceDiscoveryFacade AddRepositories(Assembly assembly)
{
- var resourceDescriptors = TypeLocator.GetIdentifiableTypes(assembly);
+ var resourceDescriptors = _typeCache.GetIdentifiableTypes(assembly);
foreach (var resourceDescriptor in resourceDescriptors)
{
AddRepositories(assembly, resourceDescriptor);
@@ -186,7 +186,7 @@ private void AddRepositories(Assembly assembly, ResourceDescriptor resourceDescr
RegisterServiceImplementations(assembly, serviceInterface, resourceDescriptor);
}
}
-
+
private void RegisterServiceImplementations(Assembly assembly, Type interfaceType, ResourceDescriptor resourceDescriptor)
{
if (resourceDescriptor.IdType == typeof(Guid) && interfaceType.GetTypeInfo().GenericTypeParameters.Length == 1)
diff --git a/src/JsonApiDotNetCore/Graph/TypeLocator.cs b/src/JsonApiDotNetCore/Graph/TypeLocator.cs
index 9fd4f5a458..a7444db227 100644
--- a/src/JsonApiDotNetCore/Graph/TypeLocator.cs
+++ b/src/JsonApiDotNetCore/Graph/TypeLocator.cs
@@ -1,4 +1,4 @@
-using JsonApiDotNetCore.Extensions;
+using JsonApiDotNetCore.Extensions;
using JsonApiDotNetCore.Models;
using System;
using System.Collections.Generic;
@@ -12,8 +12,6 @@ namespace JsonApiDotNetCore.Graph
///
internal static class TypeLocator
{
- private static readonly Dictionary> _identifiableTypeCache = new Dictionary>();
-
///
/// Determine whether or not this is a json:api resource by checking if it implements .
/// Returns the status and the resultant id type, either `(true, Type)` OR `(false, null)`
@@ -24,29 +22,6 @@ public static Type GetIdType(Type resourceType)
return identifiableInterface?.GetGenericArguments()[0];
}
- ///
- /// Get all implementations of in the assembly
- ///
- public static IEnumerable GetIdentifiableTypes(Assembly assembly)
- => (_identifiableTypeCache.TryGetValue(assembly, out _) == false)
- ? FindIdentifiableTypes(assembly)
- : _identifiableTypeCache[assembly];
-
- private static IEnumerable FindIdentifiableTypes(Assembly assembly)
- {
- var descriptors = new List();
- _identifiableTypeCache[assembly] = descriptors;
-
- foreach (var type in assembly.GetTypes())
- {
- if (TryGetResourceDescriptor(type, out var descriptor))
- {
- descriptors.Add(descriptor);
- yield return descriptor;
- }
- }
- }
-
///
/// Attempts to get a descriptor of the resource type.
///
diff --git a/test/UnitTests/Graph/IdentifiableTypeCacheTests.cs b/test/UnitTests/Graph/IdentifiableTypeCacheTests.cs
new file mode 100644
index 0000000000..a0b4220f75
--- /dev/null
+++ b/test/UnitTests/Graph/IdentifiableTypeCacheTests.cs
@@ -0,0 +1,39 @@
+using JsonApiDotNetCore.Graph;
+using JsonApiDotNetCore.Models;
+using UnitTests.Internal;
+using Xunit;
+
+namespace UnitTests.Graph
+{
+ public class IdentifiableTypeCacheTests
+ {
+ [Fact]
+ public void GetIdentifiableTypes_Locates_Identifiable_Resource()
+ {
+ // Arrange
+ var resourceType = typeof(Model);
+ var typeCache = new IdentifiableTypeCache();
+
+ // Act
+ var results = typeCache.GetIdentifiableTypes(resourceType.Assembly);
+
+ // Assert
+ Assert.Contains(results, r => r.ResourceType == resourceType);
+ }
+
+ [Fact]
+ public void GetIdentifiableTypes_Only_Contains_IIdentifiable_Types()
+ {
+ // Arrange
+ var resourceType = typeof(Model);
+ var typeCache = new IdentifiableTypeCache();
+
+ // Act
+ var resourceDescriptors = typeCache.GetIdentifiableTypes(resourceType.Assembly);
+
+ // Assert
+ foreach(var resourceDescriptor in resourceDescriptors)
+ Assert.True(typeof(IIdentifiable).IsAssignableFrom(resourceDescriptor.ResourceType));
+ }
+ }
+}
diff --git a/test/UnitTests/Graph/TypeLocator_Tests.cs b/test/UnitTests/Graph/TypeLocator_Tests.cs
index dfd4e723c4..e15b037c2b 100644
--- a/test/UnitTests/Graph/TypeLocator_Tests.cs
+++ b/test/UnitTests/Graph/TypeLocator_Tests.cs
@@ -81,33 +81,6 @@ public void GetIdType_Correctly_Identifies_NonJsonApiResource()
Assert.Equal(expectedIdType, idType);
}
- [Fact]
- public void GetIdentifiableTypes_Locates_Identifiable_Resource()
- {
- // Arrange
- var resourceType = typeof(Model);
-
- // Act
- var results = TypeLocator.GetIdentifiableTypes(resourceType.Assembly);
-
- // Assert
- Assert.Contains(results, r => r.ResourceType == resourceType);
- }
-
- [Fact]
- public void GetIdentifiableTypes_Only_Contains_IIdentifiable_Types()
- {
- // Arrange
- var resourceType = typeof(Model);
-
- // Act
- var resourceDescriptors = TypeLocator.GetIdentifiableTypes(resourceType.Assembly);
-
- // Assert
- foreach(var resourceDescriptor in resourceDescriptors)
- Assert.True(typeof(IIdentifiable).IsAssignableFrom(resourceDescriptor.ResourceType));
- }
-
[Fact]
public void TryGetResourceDescriptor_Returns_True_If_Type_Is_IIdentifiable()
{