-
-
Notifications
You must be signed in to change notification settings - Fork 159
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Removes building an intermediate service provider at startup, which i…
…s considered an anti-pattern, leading to occasional compatibility issues. (#1431) This unblocks use in [Aspire](https://learn.microsoft.com/en-us/dotnet/aspire/) (more specifically, its usage of `AddNpgsqlDataSource`, which throws an `ObjectDisposedException`) and fixes #1082.
- Loading branch information
Showing
5 changed files
with
209 additions
and
179 deletions.
There are no files selected for viewing
129 changes: 129 additions & 0 deletions
129
src/JsonApiDotNetCore/Configuration/InjectablesAssemblyScanner.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
using System.Reflection; | ||
using JsonApiDotNetCore.Repositories; | ||
using JsonApiDotNetCore.Resources; | ||
using JsonApiDotNetCore.Services; | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.DependencyInjection.Extensions; | ||
|
||
namespace JsonApiDotNetCore.Configuration; | ||
|
||
/// <summary> | ||
/// Scans assemblies for injectables (types that implement <see cref="IResourceService{TResource,TId}" />, | ||
/// <see cref="IResourceRepository{TResource,TId}" /> or <see cref="IResourceDefinition{TResource,TId}" />) and registers them in the IoC container. | ||
/// </summary> | ||
internal sealed class InjectablesAssemblyScanner | ||
{ | ||
internal static readonly HashSet<Type> ServiceUnboundInterfaces = | ||
[ | ||
typeof(IResourceService<,>), | ||
typeof(IResourceCommandService<,>), | ||
typeof(IResourceQueryService<,>), | ||
typeof(IGetAllService<,>), | ||
typeof(IGetByIdService<,>), | ||
typeof(IGetSecondaryService<,>), | ||
typeof(IGetRelationshipService<,>), | ||
typeof(ICreateService<,>), | ||
typeof(IAddToRelationshipService<,>), | ||
typeof(IUpdateService<,>), | ||
typeof(ISetRelationshipService<,>), | ||
typeof(IDeleteService<,>), | ||
typeof(IRemoveFromRelationshipService<,>) | ||
]; | ||
|
||
internal static readonly HashSet<Type> RepositoryUnboundInterfaces = | ||
[ | ||
typeof(IResourceRepository<,>), | ||
typeof(IResourceWriteRepository<,>), | ||
typeof(IResourceReadRepository<,>) | ||
]; | ||
|
||
internal static readonly HashSet<Type> ResourceDefinitionUnboundInterfaces = [typeof(IResourceDefinition<,>)]; | ||
|
||
private readonly ResourceDescriptorAssemblyCache _assemblyCache; | ||
private readonly IServiceCollection _services; | ||
private readonly TypeLocator _typeLocator = new(); | ||
|
||
public InjectablesAssemblyScanner(ResourceDescriptorAssemblyCache assemblyCache, IServiceCollection services) | ||
{ | ||
ArgumentGuard.NotNull(assemblyCache); | ||
ArgumentGuard.NotNull(services); | ||
|
||
_assemblyCache = assemblyCache; | ||
_services = services; | ||
} | ||
|
||
public void DiscoverInjectables() | ||
{ | ||
IReadOnlyCollection<ResourceDescriptor> descriptors = _assemblyCache.GetResourceDescriptors(); | ||
IReadOnlyCollection<Assembly> assemblies = _assemblyCache.GetAssemblies(); | ||
|
||
foreach (Assembly assembly in assemblies) | ||
{ | ||
AddDbContextResolvers(assembly); | ||
AddInjectables(descriptors, assembly); | ||
} | ||
} | ||
|
||
private void AddDbContextResolvers(Assembly assembly) | ||
{ | ||
IEnumerable<Type> dbContextTypes = _typeLocator.GetDerivedTypes(assembly, typeof(DbContext)); | ||
|
||
foreach (Type dbContextType in dbContextTypes) | ||
{ | ||
Type dbContextResolverClosedType = typeof(DbContextResolver<>).MakeGenericType(dbContextType); | ||
_services.TryAddScoped(typeof(IDbContextResolver), dbContextResolverClosedType); | ||
} | ||
} | ||
|
||
private void AddInjectables(IEnumerable<ResourceDescriptor> resourceDescriptors, Assembly assembly) | ||
{ | ||
foreach (ResourceDescriptor resourceDescriptor in resourceDescriptors) | ||
{ | ||
AddServices(assembly, resourceDescriptor); | ||
AddRepositories(assembly, resourceDescriptor); | ||
AddResourceDefinitions(assembly, resourceDescriptor); | ||
} | ||
} | ||
|
||
private void AddServices(Assembly assembly, ResourceDescriptor resourceDescriptor) | ||
{ | ||
foreach (Type serviceUnboundInterface in ServiceUnboundInterfaces) | ||
{ | ||
RegisterImplementations(assembly, serviceUnboundInterface, resourceDescriptor); | ||
} | ||
} | ||
|
||
private void AddRepositories(Assembly assembly, ResourceDescriptor resourceDescriptor) | ||
{ | ||
foreach (Type repositoryUnboundInterface in RepositoryUnboundInterfaces) | ||
{ | ||
RegisterImplementations(assembly, repositoryUnboundInterface, resourceDescriptor); | ||
} | ||
} | ||
|
||
private void AddResourceDefinitions(Assembly assembly, ResourceDescriptor resourceDescriptor) | ||
{ | ||
foreach (Type resourceDefinitionUnboundInterface in ResourceDefinitionUnboundInterfaces) | ||
{ | ||
RegisterImplementations(assembly, resourceDefinitionUnboundInterface, resourceDescriptor); | ||
} | ||
} | ||
|
||
private void RegisterImplementations(Assembly assembly, Type interfaceType, ResourceDescriptor resourceDescriptor) | ||
{ | ||
Type[] typeArguments = | ||
[ | ||
resourceDescriptor.ResourceClrType, | ||
resourceDescriptor.IdClrType | ||
]; | ||
|
||
(Type implementationType, Type serviceInterface)? result = _typeLocator.GetContainerRegistrationFromAssembly(assembly, interfaceType, typeArguments); | ||
|
||
if (result != null) | ||
{ | ||
(Type implementationType, Type serviceInterface) = result.Value; | ||
_services.TryAddScoped(serviceInterface, implementationType); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
src/JsonApiDotNetCore/Configuration/ResourcesAssemblyScanner.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using JsonApiDotNetCore.Resources; | ||
|
||
namespace JsonApiDotNetCore.Configuration; | ||
|
||
/// <summary> | ||
/// Scans assemblies for types that implement <see cref="IIdentifiable{TId}" /> and adds them to the resource graph. | ||
/// </summary> | ||
internal sealed class ResourcesAssemblyScanner | ||
{ | ||
private readonly ResourceDescriptorAssemblyCache _assemblyCache; | ||
private readonly ResourceGraphBuilder _resourceGraphBuilder; | ||
|
||
public ResourcesAssemblyScanner(ResourceDescriptorAssemblyCache assemblyCache, ResourceGraphBuilder resourceGraphBuilder) | ||
{ | ||
ArgumentGuard.NotNull(assemblyCache); | ||
ArgumentGuard.NotNull(resourceGraphBuilder); | ||
|
||
_assemblyCache = assemblyCache; | ||
_resourceGraphBuilder = resourceGraphBuilder; | ||
} | ||
|
||
public void DiscoverResources() | ||
{ | ||
foreach (ResourceDescriptor resourceDescriptor in _assemblyCache.GetResourceDescriptors()) | ||
{ | ||
_resourceGraphBuilder.Add(resourceDescriptor.ResourceClrType, resourceDescriptor.IdClrType); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.