diff --git a/src/Jab.FunctionalTests.Common/ContainerTests.cs b/src/Jab.FunctionalTests.Common/ContainerTests.cs index ff6d7eb..b81b634 100644 --- a/src/Jab.FunctionalTests.Common/ContainerTests.cs +++ b/src/Jab.FunctionalTests.Common/ContainerTests.cs @@ -547,6 +547,25 @@ public void DisposingScopeDisposesServices() [Scoped(typeof(IService), typeof(DisposableServiceImplementation))] internal partial class DisposingScopeDisposesServicesContainer { } + [Fact] + public void DisposingScopeDisposesFactoryGeneratedServices() + { + DisposingScopeDisposesFactoryGeneratedServicesContainer c = new(); + var scope = c.CreateScope(); + var service = Assert.IsType(scope.GetService()); + + scope.Dispose(); + + Assert.Equal(1, service.DisposalCount); + } + + [ServiceProvider] + [Scoped(typeof(IService), Factory = nameof(CreateDisposableService))] + internal partial class DisposingScopeDisposesFactoryGeneratedServicesContainer + { + private static IService CreateDisposableService() => new DisposableServiceImplementation(); + } + #if NETCOREAPP [Fact] public async Task DisposingScopeDisposesAsyncServices() @@ -572,6 +591,34 @@ public async Task DisposingScopeDisposesAsyncServices() [ServiceProvider] [Scoped(typeof(IService), typeof(AsyncDisposableServiceImplementation))] internal partial class DisposingScopeDisposesAsyncServicesContainer { } + + [Fact] + public async Task DisposingScopeDisposesFactoryGeneratedAsyncServices() + { + DisposingScopeDisposesFactoryGeneratedAsyncServicesContainer c = new(); + var scope = c.CreateScope(); + var service = Assert.IsType(scope.GetService()); + + await scope.DisposeAsync(); + + Assert.Equal(1, service.AsyncDisposalCount); + Assert.Equal(0, service.DisposalCount); + + scope = c.CreateScope(); + service = Assert.IsType(scope.GetService()); + + scope.Dispose(); + + Assert.Equal(0, service.AsyncDisposalCount); + Assert.Equal(1, service.DisposalCount); + } + + [ServiceProvider] + [Scoped(typeof(IService), Factory = nameof(CreateService))] + internal partial class DisposingScopeDisposesFactoryGeneratedAsyncServicesContainer + { + private static IService CreateService() => new AsyncDisposableServiceImplementation(); + } #endif [Fact] @@ -589,6 +636,24 @@ public void DisposingProviderDisposesRootScopedServices() [Scoped(typeof(IService), typeof(DisposableServiceImplementation))] internal partial class DisposingProviderDisposesRootScopedServicesContainer { } + [Fact] + public void DisposingProviderDisposesFactoryGeneratedRootScopedServicesIfInstructedTo() + { + DisposingProviderDisposesFactoryGeneratedRootScopedServicesContainer c = new(); + var service = Assert.IsType(c.GetService()); + + c.Dispose(); + + Assert.Equal(1, service.DisposalCount); + } + + [ServiceProvider] + [Scoped(typeof(IService), Factory = nameof(GenerateService))] + internal partial class DisposingProviderDisposesFactoryGeneratedRootScopedServicesContainer + { + private static IService GenerateService() => new DisposableServiceImplementation(); + } + [Fact] public void DisposingProviderDisposesRootSingletonServices() { @@ -604,6 +669,24 @@ public void DisposingProviderDisposesRootSingletonServices() [Singleton(typeof(IService), typeof(DisposableServiceImplementation))] internal partial class DisposingProviderDisposesRootSingletonServicesContainer { } + [Fact] + public void DisposingProviderDisposesFactoryGeneratedRootSingletonServices() + { + DisposingProviderDisposesFactoryGeneratedRootSingletonServicesContainer c = new(); + var service = Assert.IsType(c.GetService()); + + c.Dispose(); + + Assert.Equal(1, service.DisposalCount); + } + + [ServiceProvider] + [Singleton(typeof(IService), Factory = nameof(GetService))] + internal partial class DisposingProviderDisposesFactoryGeneratedRootSingletonServicesContainer + { + private static IService GetService() => new DisposableServiceImplementation(); + } + #if NETCOREAPP [Fact] public async Task DisposingProviderDisposesRootSingAsyncServices() @@ -620,6 +703,25 @@ public async Task DisposingProviderDisposesRootSingAsyncServices() [ServiceProvider] [Scoped(typeof(IService), typeof(AsyncDisposableServiceImplementation))] internal partial class DisposingProviderDisposesRootSingAsyncServicesContainer { } + + [Fact] + public async Task DisposingProviderDisposesFactoryGeneratedRootSingAsyncServices() + { + DisposingProviderDisposesFactoryGeneratedRootSingAsyncServicesContainer c = new(); + var service = Assert.IsType(c.GetService()); + + await c.DisposeAsync(); + + Assert.Equal(1, service.AsyncDisposalCount); + Assert.Equal(0, service.DisposalCount); + } + + [ServiceProvider] + [Scoped(typeof(IService), Factory = nameof(CreateService))] + internal partial class DisposingProviderDisposesFactoryGeneratedRootSingAsyncServicesContainer + { + private static IService CreateService() => new AsyncDisposableServiceImplementation(); + } #endif #if NETCOREAPP @@ -643,6 +745,30 @@ public async Task DisposingProviderDisposesAllSingletonEnumerableServices() [Scoped(typeof(IService), typeof(DisposableServiceImplementation))] [Scoped(typeof(IService), typeof(DisposableServiceImplementation))] internal partial class DisposingProviderDisposesAllSingletonEnumerableServicesContainer { } + + [Fact] + public async Task DisposingProviderDisposesAllFactoryGeneratedSingletonEnumerableServicesWhenInstructedTo() + { + DisposingProviderDisposesAllFactoryGeneratedSingletonEnumerableServicesContainer c = new(); + var services = Assert.IsType(c.GetService>()); + + await c.DisposeAsync(); + + foreach (var service in services) + { + var disposableService = Assert.IsType(service); + Assert.Equal(1, disposableService.DisposalCount); + } + } + + [ServiceProvider] + [Scoped(typeof(IService), Factory = nameof(CreateService))] + [Scoped(typeof(IService), Factory = nameof(CreateService))] + [Scoped(typeof(IService), Factory = nameof(CreateService))] + internal partial class DisposingProviderDisposesAllFactoryGeneratedSingletonEnumerableServicesContainer + { + private static IService CreateService() => new DisposableServiceImplementation(); + } #endif [Fact] @@ -668,6 +794,32 @@ public void DisposingProviderDisposesTransients() [Transient(typeof(IService), typeof(DisposableServiceImplementation))] internal partial class DisposingProviderDisposesTransientsContainer { } + [Fact] + public void DisposingProviderDisposesFactoryGeneratedTransients() + { + DisposingProviderDisposesFactoryGeneratedTransientsContainer c = new(); + List services = new(); + for (int i = 0; i < 5; i++) + { + services.Add(c.GetService()); + } + + c.Dispose(); + + foreach (var service in services) + { + var disposableService = Assert.IsType(service); + Assert.Equal(1, disposableService.DisposalCount); + } + } + + [ServiceProvider] + [Transient(typeof(IService), Factory = nameof(CreateService))] + internal partial class DisposingProviderDisposesFactoryGeneratedTransientsContainer + { + private static IService CreateService() => new DisposableServiceImplementation(); + } + [Fact] public void DisposingScopeDisposesTransients() { @@ -694,24 +846,31 @@ public void DisposingScopeDisposesTransients() internal partial class DisposingScopeDisposesTransientsContainer { } [Fact] - public void DisposingProviderDisposesRootSingletonFactoryServices() + public void DisposingScopeDisposesFactoryGeneratedTransients() { - DisposingProviderDisposesRootSingletonServicesContainer c = new(); - var service = Assert.IsType(c.GetService()); + DisposingScopeDisposesFactoryGeneratedTransientsContainer c = new(); + var scope = c.CreateScope(); - c.Dispose(); + List services = new(); + for (int i = 0; i < 5; i++) + { + services.Add(scope.GetService()); + } - Assert.Equal(1, service.DisposalCount); + scope.Dispose(); + + foreach (var service in services) + { + var disposableService = Assert.IsType(service); + Assert.Equal(1, disposableService.DisposalCount); + } } [ServiceProvider] - [Singleton(typeof(IService), Factory = nameof(CreateDisposableServiceImplementation))] - internal partial class DisposingProviderDisposesRootSingletonFactoryServicesContainer + [Transient(typeof(IService), Factory = nameof(CreateService))] + internal partial class DisposingScopeDisposesFactoryGeneratedTransientsContainer { - internal IService CreateDisposableServiceImplementation() - { - return new DisposableServiceImplementation(); - } + private IService CreateService() => new DisposableServiceImplementation(); } [Fact] diff --git a/src/Jab/Attributes.cs b/src/Jab/Attributes.cs index 373ab12..f38f7b3 100644 --- a/src/Jab/Attributes.cs +++ b/src/Jab/Attributes.cs @@ -90,6 +90,7 @@ public SingletonAttribute(Type serviceType, Type implementationType) class TransientAttribute : Attribute { public Type ServiceType { get; } + public string? Name { get; set; } public Type? ImplementationType { get; } @@ -118,6 +119,7 @@ public TransientAttribute(Type serviceType, Type implementationType) class ScopedAttribute : Attribute { public Type ServiceType { get; } + public string? Name { get; set; } public Type? ImplementationType { get; } diff --git a/src/Jab/ServiceProviderBuilder.cs b/src/Jab/ServiceProviderBuilder.cs index 3c7cb24..2b9af50 100644 --- a/src/Jab/ServiceProviderBuilder.cs +++ b/src/Jab/ServiceProviderBuilder.cs @@ -329,7 +329,7 @@ ServiceCallSite BuiltInCallSite(ServiceCallSite callSite) { var constructedFactoryMethod = factoryMethod.ConstructedFrom.Construct(genericType.TypeArguments, genericType.TypeArgumentNullableAnnotations); - callSite = CreateFactoryCallSite( + callSite = CreateFactoryCallSite( identity, genericType, registration.Lifetime, @@ -571,7 +571,7 @@ ImmutableArray GetDelegateParameters(ITypeSymbol type) parameters.ToArray(), namedParameters.ToArray(), lifetime, - false); + true); return factoryCallSite; } diff --git a/src/Jab/ServiceRegistration.cs b/src/Jab/ServiceRegistration.cs index 81b02b5..a8b81a8 100644 --- a/src/Jab/ServiceRegistration.cs +++ b/src/Jab/ServiceRegistration.cs @@ -8,16 +8,6 @@ internal record ServiceRegistration( ISymbol? InstanceMember, ISymbol? FactoryMember, Location? Location, - MemberLocation MemberLocation) -{ - public INamedTypeSymbol ServiceType { get; } = ServiceType; - public string? Name { get; } = Name; - public INamedTypeSymbol? ImplementationType { get; } = ImplementationType; - public ISymbol? InstanceMember { get; } = InstanceMember; - public ISymbol? FactoryMember { get; } = FactoryMember; - public ServiceLifetime Lifetime { get; } = Lifetime; - public Location? Location { get; } = Location; - public MemberLocation MemberLocation { get; } = MemberLocation; -} + MemberLocation MemberLocation); internal record RootService(INamedTypeSymbol Service, Location? Location); \ No newline at end of file