diff --git a/test/Autofac.Specification.Test/Features/DecoratorTests.cs b/test/Autofac.Specification.Test/Features/DecoratorTests.cs new file mode 100644 index 000000000..9a27cf248 --- /dev/null +++ b/test/Autofac.Specification.Test/Features/DecoratorTests.cs @@ -0,0 +1,708 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Autofac.Features.Decorators; +using Xunit; + +namespace Autofac.Specification.Test.Features +{ + public class DecoratorTests + { + private interface IDecoratedService : IService + { + IDecoratedService Decorated { get; } + } + + private interface IDecoratorWithContext + { + IDecoratorContext Context { get; } + } + + private interface IDecoratorWithParameter + { + string Parameter { get; } + } + + private interface IService + { + } + + private interface ISomeOtherService + { + } + + [Fact] + public void CanApplyDecoratorConditionallyAtRuntime() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterDecorator(context => context.AppliedDecorators.Any()); + builder.RegisterDecorator(); + var container = builder.Build(); + + var instance = container.Resolve(); + + Assert.IsType(instance); + Assert.IsType(instance.Decorated); + } + + [Fact] + public void CanInjectDecoratorContextAsSnapshot() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterDecorator(); + builder.RegisterDecorator(); + builder.RegisterDecorator(); + builder.RegisterDecorator(); + var container = builder.Build(); + + var instance = container.Resolve(); + + var contextB = ((IDecoratorWithContext)instance).Context; + Assert.Equal(typeof(IDecoratedService), contextB.ServiceType); + Assert.Equal(typeof(ImplementorA), contextB.ImplementationType); + Assert.IsType(contextB.CurrentInstance); + Assert.Collection( + contextB.AppliedDecorators, + item => Assert.IsType(item), + item => Assert.IsType(item), + item => Assert.IsType(item)); + Assert.Collection( + contextB.AppliedDecoratorTypes, + item => Assert.Equal(typeof(DecoratorA), item), + item => Assert.Equal(typeof(DecoratorB), item), + item => Assert.Equal(typeof(DecoratorWithContextA), item)); + + var contextA = ((IDecoratorWithContext)instance.Decorated).Context; + Assert.Equal(typeof(IDecoratedService), contextA.ServiceType); + Assert.Equal(typeof(ImplementorA), contextA.ImplementationType); + Assert.IsType(contextA.CurrentInstance); + Assert.Collection( + contextA.AppliedDecorators, + item => Assert.IsType(item), + item => Assert.IsType(item)); + Assert.Collection( + contextA.AppliedDecoratorTypes, + item => Assert.Equal(typeof(DecoratorA), item), + item => Assert.Equal(typeof(DecoratorB), item)); + } + + [Fact] + public void CanResolveDecoratorWithFunc() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterDecorator(); + var container = builder.Build(); + + var factory = container.Resolve>(); + + Assert.IsType(factory()); + } + + [Fact] + public void CanResolveDecoratorWithLazy() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterDecorator(); + var container = builder.Build(); + + var lazy = container.Resolve>(); + + Assert.IsType(lazy.Value); + } + + [Fact] + public void CanResolveMultipleDecoratedServices() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterDecorator(); + var container = builder.Build(); + + var services = container.Resolve>(); + + Assert.Collection( + services, + s => + { + Assert.IsType(s); + Assert.IsType(s.Decorated); + }, + s => + { + Assert.IsType(s); + Assert.IsType(s.Decorated); + }); + } + + [Fact(Skip = "Cannot currently determine requested resolve service type")] + public void DecoratedRegistrationCanIncludeImplementationType() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As().AsSelf(); + builder.RegisterDecorator(); + var container = builder.Build(); + + Assert.IsType(container.Resolve()); + } + + [Fact] + public void DecoratorAndDecoratedBothDisposedWhenInstancePerDependency() + { + var builder = new ContainerBuilder(); + builder.RegisterType(). + As() + .InstancePerDependency(); + builder.RegisterDecorator(); + var container = builder.Build(); + + DisposableDecorator decorator; + DisposableImplementor decorated; + + using (var scope = container.BeginLifetimeScope()) + { + var instance = scope.Resolve(); + decorator = (DisposableDecorator)instance; + decorated = (DisposableImplementor)instance.Decorated; + } + + Assert.Equal(1, decorator.DisposeCallCount); + Assert.Equal(1, decorated.DisposeCallCount); + } + + [Fact] + public void DecoratorAndDecoratedBothDisposedWhenInstancePerLifetimeScope() + { + var builder = new ContainerBuilder(); + builder.RegisterType() + .As() + .InstancePerLifetimeScope(); + builder.RegisterDecorator(); + var container = builder.Build(); + + DisposableDecorator decorator; + DisposableImplementor decorated; + + using (var scope = container.BeginLifetimeScope()) + { + var instance = scope.Resolve(); + decorator = (DisposableDecorator)instance; + decorated = (DisposableImplementor)instance.Decorated; + } + + Assert.Equal(1, decorator.DisposeCallCount); + Assert.Equal(1, decorated.DisposeCallCount); + } + + [Fact] + public void DecoratorAndDecoratedBothDisposedWhenInstancePerMatchingLifetimeScope() + { + const string tag = "foo"; + + var builder = new ContainerBuilder(); + + builder.RegisterType() + .As() + .InstancePerMatchingLifetimeScope(tag); + builder.RegisterDecorator(); + var container = builder.Build(); + + DisposableDecorator decorator; + DisposableImplementor decorated; + + using (var scope = container.BeginLifetimeScope(tag)) + { + var instance = scope.Resolve(); + decorator = (DisposableDecorator)instance; + decorated = (DisposableImplementor)instance.Decorated; + + DisposableDecorator decorator2; + DisposableImplementor decorated2; + + using (var scope2 = scope.BeginLifetimeScope()) + { + var instance2 = scope2.Resolve(); + decorator2 = (DisposableDecorator)instance2; + decorated2 = (DisposableImplementor)instance2.Decorated; + } + + Assert.Equal(0, decorator2.DisposeCallCount); + Assert.Equal(0, decorated2.DisposeCallCount); + } + + Assert.Equal(1, decorator.DisposeCallCount); + Assert.Equal(1, decorated.DisposeCallCount); + } + + [Fact] + public void DecoratorAndDecoratedBothDisposedWhenSingleInstance() + { + var builder = new ContainerBuilder(); + + builder.RegisterType() + .As() + .SingleInstance(); + builder.RegisterDecorator(); + var container = builder.Build(); + + var instance = container.Resolve(); + container.Dispose(); + + var decorator = (DisposableDecorator)instance; + var decorated = (DisposableImplementor)instance.Decorated; + + Assert.Equal(1, decorator.DisposeCallCount); + Assert.Equal(1, decorated.DisposeCallCount); + } + + [Fact] + public void DecoratorCanBeAppliedToServiceRegisteredInChildLifetimeScope() + { + var builder = new ContainerBuilder(); + builder.RegisterDecorator(); + var container = builder.Build(); + + var scope = container.BeginLifetimeScope(b => b.RegisterType().As()); + var instance = scope.Resolve(); + + Assert.IsType(instance); + } + + [Fact] + public void DecoratorCanBeRegisteredInChildLifetimeScope() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + var container = builder.Build(); + + var scope = container.BeginLifetimeScope(b => b.RegisterDecorator()); + + var scopedInstance = scope.Resolve(); + Assert.IsType(scopedInstance); + + var rootInstance = container.Resolve(); + Assert.IsType(rootInstance); + } + + [Fact] + public void DecoratorInheritsDecoratedLifetimeWhenInstancePerDependency() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As().InstancePerDependency(); + builder.RegisterDecorator(); + + var container = builder.Build(); + + var first = container.Resolve(); + var second = container.Resolve(); + Assert.NotSame(first, second); + + using (var scope = container.BeginLifetimeScope()) + { + var third = scope.Resolve(); + Assert.NotSame(first, third); + Assert.NotSame(second, third); + } + } + + [Fact] + public void DecoratorInheritsDecoratedLifetimeWhenInstancePerLifetimeScope() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterDecorator(); + + var container = builder.Build(); + + var first = container.Resolve(); + var second = container.Resolve(); + Assert.Same(first, second); + + using (var scope = container.BeginLifetimeScope()) + { + var third = scope.Resolve(); + Assert.NotSame(first, third); + + var forth = scope.Resolve(); + Assert.Same(third, forth); + } + } + + [Fact] + public void DecoratorInheritsDecoratedLifetimeWhenInstancePerMatchingLifetimeScope() + { + const string tag = "foo"; + + var builder = new ContainerBuilder(); + builder.RegisterType() + .As() + .InstancePerMatchingLifetimeScope(tag); + builder.RegisterDecorator(); + + var container = builder.Build(); + + using (var scope = container.BeginLifetimeScope(tag)) + { + var first = scope.Resolve(); + var second = scope.Resolve(); + Assert.Same(first, second); + + using (var scope2 = scope.BeginLifetimeScope()) + { + var third = scope2.Resolve(); + Assert.Same(second, third); + } + } + } + + [Fact] + public void DecoratorInheritsDecoratedLifetimeWhenSingleInstance() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterDecorator(); + + var container = builder.Build(); + + var instance = container.Resolve(); + Assert.Same(instance, container.Resolve()); + + using (var scope = container.BeginLifetimeScope()) + { + Assert.Same(instance, scope.Resolve()); + } + } + + [Fact] + public void DecoratorRegisteredAsLambdaCanAcceptAdditionalParameters() + { + const string parameterName = "parameter"; + const string parameterValue = "ABC"; + + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterDecorator((c, p, i) => + { + var stringParameter = (string)p + .OfType() + .FirstOrDefault(np => np.Name == parameterName)?.Value; + + return new DecoratorWithParameter(i, stringParameter); + }); + builder.RegisterDecorator(); + var container = builder.Build(); + + var parameter = new NamedParameter(parameterName, parameterValue); + var instance = container.Resolve(parameter); + + Assert.Equal(parameterValue, ((DecoratorWithParameter)instance.Decorated).Parameter); + } + + [Fact] + public void DecoratorRegisteredAsLambdaCanBeResolved() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterDecorator((c, p, i) => new DecoratorA(i)); + var container = builder.Build(); + + var instance = container.Resolve(); + + Assert.IsType(instance); + Assert.IsType(instance.Decorated); + } + + [Fact] + public void DecoratorRegistrationsGetAppliedInOrderAdded() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterDecorator(); + builder.RegisterDecorator(); + var container = builder.Build(); + + var instance = container.Resolve(); + + Assert.IsType(instance); + Assert.IsType(instance.Decorated); + Assert.IsType(instance.Decorated.Decorated); + + builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterDecorator(); + builder.RegisterDecorator(); + container = builder.Build(); + + instance = container.Resolve(); + + Assert.IsType(instance); + Assert.IsType(instance.Decorated); + Assert.IsType(instance.Decorated.Decorated); + } + + [Fact] + public void DecoratorsApplyToKeyedServices() + { + var builder = new ContainerBuilder(); + + builder.RegisterType().Keyed("service"); + builder.RegisterDecorator(); + var container = builder.Build(); + + var instance = container.ResolveKeyed("service"); + + Assert.IsType(instance); + Assert.IsType(instance.Decorated); + } + + [Theory] + [InlineData(typeof(IDecoratedService), typeof(ISomeOtherService))] + [InlineData(typeof(ISomeOtherService), typeof(IDecoratedService))] + public void DecoratorShouldBeAppliedRegardlessOfServiceOrder(Type firstService, Type secondService) + { + var builder = new ContainerBuilder(); + + builder.RegisterType().As(firstService, secondService); + builder.RegisterDecorator(); + var container = builder.Build(); + + var instance = container.Resolve(); + + Assert.IsType(instance); + Assert.IsType(instance.Decorated); + } + + [Fact] + public void ParametersArePassedThroughDecoratorChain() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterDecorator(); + builder.RegisterDecorator(); + var container = builder.Build(); + + var parameter = new NamedParameter("parameter", "ABC"); + var instance = container.Resolve(parameter); + + Assert.Equal("ABC", ((DecoratorWithParameter)instance.Decorated).Parameter); + } + + [Fact] + public void ParametersCanBeConfiguredOnDecoratedService() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As().WithParameter("parameter", "ABC"); + builder.RegisterDecorator(); + builder.RegisterDecorator(); + var container = builder.Build(); + + var instance = container.Resolve(); + + Assert.Equal("ABC", ((ImplementorWithParameters)instance.Decorated.Decorated).Parameter); + } + + [Fact] + public void ResolvesDecoratedServiceWhenMultipleDecoratorRegistered() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterDecorator(); + builder.RegisterDecorator(); + var container = builder.Build(); + + var instance = container.Resolve(); + + Assert.IsType(instance); + Assert.IsType(instance.Decorated); + Assert.IsType(instance.Decorated.Decorated); + } + + [Fact] + public void ResolvesDecoratedServiceWhenNoDecoratorsRegistered() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + var container = builder.Build(); + + var instance = container.Resolve(); + + Assert.IsType(instance); + } + + [Fact] + public void ResolvesDecoratedServiceWhenRegisteredWithoutGenericConstraint() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterDecorator(typeof(DecoratorA), typeof(IDecoratedService)); + builder.RegisterDecorator(typeof(DecoratorB), typeof(IDecoratedService)); + var container = builder.Build(); + + var instance = container.Resolve(); + + Assert.IsType(instance); + Assert.IsType(instance.Decorated); + Assert.IsType(instance.Decorated.Decorated); + } + + [Fact] + public void ResolvesDecoratedServiceWhenSingleDecoratorRegistered() + { + var builder = new ContainerBuilder(); + + builder.RegisterType().As(); + builder.RegisterDecorator(); + var container = builder.Build(); + + var instance = container.Resolve(); + + Assert.IsType(instance); + Assert.IsType(instance.Decorated); + } + + [Fact] + public void StartableTypesCanBeDecorated() + { + var builder = new ContainerBuilder(); + builder.RegisterType() + .As() + .As() + .SingleInstance(); + builder.RegisterDecorator(); + var container = builder.Build(); + + var decorated = Assert.IsType(container.Resolve()); + var instance = Assert.IsType(decorated.Decorated); + Assert.True(instance.Started); + } + + private abstract class Decorator : IDecoratedService + { + protected Decorator(IDecoratedService decorated) + { + this.Decorated = decorated; + } + + public IDecoratedService Decorated { get; } + } + + private class DecoratorA : Decorator + { + public DecoratorA(IDecoratedService decorated) + : base(decorated) + { + } + } + + private class DecoratorB : Decorator + { + public DecoratorB(IDecoratedService decorated) + : base(decorated) + { + } + } + + private class DecoratorWithContextA : Decorator, IDecoratorWithContext + { + public DecoratorWithContextA(IDecoratedService decorated, IDecoratorContext context) + : base(decorated) + { + this.Context = context; + } + + public IDecoratorContext Context { get; } + } + + private class DecoratorWithContextB : Decorator, IDecoratorWithContext + { + public DecoratorWithContextB(IDecoratedService decorated, IDecoratorContext context) + : base(decorated) + { + this.Context = context; + } + + public IDecoratorContext Context { get; } + } + + private class DecoratorWithParameter : Decorator, IDecoratorWithParameter + { + public DecoratorWithParameter(IDecoratedService decorated, string parameter) + : base(decorated) + { + this.Parameter = parameter; + } + + public string Parameter { get; } + } + + private class DisposableDecorator : Decorator, IDisposable + { + public DisposableDecorator(IDecoratedService decorated) + : base(decorated) + { + } + + public int DisposeCallCount { get; private set; } + + public void Dispose() + { + this.DisposeCallCount++; + } + } + + private class DisposableImplementor : IDecoratedService, IDisposable + { + public IDecoratedService Decorated => this; + + public int DisposeCallCount { get; private set; } + + public void Dispose() + { + this.DisposeCallCount++; + } + } + + private class ImplementorA : IDecoratedService + { + public IDecoratedService Decorated => this; + } + + private class ImplementorB : IDecoratedService + { + public IDecoratedService Decorated => this; + } + + private class ImplementorWithParameters : IDecoratedService + { + public ImplementorWithParameters(string parameter) + { + this.Parameter = parameter; + } + + public IDecoratedService Decorated => this; + + public string Parameter { get; } + } + + private class ImplementorWithSomeOtherService : IDecoratedService, ISomeOtherService + { + public IDecoratedService Decorated => this; + } + + private class StartableImplementation : IDecoratedService, IStartable + { + public IDecoratedService Decorated => this; + + public bool Started { get; private set; } + + public void Start() + { + this.Started = true; + } + } + } +} diff --git a/test/Autofac.Test/Features/Decorators/DecoratorTests.cs b/test/Autofac.Test/Features/Decorators/DecoratorTests.cs index 485dac84c..b8e2d0d82 100644 --- a/test/Autofac.Test/Features/Decorators/DecoratorTests.cs +++ b/test/Autofac.Test/Features/Decorators/DecoratorTests.cs @@ -1,201 +1,19 @@ using System; -using System.Collections.Generic; using System.Linq; using Autofac.Core; -using Autofac.Features.Decorators; using Xunit; namespace Autofac.Test.Features.Decorators { public class DecoratorTests { - public interface IService - { - } - - public interface ISomeOtherService - { - } - - public interface IDecoratedService : IService + private interface IDecoratedService : IService { IDecoratedService Decorated { get; } } - public class ImplementorA : IDecoratedService - { - public IDecoratedService Decorated => this; - } - - public class ImplementorB : IDecoratedService - { - public IDecoratedService Decorated => this; - } - - public class ImplementorWithParameters : IDecoratedService + private interface IService { - public IDecoratedService Decorated => this; - - public string Parameter { get; } - - public ImplementorWithParameters(string parameter) - { - Parameter = parameter; - } - } - - public class ImplementorWithSomeOtherService : IDecoratedService, ISomeOtherService - { - public IDecoratedService Decorated => this; - } - - public abstract class Decorator : IDecoratedService - { - protected Decorator(IDecoratedService decorated) - { - Decorated = decorated; - } - - public IDecoratedService Decorated { get; } - } - - public class DecoratorA : Decorator - { - public DecoratorA(IDecoratedService decorated) - : base(decorated) - { - } - } - - public class DecoratorB : Decorator - { - public DecoratorB(IDecoratedService decorated) - : base(decorated) - { - } - } - - public interface IDecoratorWithParameter - { - string Parameter { get; } - } - - public class DecoratorWithParameter : Decorator, IDecoratorWithParameter - { - public DecoratorWithParameter(IDecoratedService decorated, string parameter) - : base(decorated) - { - Parameter = parameter; - } - - public string Parameter { get; } - } - - public interface IDecoratorWithContext - { - IDecoratorContext Context { get; } - } - - public class DecoratorWithContextA : Decorator, IDecoratorWithContext - { - public DecoratorWithContextA(IDecoratedService decorated, IDecoratorContext context) - : base(decorated) - { - Context = context; - } - - public IDecoratorContext Context { get; } - } - - public class DecoratorWithContextB : Decorator, IDecoratorWithContext - { - public DecoratorWithContextB(IDecoratedService decorated, IDecoratorContext context) - : base(decorated) - { - Context = context; - } - - public IDecoratorContext Context { get; } - } - - public class StartableImplementation : IDecoratedService, IStartable - { - public IDecoratedService Decorated => this; - - public bool Started { get; private set; } - - public void Start() - { - this.Started = true; - } - } - - public class DisposableImplementor : IDecoratedService, IDisposable - { - public int DisposeCallCount { get; private set; } - - public IDecoratedService Decorated => this; - - public void Dispose() - { - DisposeCallCount++; - } - } - - public class DisposableDecorator : Decorator, IDisposable - { - public int DisposeCallCount { get; private set; } - - public DisposableDecorator(IDecoratedService decorated) - : base(decorated) - { - } - - public void Dispose() - { - DisposeCallCount++; - } - } - - [Fact] - public void RegistrationIncludesTheServiceType() - { - var builder = new ContainerBuilder(); - - builder.RegisterType().As(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var registration = container.RegistrationFor(); - Assert.NotNull(registration); - - var decoratedService = new TypedService(typeof(IDecoratedService)); - Assert.Contains(registration.Services.OfType(), s => s == decoratedService); - } - - [Fact] - public void RegistrationTargetsTheImplementationType() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var registration = container.RegistrationFor(); - - Assert.NotNull(registration); - Assert.Equal(typeof(ImplementorA), registration.Target.Activator.LimitType); - } - - [Fact(Skip ="Cannot currently determine requested resolve service type")] - public void DecoratedRegistrationCanIncludeImplementationType() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As().AsSelf(); - builder.RegisterDecorator(); - var container = builder.Build(); - - Assert.IsType(container.Resolve()); } [Fact] @@ -221,541 +39,56 @@ public void DecoratedRegistrationCanIncludeOtherServices() } [Fact] - public void ResolvesDecoratedServiceWhenNoDecoratorsRegistered() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As(); - var container = builder.Build(); - - var instance = container.Resolve(); - - Assert.IsType(instance); - } - - [Fact] - public void ResolvesDecoratedServiceWhenSingleDecoratorRegistered() - { - var builder = new ContainerBuilder(); - - builder.RegisterType().As(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var instance = container.Resolve(); - - Assert.IsType(instance); - Assert.IsType(instance.Decorated); - } - - [Fact] - public void DecoratorRegisteredAsLambdaCanBeResolved() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterDecorator((c, p, i) => new DecoratorA(i)); - var container = builder.Build(); - - var instance = container.Resolve(); - - Assert.IsType(instance); - Assert.IsType(instance.Decorated); - } - - [Fact] - public void DecoratorRegisteredAsLambdaCanAcceptAdditionalParameters() - { - const string parameterName = "parameter"; - const string parameterValue = "ABC"; - - var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterDecorator((c, p, i) => - { - var stringParameter = (string)p - .OfType() - .FirstOrDefault(np => np.Name == parameterName)?.Value; - - return new DecoratorWithParameter(i, stringParameter); - }); - builder.RegisterDecorator(); - var container = builder.Build(); - - var parameter = new NamedParameter(parameterName, parameterValue); - var instance = container.Resolve(parameter); - - Assert.Equal(parameterValue, ((DecoratorWithParameter)instance.Decorated).Parameter); - } - - [Fact] - public void ResolvesDecoratedServiceWhenMultipleDecoratorRegistered() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterDecorator(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var instance = container.Resolve(); - - Assert.IsType(instance); - Assert.IsType(instance.Decorated); - Assert.IsType(instance.Decorated.Decorated); - } - - [Fact] - public void CanResolveMultipleDecoratedServices() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var services = container.Resolve>(); - - Assert.Collection( - services, - s => - { - Assert.IsType(s); - Assert.IsType(s.Decorated); - }, - s => - { - Assert.IsType(s); - Assert.IsType(s.Decorated); - }); - } - - [Fact] - public void CanResolveDecoratorWithFunc() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var factory = container.Resolve>(); - - Assert.IsType(factory()); - } - - [Fact] - public void CanResolveDecoratorWithLazy() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var lazy = container.Resolve>(); - - Assert.IsType(lazy.Value); - } - - [Fact] - public void ResolvesDecoratedServiceWhenRegisteredWithoutGenericConstraint() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterDecorator(typeof(DecoratorA), typeof(IDecoratedService)); - builder.RegisterDecorator(typeof(DecoratorB), typeof(IDecoratedService)); - var container = builder.Build(); - - var instance = container.Resolve(); - - Assert.IsType(instance); - Assert.IsType(instance.Decorated); - Assert.IsType(instance.Decorated.Decorated); - } - - [Fact] - public void DecoratorRegistrationsGetAppliedInOrderAdded() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterDecorator(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var instance = container.Resolve(); - - Assert.IsType(instance); - Assert.IsType(instance.Decorated); - Assert.IsType(instance.Decorated.Decorated); - - builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterDecorator(); - builder.RegisterDecorator(); - container = builder.Build(); - - instance = container.Resolve(); - - Assert.IsType(instance); - Assert.IsType(instance.Decorated); - Assert.IsType(instance.Decorated.Decorated); - } - - [Fact] - public void CanApplyDecoratorConditionallyAtRuntime() + public void RegistrationIncludesTheServiceType() { var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterDecorator(context => context.AppliedDecorators.Any()); - builder.RegisterDecorator(); - var container = builder.Build(); - - var instance = container.Resolve(); - - Assert.IsType(instance); - Assert.IsType(instance.Decorated); - } - [Fact] - public void CanInjectDecoratorContextAsSnapshot() - { - var builder = new ContainerBuilder(); builder.RegisterType().As(); builder.RegisterDecorator(); - builder.RegisterDecorator(); - builder.RegisterDecorator(); - builder.RegisterDecorator(); var container = builder.Build(); - var instance = container.Resolve(); - - var contextB = ((IDecoratorWithContext)instance).Context; - Assert.Equal(typeof(IDecoratedService), contextB.ServiceType); - Assert.Equal(typeof(ImplementorA), contextB.ImplementationType); - Assert.IsType(contextB.CurrentInstance); - Assert.Collection( - contextB.AppliedDecorators, - item => Assert.IsType(item), - item => Assert.IsType(item), - item => Assert.IsType(item)); - Assert.Collection( - contextB.AppliedDecoratorTypes, - item => Assert.Equal(typeof(DecoratorA), item), - item => Assert.Equal(typeof(DecoratorB), item), - item => Assert.Equal(typeof(DecoratorWithContextA), item)); - - var contextA = ((IDecoratorWithContext)instance.Decorated).Context; - Assert.Equal(typeof(IDecoratedService), contextA.ServiceType); - Assert.Equal(typeof(ImplementorA), contextA.ImplementationType); - Assert.IsType(contextA.CurrentInstance); - Assert.Collection( - contextA.AppliedDecorators, - item => Assert.IsType(item), - item => Assert.IsType(item)); - Assert.Collection( - contextA.AppliedDecoratorTypes, - item => Assert.Equal(typeof(DecoratorA), item), - item => Assert.Equal(typeof(DecoratorB), item)); - } - - [Fact] - public void DecoratorInheritsDecoratedLifetimeWhenSingleInstance() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterDecorator(); - - var container = builder.Build(); - - var instance = container.Resolve(); - Assert.Same(instance, container.Resolve()); - - using (var scope = container.BeginLifetimeScope()) - { - Assert.Same(instance, scope.Resolve()); - } - } - - [Fact] - public void DecoratorInheritsDecoratedLifetimeWhenInstancePerDependency() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As().InstancePerDependency(); - builder.RegisterDecorator(); - - var container = builder.Build(); - - var first = container.Resolve(); - var second = container.Resolve(); - Assert.NotSame(first, second); - - using (var scope = container.BeginLifetimeScope()) - { - var third = scope.Resolve(); - Assert.NotSame(first, third); - Assert.NotSame(second, third); - } - } - - [Fact] - public void DecoratorInheritsDecoratedLifetimeWhenInstancePerLifetimeScope() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterDecorator(); - - var container = builder.Build(); - - var first = container.Resolve(); - var second = container.Resolve(); - Assert.Same(first, second); - - using (var scope = container.BeginLifetimeScope()) - { - var third = scope.Resolve(); - Assert.NotSame(first, third); - - var forth = scope.Resolve(); - Assert.Same(third, forth); - } - } - - [Fact] - public void DecoratorInheritsDecoratedLifetimeWhenInstancePerMatchingLifetimeScope() - { - const string tag = "foo"; - - var builder = new ContainerBuilder(); - builder.RegisterType() - .As() - .InstancePerMatchingLifetimeScope(tag); - builder.RegisterDecorator(); - - var container = builder.Build(); - - using (var scope = container.BeginLifetimeScope(tag)) - { - var first = scope.Resolve(); - var second = scope.Resolve(); - Assert.Same(first, second); - - using (var scope2 = scope.BeginLifetimeScope()) - { - var third = scope2.Resolve(); - Assert.Same(second, third); - } - } - } - - [Fact] - public void ParametersArePassedThroughDecoratorChain() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterDecorator(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var parameter = new NamedParameter("parameter", "ABC"); - var instance = container.Resolve(parameter); - - Assert.Equal("ABC", ((DecoratorWithParameter)instance.Decorated).Parameter); - } - - [Fact] - public void ParametersCanBeConfiguredOnDecoratedService() - { - var builder = new ContainerBuilder(); - builder.RegisterType().As().WithParameter("parameter", "ABC"); - builder.RegisterDecorator(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var instance = container.Resolve(); - - Assert.Equal("ABC", ((ImplementorWithParameters)instance.Decorated.Decorated).Parameter); - } - - [Fact] - public void DecoratorCanBeAppliedToServiceRegisteredInChildLifetimeScope() - { - var builder = new ContainerBuilder(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var scope = container.BeginLifetimeScope(b => b.RegisterType().As()); - var instance = scope.Resolve(); + var registration = container.RegistrationFor(); + Assert.NotNull(registration); - Assert.IsType(instance); + var decoratedService = new TypedService(typeof(IDecoratedService)); + Assert.Contains(registration.Services.OfType(), s => s == decoratedService); } [Fact] - public void DecoratorCanBeRegisteredInChildLifetimeScope() + public void RegistrationTargetsTheImplementationType() { var builder = new ContainerBuilder(); builder.RegisterType().As(); - var container = builder.Build(); - - var scope = container.BeginLifetimeScope(b => b.RegisterDecorator()); - - var scopedInstance = scope.Resolve(); - Assert.IsType(scopedInstance); - - var rootInstance = container.Resolve(); - Assert.IsType(rootInstance); - } - - [Fact] - public void StartableTypesCanBeDecorated() - { - var builder = new ContainerBuilder(); - builder.RegisterType() - .As() - .As() - .SingleInstance(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var decorated = Assert.IsType(container.Resolve()); - var instance = Assert.IsType(decorated.Decorated); - Assert.True(instance.Started); - } - - [Fact] - public void DecoratorsApplyToKeyedServices() - { - var builder = new ContainerBuilder(); - - builder.RegisterType().Keyed("service"); builder.RegisterDecorator(); var container = builder.Build(); - var instance = container.ResolveKeyed("service"); - - Assert.IsType(instance); - Assert.IsType(instance.Decorated); - } - - [Fact] - public void DecoratorAndDecoratedBothDisposedWhenInstancePerDependency() - { - var builder = new ContainerBuilder(); - builder.RegisterType(). - As() - .InstancePerDependency(); - builder.RegisterDecorator(); - var container = builder.Build(); - - DisposableDecorator decorator; - DisposableImplementor decorated; - - using (var scope = container.BeginLifetimeScope()) - { - var instance = scope.Resolve(); - decorator = (DisposableDecorator)instance; - decorated = (DisposableImplementor)instance.Decorated; - } + var registration = container.RegistrationFor(); - Assert.Equal(1, decorator.DisposeCallCount); - Assert.Equal(1, decorated.DisposeCallCount); + Assert.NotNull(registration); + Assert.Equal(typeof(ImplementorA), registration.Target.Activator.LimitType); } - [Fact] - public void DecoratorAndDecoratedBothDisposedWhenInstancePerLifetimeScope() + private abstract class Decorator : IDecoratedService { - var builder = new ContainerBuilder(); - builder.RegisterType() - .As() - .InstancePerLifetimeScope(); - builder.RegisterDecorator(); - var container = builder.Build(); - - DisposableDecorator decorator; - DisposableImplementor decorated; - - using (var scope = container.BeginLifetimeScope()) + protected Decorator(IDecoratedService decorated) { - var instance = scope.Resolve(); - decorator = (DisposableDecorator)instance; - decorated = (DisposableImplementor)instance.Decorated; + this.Decorated = decorated; } - Assert.Equal(1, decorator.DisposeCallCount); - Assert.Equal(1, decorated.DisposeCallCount); + public IDecoratedService Decorated { get; } } - [Fact] - public void DecoratorAndDecoratedBothDisposedWhenInstancePerMatchingLifetimeScope() + private class DecoratorA : Decorator { - const string tag = "foo"; - - var builder = new ContainerBuilder(); - - builder.RegisterType() - .As() - .InstancePerMatchingLifetimeScope(tag); - builder.RegisterDecorator(); - var container = builder.Build(); - - DisposableDecorator decorator; - DisposableImplementor decorated; - - using (var scope = container.BeginLifetimeScope(tag)) + public DecoratorA(IDecoratedService decorated) + : base(decorated) { - var instance = scope.Resolve(); - decorator = (DisposableDecorator)instance; - decorated = (DisposableImplementor)instance.Decorated; - - DisposableDecorator decorator2; - DisposableImplementor decorated2; - - using (var scope2 = scope.BeginLifetimeScope()) - { - var instance2 = scope2.Resolve(); - decorator2 = (DisposableDecorator)instance2; - decorated2 = (DisposableImplementor)instance2.Decorated; - } - - Assert.Equal(0, decorator2.DisposeCallCount); - Assert.Equal(0, decorated2.DisposeCallCount); } - - Assert.Equal(1, decorator.DisposeCallCount); - Assert.Equal(1, decorated.DisposeCallCount); } - [Fact] - public void DecoratorAndDecoratedBothDisposedWhenSingleInstance() + private class ImplementorA : IDecoratedService { - var builder = new ContainerBuilder(); - - builder.RegisterType() - .As() - .SingleInstance(); - builder.RegisterDecorator(); - var container = builder.Build(); - - var instance = container.Resolve(); - container.Dispose(); - - var decorator = (DisposableDecorator)instance; - var decorated = (DisposableImplementor)instance.Decorated; - - Assert.Equal(1, decorator.DisposeCallCount); - Assert.Equal(1, decorated.DisposeCallCount); - } - - [Theory] - [InlineData(typeof(IDecoratedService), typeof(ISomeOtherService))] - [InlineData(typeof(ISomeOtherService), typeof(IDecoratedService))] - public void DecoratorShouldBeAppliedRegardlessOfServiceOrder(Type firstService, Type secondService) - { - var builder = new ContainerBuilder(); - - builder.RegisterType().As(firstService, secondService); - builder.RegisterDecorator(); - var container = builder.Build(); - - var instance = container.Resolve(); - - Assert.IsType(instance); - Assert.IsType(instance.Decorated); + public IDecoratedService Decorated => this; } } }