Skip to content

Commit

Permalink
#495: Improved generic handling in ACTNARS so it no longer interferes…
Browse files Browse the repository at this point in the history
… with supported relationship types.
  • Loading branch information
Travis Illig committed Oct 26, 2016
1 parent 20077a0 commit 1aca241
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,39 @@ public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service,
Func<Service, IEnumerable<IComponentRegistration>> 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<IComponentRegistration>();
}

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<IComponentRegistration>();
}

if (typeInfo.IsGenericType)
{
foreach (var typeParameter in typeInfo.GenericTypeArguments)
{
if (!registrationAccessor(new TypedService(typeParameter)).Any())
{
return Enumerable.Empty<IComponentRegistration>();
}
}
}

var builder = RegistrationBuilder.ForType(ts.ServiceType);
RegistrationConfiguration?.Invoke(builder);
Expand Down
179 changes: 125 additions & 54 deletions test/Autofac.Test/Features/ResolveAnything/ResolveAnythingTests.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -9,7 +12,7 @@ namespace Autofac.Test.Features.ResolveAnything
{
public class ResolveAnythingTests
{
public class NotRegisteredType
public interface IInterfaceType
{
}

Expand All @@ -20,61 +23,52 @@ public void AConcreteTypeNotRegisteredWithTheContainerWillBeProvided()
Assert.True(container.IsRegistered<NotRegisteredType>());
}

public abstract class AbstractType
{
}

[Fact]
public void AnAbstractTypeNotRegisteredWithTheContainerWillNotBeProvided()
public void AllConcreteTypesSourceAlreadyRegisteredResolvesOptionalParams()
{
var container = CreateResolveAnythingContainer();
Assert.False(container.IsRegistered<AbstractType>());
}
var cb = new ContainerBuilder();

public interface IInterfaceType
{
}
// Concrete type is already registered, but still errors
cb.RegisterType<RegisterTypeWithCtorParam>();
cb.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
var container = cb.Build();

[Fact]
public void AnInterfaceTypeNotRegisteredWithTheContainerWillNotBeProvided()
{
var container = CreateResolveAnythingContainer();
Assert.False(container.IsRegistered<IInterfaceType>());
var resolved = container.Resolve<RegisterTypeWithCtorParam>();

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<Func<NotRegisteredType>>());
Assert.Equal(1, container.Resolve<IEnumerable<Func<NotRegisteredType>>>().Count());
var resolved = container.Resolve<RegisterTypeWithCtorParam>();

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<RegisteredType>();
var container = cb.Build();
Assert.True(container.IsRegistered<object>());
Assert.True(container.IsRegistered<RegisteredType>());
Assert.Equal(1, container.Resolve<IEnumerable<object>>().Count());
}

public class RegisteredType
{
}

[Fact]
public void AServiceAlreadyRegisteredWillNotBeProvided()
public void AServiceProvideByAnotherRegistrationSourceWillNotBeProvided()
{
var cb = new ContainerBuilder();
cb.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
cb.RegisterType<RegisteredType>();
cb.RegisterSource(new ObjectRegistrationSource());
var container = cb.Build();
Assert.True(container.IsRegistered<RegisteredType>());
Assert.True(container.IsRegistered<object>());
Assert.Equal(1, container.Resolve<IEnumerable<object>>().Count());
}

Expand All @@ -90,14 +84,18 @@ public void AServiceRegisteredBeforeTheSourceWillNotBeProvided()
Assert.Equal(1, container.Resolve<IEnumerable<object>>().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<string>());
cb.RegisterSource(registrationSource);
var container = cb.Build();
Assert.False(container.IsRegistered<string>());
var source = new AnyConcreteTypeNotAlreadyRegisteredSource();
var service = new TypedService(serviceType);
Assert.False(source.RegistrationsFor(service, s => Enumerable.Empty<IComponentRegistration>()).Any(), $"Failed: {serviceType}");
}

[Fact]
Expand Down Expand Up @@ -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<Func<NotRegisteredType>>());
Assert.Equal(1, container.Resolve<IEnumerable<Func<NotRegisteredType>>>().Count());
Assert.True(container.IsRegistered<Owned<NotRegisteredType>>());
Assert.True(container.IsRegistered<Meta<NotRegisteredType>>());
Assert.True(container.IsRegistered<Lazy<NotRegisteredType>>());
}

[Theory]
[InlineData(typeof(Func<IInterfaceType>))]
[InlineData(typeof(Owned<IInterfaceType>))]
[InlineData(typeof(Meta<IInterfaceType>))]
[InlineData(typeof(Lazy<IInterfaceType>))]
[InlineData(typeof(Tuple<IInterfaceType>))]
public void IgnoredTypesFromTheRegistrationSourceAreNotProvidedToOtherSources(Type serviceType)
{
// Issue #495: Meta<T> 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<T> not correctly handled with ACTNARS.
var cb = new ContainerBuilder();
cb.RegisterType<RegisteredType>().WithMetadata("value", 1);
cb.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
var container = cb.Build();

var resolved = container.Resolve<RegisterTypeWithCtorParam>();
var regType = container.Resolve<Meta<RegisteredType>>();
Assert.Equal(1, regType.Metadata["value"]);

Assert.Equal("MyString", resolved.StringParam);
var nonRegType = container.Resolve<Meta<NotRegisteredType>>();
Assert.False(nonRegType.Metadata.ContainsKey("value"));

var interfaceMeta = container.Resolve<IEnumerable<Meta<IInterfaceType>>>();
Assert.Equal(0, interfaceMeta.Count());

var classMeta = container.Resolve<IEnumerable<Meta<NotRegisteredType>>>();
Assert.Equal(1, classMeta.Count());
}

[Fact]
public void AllConcreteTypesSourceAlreadyRegisteredResolvesOptionalParams()
public void ConstructableClosedGenericsCanBeResolved()
{
var cb = new ContainerBuilder();
var container = CreateResolveAnythingContainer();
Assert.True(container.IsRegistered<Tuple<Exception>>());
Assert.NotNull(container.Resolve<Tuple<Exception>>());
}

// Concrete type is already registered, but still errors
cb.RegisterType<RegisterTypeWithCtorParam>();
[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<Progress<Exception>>());
Assert.NotNull(container.Resolve<Progress<Exception>>());
}

var resolved = container.Resolve<RegisterTypeWithCtorParam>();
[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<IProgress<Exception>>());
Assert.NotNull(container.Resolve<IProgress<Exception>>());
}

Assert.Equal("MyString", resolved.StringParam);
[Fact]
public void TypesIgnoredUsingPredicateAreNotResolvedFromTheContainer()
{
var cb = new ContainerBuilder();
var registrationSource = new AnyConcreteTypeNotAlreadyRegisteredSource(t => !t.IsAssignableTo<string>());
cb.RegisterSource(registrationSource);
var container = cb.Build();
Assert.False(container.IsRegistered<string>());
}

private static IContainer CreateResolveAnythingContainer()
Expand All @@ -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; }
}
}
}

0 comments on commit 1aca241

Please sign in to comment.