From 1aca241fc0e7d5535fdff23c833dcbfdaa0f02b7 Mon Sep 17 00:00:00 2001 From: Travis Illig Date: Wed, 26 Oct 2016 11:23:56 -0700 Subject: [PATCH] #495: Improved generic handling in ACTNARS so it no longer interferes with supported relationship types. --- ...yConcreteTypeNotAlreadyRegisteredSource.cs | 33 +++- .../ResolveAnything/ResolveAnythingTests.cs | 179 ++++++++++++------ 2 files changed, 152 insertions(+), 60 deletions(-) diff --git a/src/Autofac/Features/ResolveAnything/AnyConcreteTypeNotAlreadyRegisteredSource.cs b/src/Autofac/Features/ResolveAnything/AnyConcreteTypeNotAlreadyRegisteredSource.cs index bbe95e837..fdf2215c5 100644 --- a/src/Autofac/Features/ResolveAnything/AnyConcreteTypeNotAlreadyRegisteredSource.cs +++ b/src/Autofac/Features/ResolveAnything/AnyConcreteTypeNotAlreadyRegisteredSource.cs @@ -70,18 +70,39 @@ public IEnumerable RegistrationsFor( Service service, Func> registrationAccessor) { - if (registrationAccessor == null) throw new ArgumentNullException(nameof(registrationAccessor)); + if (registrationAccessor == null) + { + throw new ArgumentNullException(nameof(registrationAccessor)); + } var ts = service as TypedService; - if (ts == null || - ts.ServiceType == typeof(string) || - !ts.ServiceType.GetTypeInfo().IsClass || - ts.ServiceType.GetTypeInfo().IsSubclassOf(typeof(Delegate)) || - ts.ServiceType.GetTypeInfo().IsAbstract || + if (ts == null || ts.ServiceType == typeof(string)) + { + return Enumerable.Empty(); + } + + var typeInfo = ts.ServiceType.GetTypeInfo(); + if (!typeInfo.IsClass || + typeInfo.IsSubclassOf(typeof(Delegate)) || + typeInfo.IsAbstract || + typeInfo.IsGenericTypeDefinition || !_predicate(ts.ServiceType) || registrationAccessor(service).Any()) + { return Enumerable.Empty(); + } + + if (typeInfo.IsGenericType) + { + foreach (var typeParameter in typeInfo.GenericTypeArguments) + { + if (!registrationAccessor(new TypedService(typeParameter)).Any()) + { + return Enumerable.Empty(); + } + } + } var builder = RegistrationBuilder.ForType(ts.ServiceType); RegistrationConfiguration?.Invoke(builder); diff --git a/test/Autofac.Test/Features/ResolveAnything/ResolveAnythingTests.cs b/test/Autofac.Test/Features/ResolveAnything/ResolveAnythingTests.cs index 61995d6b0..3ed0eefc1 100644 --- a/test/Autofac.Test/Features/ResolveAnything/ResolveAnythingTests.cs +++ b/test/Autofac.Test/Features/ResolveAnything/ResolveAnythingTests.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using Autofac.Core; +using Autofac.Features.Metadata; +using Autofac.Features.OwnedInstances; using Autofac.Features.ResolveAnything; using Autofac.Test.Scenarios.RegistrationSources; using Xunit; @@ -9,7 +12,7 @@ namespace Autofac.Test.Features.ResolveAnything { public class ResolveAnythingTests { - public class NotRegisteredType + public interface IInterfaceType { } @@ -20,61 +23,52 @@ public void AConcreteTypeNotRegisteredWithTheContainerWillBeProvided() Assert.True(container.IsRegistered()); } - public abstract class AbstractType - { - } - [Fact] - public void AnAbstractTypeNotRegisteredWithTheContainerWillNotBeProvided() + public void AllConcreteTypesSourceAlreadyRegisteredResolvesOptionalParams() { - var container = CreateResolveAnythingContainer(); - Assert.False(container.IsRegistered()); - } + var cb = new ContainerBuilder(); - public interface IInterfaceType - { - } + // Concrete type is already registered, but still errors + cb.RegisterType(); + cb.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource()); + var container = cb.Build(); - [Fact] - public void AnInterfaceTypeNotRegisteredWithTheContainerWillNotBeProvided() - { - var container = CreateResolveAnythingContainer(); - Assert.False(container.IsRegistered()); + var resolved = container.Resolve(); + + Assert.Equal("MyString", resolved.StringParam); } [Fact] - public void TypesFromTheRegistrationSourceAreProvidedToOtherSources() + public void AllConcreteTypesSourceResolvesOptionalParams() { - var container = CreateResolveAnythingContainer(); + var cb = new ContainerBuilder(); + cb.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource()); + var container = cb.Build(); - // The RS for Func<> is getting the NotRegisteredType from the resolve-anything source - Assert.True(container.IsRegistered>()); - Assert.Equal(1, container.Resolve>>().Count()); + var resolved = container.Resolve(); + + Assert.Equal("MyString", resolved.StringParam); } [Fact] - public void AServiceProvideByAnotherRegistrationSourceWillNotBeProvided() + public void AServiceAlreadyRegisteredWillNotBeProvided() { var cb = new ContainerBuilder(); cb.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource()); - cb.RegisterSource(new ObjectRegistrationSource()); + cb.RegisterType(); var container = cb.Build(); - Assert.True(container.IsRegistered()); + Assert.True(container.IsRegistered()); Assert.Equal(1, container.Resolve>().Count()); } - public class RegisteredType - { - } - [Fact] - public void AServiceAlreadyRegisteredWillNotBeProvided() + public void AServiceProvideByAnotherRegistrationSourceWillNotBeProvided() { var cb = new ContainerBuilder(); cb.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource()); - cb.RegisterType(); + cb.RegisterSource(new ObjectRegistrationSource()); var container = cb.Build(); - Assert.True(container.IsRegistered()); + Assert.True(container.IsRegistered()); Assert.Equal(1, container.Resolve>().Count()); } @@ -90,14 +84,18 @@ public void AServiceRegisteredBeforeTheSourceWillNotBeProvided() Assert.Equal(1, container.Resolve>().Count()); } - [Fact] - public void TypesIgnoredUsingPredicateAreNotResolvedFromTheContainer() + [Theory] + [InlineData(typeof(string))] + [InlineData(typeof(IInterfaceType))] + [InlineData(typeof(AbstractType))] + [InlineData(typeof(Delegate))] + [InlineData(typeof(MulticastDelegate))] + [InlineData(typeof(Tuple<>))] + public void IgnoresTypesThatShouldNotBeProvided(Type serviceType) { - var cb = new ContainerBuilder(); - var registrationSource = new AnyConcreteTypeNotAlreadyRegisteredSource(t => !t.IsAssignableTo()); - cb.RegisterSource(registrationSource); - var container = cb.Build(); - Assert.False(container.IsRegistered()); + var source = new AnyConcreteTypeNotAlreadyRegisteredSource(); + var service = new TypedService(serviceType); + Assert.False(source.RegistrationsFor(service, s => Enumerable.Empty()).Any(), $"Failed: {serviceType}"); } [Fact] @@ -136,41 +134,92 @@ public void LifetimeCanBeSingleInstance() } } - public class RegisterTypeWithCtorParam + [Fact] + public void TypesFromTheRegistrationSourceAreProvidedToOtherSources() { - public RegisterTypeWithCtorParam(string stringParam = "MyString") - { - StringParam = stringParam; - } + var container = CreateResolveAnythingContainer(); - public string StringParam { get; } + // The RS for Func<> is getting the NotRegisteredType from the resolve-anything source + Assert.True(container.IsRegistered>()); + Assert.Equal(1, container.Resolve>>().Count()); + Assert.True(container.IsRegistered>()); + Assert.True(container.IsRegistered>()); + Assert.True(container.IsRegistered>()); + } + + [Theory] + [InlineData(typeof(Func))] + [InlineData(typeof(Owned))] + [InlineData(typeof(Meta))] + [InlineData(typeof(Lazy))] + [InlineData(typeof(Tuple))] + public void IgnoredTypesFromTheRegistrationSourceAreNotProvidedToOtherSources(Type serviceType) + { + // Issue #495: Meta not correctly handled with ACTNARS. + var container = CreateResolveAnythingContainer(); + Assert.False(container.IsRegistered(serviceType), $"Failed: {serviceType}"); } [Fact] - public void AllConcreteTypesSourceResolvesOptionalParams() + public void DoesNotInterfereWithMetadata() { + // Issue #495: Meta not correctly handled with ACTNARS. var cb = new ContainerBuilder(); + cb.RegisterType().WithMetadata("value", 1); cb.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource()); var container = cb.Build(); - var resolved = container.Resolve(); + var regType = container.Resolve>(); + Assert.Equal(1, regType.Metadata["value"]); - Assert.Equal("MyString", resolved.StringParam); + var nonRegType = container.Resolve>(); + Assert.False(nonRegType.Metadata.ContainsKey("value")); + + var interfaceMeta = container.Resolve>>(); + Assert.Equal(0, interfaceMeta.Count()); + + var classMeta = container.Resolve>>(); + Assert.Equal(1, classMeta.Count()); } [Fact] - public void AllConcreteTypesSourceAlreadyRegisteredResolvesOptionalParams() + public void ConstructableClosedGenericsCanBeResolved() { - var cb = new ContainerBuilder(); + var container = CreateResolveAnythingContainer(); + Assert.True(container.IsRegistered>()); + Assert.NotNull(container.Resolve>()); + } - // Concrete type is already registered, but still errors - cb.RegisterType(); + [Fact] + public void WorksWithOpenGenericClassRegistrations() + { + var cb = new ContainerBuilder(); + cb.RegisterGeneric(typeof(Progress<>)).AsSelf(); cb.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource()); var container = cb.Build(); + Assert.True(container.IsRegistered>()); + Assert.NotNull(container.Resolve>()); + } - var resolved = container.Resolve(); + [Fact] + public void WorksWithOpenGenericInterfaceRegistrations() + { + var cb = new ContainerBuilder(); + cb.RegisterGeneric(typeof(Progress<>)).As(typeof(IProgress<>)); + cb.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource()); + var container = cb.Build(); + Assert.True(container.IsRegistered>()); + Assert.NotNull(container.Resolve>()); + } - Assert.Equal("MyString", resolved.StringParam); + [Fact] + public void TypesIgnoredUsingPredicateAreNotResolvedFromTheContainer() + { + var cb = new ContainerBuilder(); + var registrationSource = new AnyConcreteTypeNotAlreadyRegisteredSource(t => !t.IsAssignableTo()); + cb.RegisterSource(registrationSource); + var container = cb.Build(); + Assert.False(container.IsRegistered()); } private static IContainer CreateResolveAnythingContainer() @@ -179,5 +228,27 @@ private static IContainer CreateResolveAnythingContainer() cb.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource()); return cb.Build(); } + + public abstract class AbstractType + { + } + + public class NotRegisteredType + { + } + + public class RegisteredType + { + } + + public class RegisterTypeWithCtorParam + { + public RegisterTypeWithCtorParam(string stringParam = "MyString") + { + this.StringParam = stringParam; + } + + public string StringParam { get; } + } } }