diff --git a/src/Lamar.Microsoft.DependencyInjection/HostBuilderExtensions.cs b/src/Lamar.Microsoft.DependencyInjection/HostBuilderExtensions.cs index 73f9b04f..70dbff71 100644 --- a/src/Lamar.Microsoft.DependencyInjection/HostBuilderExtensions.cs +++ b/src/Lamar.Microsoft.DependencyInjection/HostBuilderExtensions.cs @@ -39,7 +39,8 @@ public static HostApplicationBuilder UseLamar(this HostApplicationBuilder builde // This enables the usage of implicit services in Minimal APIs builder.Services.AddSingleton(s => (IServiceProviderIsService) s.GetRequiredService()); - + builder.Services.AddSingleton(s => (IServiceProviderIsKeyedService) s.GetRequiredService()); + builder.ConfigureContainer(new LamarServiceProviderFactory(), x => { configure?.Invoke(x); @@ -76,8 +77,9 @@ public static IHostBuilder UseLamar(this IHostBuilder builder, Action (IServiceProviderIsService) s.GetRequiredService()); + services.AddSingleton(s => (IServiceProviderIsKeyedService) s.GetRequiredService()); #endif - + }); } @@ -119,6 +121,7 @@ public static IServiceCollection AddLamar(this IServiceCollection services, Serv #if NET6_0_OR_GREATER services.AddSingleton(s => (IServiceProviderIsService) s.GetRequiredService()); + services.AddSingleton(s => (IServiceProviderIsKeyedService)s.GetRequiredService()); #endif registry ??= new ServiceRegistry(); diff --git a/src/Lamar.Testing/IoC/Acceptance/IServiceProviderIsKeyedService_implementation.cs b/src/Lamar.Testing/IoC/Acceptance/IServiceProviderIsKeyedService_implementation.cs new file mode 100644 index 00000000..f2cdeab1 --- /dev/null +++ b/src/Lamar.Testing/IoC/Acceptance/IServiceProviderIsKeyedService_implementation.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using StructureMap.Testing.Widget; +using Xunit; + +namespace Lamar.Testing.IoC.Acceptance; +#if NET6_0_OR_GREATER +public class IServiceProviderIsKeyedService_implementation +{ + [Fact] + public void explicit_checks_of_non_concrete_types() + { + var container = Container.For(services => { + services.For().Use(); + services.AddKeyedSingleton("IClock"); + }); + + container.IsKeyedService(typeof(IClock), "IClock").ShouldBeTrue(); + container.IsKeyedService(typeof(IWidget), "IWidget").ShouldBeFalse(); + } + + [Fact] + public void fix_for_368_auto_resolve_enumerable_concretes_and_generics() + { + var container = Container.For(services => + { + services.Scan(s => + { + s.AssemblyContainingType(GetType()); + s.WithDefaultConventions(); + s.AddAllTypesOf(); + }); + + services.For(typeof(IGenericService1<>)).Use(typeof(GenericService1<>)); + services.AddKeyedSingleton, List>("IEnumerable"); + services.AddKeyedSingleton, List>("IEnumerable"); + services.AddKeyedSingleton(typeof(IGenericService1<>), "GenericService1<>", typeof(GenericService1<>)); + + }); + + container.IsKeyedService(typeof(ConcreteClass), "ConcreteClass").ShouldBeFalse(); + container.IsKeyedService(typeof(IEnumerable), "IEnumerable").ShouldBeTrue(); + container.IsKeyedService(typeof(IGenericService1), "IGenericService1").ShouldBeTrue(); + container.IsKeyedService(typeof(IGenericService1), "IGenericService1").ShouldBeTrue(); + } + + [Fact] + public void fix_for_391_dont_resolve_collections_with_not_registered_type() + { + var containerWithNoRegistrations = Container.Empty(); + + containerWithNoRegistrations.IsKeyedService(typeof(IEnumerable),"IEnumerable").ShouldBeFalse(); + containerWithNoRegistrations.IsKeyedService(typeof(IReadOnlyCollection) ,"IReadOnlyCollection").ShouldBeFalse(); + containerWithNoRegistrations.IsKeyedService(typeof(IList), "IList").ShouldBeFalse(); + containerWithNoRegistrations.IsKeyedService(typeof(List), "List").ShouldBeFalse(); + + var containerWithRegistrations = Container.For(services => + { + services.ForConcreteType(); + services.AddKeyedSingleton, List>("IEnumerable"); + }); + + containerWithRegistrations.IsKeyedService(typeof(IEnumerable), "IEnumerable").ShouldBeTrue(); + containerWithRegistrations.IsKeyedService(typeof(IReadOnlyCollection), "IReadOnlyCollection").ShouldBeTrue(); + containerWithRegistrations.IsKeyedService(typeof(IList), "IList").ShouldBeTrue(); + containerWithRegistrations.IsKeyedService(typeof(List), "List").ShouldBeTrue(); + } + + public interface IService + { + } + + public class ServiceA : IService + { + } + + public class ServiceB : IService + { + } + + public class ConcreteClass + { + } + + public interface IGenericService1 + { + } + + public class GenericService1 : IGenericService1 + { + } +} +#endif \ No newline at end of file diff --git a/src/Lamar/IoC/Scope.cs b/src/Lamar/IoC/Scope.cs index 8f862f54..64148a36 100644 --- a/src/Lamar/IoC/Scope.cs +++ b/src/Lamar/IoC/Scope.cs @@ -21,7 +21,7 @@ namespace Lamar.IoC; #region sample_Scope-Declarations -public class Scope : IServiceContext, IServiceProviderIsService +public class Scope : IServiceContext, IServiceProviderIsService, IServiceProviderIsKeyedService #endregion @@ -369,6 +369,11 @@ public IServiceVariableSource CreateServiceVariableSource() public bool IsService(Type serviceType) { return ServiceGraph.CanBeServiceByNetCoreRules(serviceType); + } + + public bool IsKeyedService(Type serviceType, object serviceKey) + { + return ServiceGraph.CanBeServiceByNetCoreRules(serviceType, serviceKey.ToString()); } public static Scope Empty() @@ -482,5 +487,5 @@ public object GetKeyedService(Type serviceType, object serviceKey) public object GetRequiredKeyedService(Type serviceType, object serviceKey) { return GetInstance(serviceType, serviceKey.ToString()); - } + } } \ No newline at end of file diff --git a/src/Lamar/ServiceGraph.cs b/src/Lamar/ServiceGraph.cs index f2d311c4..3137795a 100644 --- a/src/Lamar/ServiceGraph.cs +++ b/src/Lamar/ServiceGraph.cs @@ -593,4 +593,35 @@ public bool CanBeServiceByNetCoreRules(Type serviceType) return family.Default != null; } + + public bool CanBeServiceByNetCoreRules(Type serviceType, string name) + { + if (_families.TryFind(serviceType, out var family)) + { + return family.InstanceFor(name) != null; + } + + lock (_familyLock) + { + if (_families.TryFind(serviceType, out family)) + { + return family.InstanceFor(name) != null; + } + + family = TryToCreateMissingFamilyWithNetCoreRules(serviceType); + _families = _families.AddOrUpdate(serviceType, family); + + if (!_inPlanning) + { + buildOutMissingResolvers(); + + if (family != null) + { + rebuildReferencedAssemblyArray(); + } + } + } + + return family.InstanceFor(name) != null; + } } \ No newline at end of file