From 4ca309a193137db944056120350bd3a85be9863e Mon Sep 17 00:00:00 2001 From: Travis Illig Date: Thu, 14 Mar 2019 12:28:25 -0700 Subject: [PATCH] Split lifetime scope tests up. --- .../Features/CircularDependencyTests.cs | 26 + ...DisposalOrderTests.cs => DisposalTests.cs} | 47 +- .../Lifetime/NestedScopeTests.cs | 25 + .../Lifetime/SingleInstanceTests.cs | 53 +- .../NestedScopeRegistrationTests.cs | 254 ++++++++- .../Registration/PropertyDictionaryTests.cs | 81 +++ .../Core/Lifetime/LifetimeScopeTests.cs | 537 ++---------------- 7 files changed, 510 insertions(+), 513 deletions(-) rename test/Autofac.Specification.Test/Lifetime/{DisposalOrderTests.cs => DisposalTests.cs} (57%) create mode 100644 test/Autofac.Specification.Test/Lifetime/NestedScopeTests.cs create mode 100644 test/Autofac.Specification.Test/Registration/PropertyDictionaryTests.cs diff --git a/test/Autofac.Specification.Test/Features/CircularDependencyTests.cs b/test/Autofac.Specification.Test/Features/CircularDependencyTests.cs index 72782b99f..5fbd7c8d8 100644 --- a/test/Autofac.Specification.Test/Features/CircularDependencyTests.cs +++ b/test/Autofac.Specification.Test/Features/CircularDependencyTests.cs @@ -19,5 +19,31 @@ public void DetectsCircularDependencies() var de = Assert.Throws(() => container.Resolve()); } + + [Fact] + public void InstancePerLifetimeScopeServiceCannotCreateSecondInstanceOfSelfDuringConstruction() + { + var builder = new ContainerBuilder(); + builder.RegisterType().InstancePerLifetimeScope(); + builder.RegisterType().InstancePerLifetimeScope(); + var container = builder.Build(); + + var exception = Assert.Throws(() => container.Resolve()); + } + + private class AThatDependsOnB + { + public AThatDependsOnB(BThatCreatesA bThatCreatesA) + { + } + } + + private class BThatCreatesA + { + public BThatCreatesA(Func factory) + { + factory(this); + } + } } } diff --git a/test/Autofac.Specification.Test/Lifetime/DisposalOrderTests.cs b/test/Autofac.Specification.Test/Lifetime/DisposalTests.cs similarity index 57% rename from test/Autofac.Specification.Test/Lifetime/DisposalOrderTests.cs rename to test/Autofac.Specification.Test/Lifetime/DisposalTests.cs index fae4cd563..2c73e8015 100644 --- a/test/Autofac.Specification.Test/Lifetime/DisposalOrderTests.cs +++ b/test/Autofac.Specification.Test/Lifetime/DisposalTests.cs @@ -5,8 +5,30 @@ namespace Autofac.Specification.Test.Lifetime { - public class DisposalOrderTests + public class DisposalTests { + [Fact] + public void ComponentsAreDisposedEvenIfCurrentScopeEndingThrowsException() + { + var rootScope = new ContainerBuilder().Build(); + + var nestedScope = rootScope.BeginLifetimeScope(cb => cb.RegisterType().SingleInstance()); + + nestedScope.CurrentScopeEnding += (sender, args) => throw new DivideByZeroException(); + + var dt = nestedScope.Resolve(); + + try + { + nestedScope.Dispose(); + } + catch (DivideByZeroException) + { + } + + Assert.True(dt.IsDisposed); + } + [Fact] public void ComponentsResolvedFromContainer_DisposedInReverseDependencyOrder() { @@ -54,6 +76,29 @@ public void ComponentsResolvedFromContainerInReverseOrder_DisposedInReverseDepen Assert.Same(a, disposeOrder.Dequeue()); } + [Fact] + public void InstancesRegisteredInParentScope_ButResolvedInChild_AreDisposedWithChild() + { + var builder = new ContainerBuilder(); + builder.RegisterType(); + var parent = builder.Build(); + var child = parent.BeginLifetimeScope(b => { }); + var dt = child.Resolve(); + child.Dispose(); + Assert.True(dt.IsDisposed); + } + + [Fact] + public void ResolvingFromAnEndedLifetimeProducesObjectDisposedException() + { + var builder = new ContainerBuilder(); + builder.RegisterType(); + var container = builder.Build(); + var lifetime = container.BeginLifetimeScope(); + lifetime.Dispose(); + Assert.Throws(() => lifetime.Resolve()); + } + private class A : DisposeTracker { } diff --git a/test/Autofac.Specification.Test/Lifetime/NestedScopeTests.cs b/test/Autofac.Specification.Test/Lifetime/NestedScopeTests.cs new file mode 100644 index 000000000..ffd4d1b4f --- /dev/null +++ b/test/Autofac.Specification.Test/Lifetime/NestedScopeTests.cs @@ -0,0 +1,25 @@ +using System; +using Xunit; + +namespace Autofac.Specification.Test.Lifetime +{ + /// + /// Tests involving the lifetime of a nested scope. Tests for registering objects in a nested scope are + /// in . Tests for specifics + /// around disposal are in or in the fixture + /// for the specific lifetime model being tested (singleton, provided instance, etc.). + /// + public class NestedScopeTests + { + [Fact] + public void BeginLifetimeScopeCannotBeCalledWithDuplicateTag() + { + var rootScope = new ContainerBuilder().Build(); + const string duplicateTagName = "ABC"; + var taggedScope = rootScope.BeginLifetimeScope(duplicateTagName); + var differentTaggedScope = taggedScope.BeginLifetimeScope("DEF"); + + var exception = Assert.Throws(() => differentTaggedScope.BeginLifetimeScope(duplicateTagName)); + } + } +} diff --git a/test/Autofac.Specification.Test/Lifetime/SingleInstanceTests.cs b/test/Autofac.Specification.Test/Lifetime/SingleInstanceTests.cs index 6e6fca91a..15510fed7 100644 --- a/test/Autofac.Specification.Test/Lifetime/SingleInstanceTests.cs +++ b/test/Autofac.Specification.Test/Lifetime/SingleInstanceTests.cs @@ -10,6 +10,42 @@ private interface IA { } + [Fact] + public void SingletonsCanBeRegisteredInNestedScope() + { + var rootScope = new ContainerBuilder().Build(); + var nestedScope = rootScope.BeginLifetimeScope(cb => cb.RegisterType().SingleInstance()); + + var dt = nestedScope.Resolve(); + var dt1 = nestedScope.Resolve(); + Assert.Same(dt, dt1); + } + + [Fact] + public void SingletonsRegisteredInContainerAreDisposedWithContainer() + { + var cb = new ContainerBuilder(); + cb.RegisterType().SingleInstance(); + var c = cb.Build(); + var a1 = c.Resolve(); + var lifetime = c.BeginLifetimeScope(); + var a2 = lifetime.Resolve(); + lifetime.Dispose(); + Assert.False(a1.IsDisposed); + c.Dispose(); + Assert.True(a1.IsDisposed); + } + + [Fact] + public void SingletonsRegisteredInNestedScopeAreDisposedWithThatScope() + { + var rootScope = new ContainerBuilder().Build(); + var nestedScope = rootScope.BeginLifetimeScope(cb => cb.RegisterType().SingleInstance()); + var dt = nestedScope.Resolve(); + nestedScope.Dispose(); + Assert.True(dt.IsDisposed); + } + [Fact] public void TypeAsSingleInstance() { @@ -29,23 +65,6 @@ public void TypeAsSingleInstance() Assert.Same(a1, a4); } - [Fact] - public void TypeAsSingleInstanceDisposedWithContainer() - { - var cb = new ContainerBuilder(); - cb.RegisterType() - .As() - .SingleInstance(); - var c = cb.Build(); - var a1 = c.Resolve(); - var lifetime = c.BeginLifetimeScope(); - var a2 = lifetime.Resolve(); - lifetime.Dispose(); - Assert.False(((A)a1).IsDisposed); - c.Dispose(); - Assert.True(((A)a1).IsDisposed); - } - private class A : DisposeTracker, IA { } diff --git a/test/Autofac.Specification.Test/Registration/NestedScopeRegistrationTests.cs b/test/Autofac.Specification.Test/Registration/NestedScopeRegistrationTests.cs index 61d72519d..e4b5da556 100644 --- a/test/Autofac.Specification.Test/Registration/NestedScopeRegistrationTests.cs +++ b/test/Autofac.Specification.Test/Registration/NestedScopeRegistrationTests.cs @@ -1,4 +1,8 @@ using System; +using System.Collections.Generic; +using System.Linq; +using Autofac.Core.Registration; +using Autofac.Specification.Test.Util; using Xunit; namespace Autofac.Specification.Test.Registration @@ -9,18 +13,262 @@ private interface IMyService { } + private interface IServiceA + { + } + + private interface IServiceB + { + } + + private interface IServiceCommon + { + } + + [Fact] + public void BothLocalAndParentRegistrationsAreAvailable() + { + var cb = new ContainerBuilder(); + cb.RegisterType(); + var container = cb.Build(); + var ls1 = container.BeginLifetimeScope(b => b.RegisterType()); + var ls2 = ls1.BeginLifetimeScope(b => b.RegisterType()); + Assert.Equal(3, ls2.Resolve>().Count()); + } + + [Fact] + public void BothLocalAndParentRegistrationsAreAvailableViaAdapter() + { + var cb = new ContainerBuilder(); + cb.RegisterType(); + var container = cb.Build(); + var ls1 = container.BeginLifetimeScope(b => b.RegisterType()); + var ls2 = ls1.BeginLifetimeScope(b => b.RegisterType()); + Assert.Equal(3, ls2.Resolve>>().Count()); + } + + [Fact] + public void ComponentsInNestedLifetimeCanResolveDependenciesFromParent() + { + var level1Scope = new ContainerBuilder().Build(); + var level2Scope = level1Scope.BeginLifetimeScope(cb => cb.RegisterType()); + var level3Scope = level2Scope.BeginLifetimeScope(cb => cb.RegisterType()); + Assert.NotNull(level3Scope.Resolve().Add()); + } + + [Fact] + public void ExplicitCollectionRegistrationsMadeInParentArePreservedInChildScope() + { + var obs = new MyComponent[5]; + var cb = new ContainerBuilder(); + cb.RegisterInstance(obs).As>(); + var container = cb.Build(); + var ls = container.BeginLifetimeScope(b => b.RegisterType()); + Assert.Same(obs, ls.Resolve>()); + } + + [Fact] + public void InstancesRegisteredInNestedScopeAreSingletonsInThatScope() + { + var rootScope = new ContainerBuilder().Build(); + var dt = new DisposeTracker(); + var nestedScope = rootScope.BeginLifetimeScope(cb => cb.RegisterInstance(dt)); + var dt1 = nestedScope.Resolve(); + Assert.Same(dt, dt1); + } + + [Fact] + public void IntermediateRegistrationOverridesParentAsDefault() + { + var o1 = new MyComponent(); + var o2 = new MyComponent(); + + var builder = new ContainerBuilder(); + builder.Register(c => o1); + var scope1 = builder.Build(); + var scope2 = scope1.BeginLifetimeScope(b => b.Register(c => o2)); + var scope3 = scope2.BeginLifetimeScope(b => { }); + + Assert.Same(o2, scope3.Resolve()); + } + + [Fact] + public void LocalRegistrationCanPreserveParentAsDefault() + { + var o = new MyComponent(); + var cb = new ContainerBuilder(); + cb.RegisterType(); + var container = cb.Build(); + var ls = container.BeginLifetimeScope(b => b.Register(c => o).PreserveExistingDefaults()); + Assert.NotSame(o, ls.Resolve()); + } + + [Fact] + public void MostLocalRegistrationIsDefault() + { + var cb = new ContainerBuilder(); + cb.RegisterType(); + var container = cb.Build(); + var ls1 = container.BeginLifetimeScope(b => b.RegisterType()); + var o = new MyComponent(); + var ls2 = ls1.BeginLifetimeScope(b => b.RegisterInstance(o)); + Assert.Same(o, ls2.Resolve()); + } + + [Fact] + public void RegistrationsMadeInLifetimeScopeAreAdapted() + { + var container = new ContainerBuilder().Build(); + var ls = container.BeginLifetimeScope(b => b.RegisterType().As()); + + var component = ls.Resolve>().Invoke(); + Assert.IsType(component); + } + [Fact] - public void RegistrationsMadeInConfigureExpressionAreAddedToContainer() + public void RegistrationsMadeInLifetimeScopeCanBeResolvedThere() { var builder = new ContainerBuilder(); var container = builder.Build(); var ls = container.BeginLifetimeScope(b => b.RegisterType().As()); var component = ls.Resolve(); - Assert.True(component is MyComponent); + Assert.IsType(component); + } + + [Fact] + public void RegistrationsMadeInLifetimeScopeCannotBeResolvedInItsParent() + { + var builder = new ContainerBuilder(); + var container = builder.Build(); + var ls = container.BeginLifetimeScope(b => b.RegisterType().As()); + + Assert.Throws(() => container.Resolve()); + } + + [Fact] + public void RegistrationsMadeInParentScopeAreAdapted() + { + var cb = new ContainerBuilder(); + cb.RegisterType().As(); + var container = cb.Build(); + var ls = container.BeginLifetimeScope(b => { }); + + var component = container.Resolve>().Invoke(); + Assert.IsType(component); + } + + [Fact] + public void ServiceOverrideThroughIntermediateScopeIsCorrect() + { + // Issue #475 + var builder = new ContainerBuilder(); + builder.RegisterType(typeof(ServiceA)).AsImplementedInterfaces(); + builder.RegisterType(typeof(ServiceB1)).AsImplementedInterfaces(); + + using (var scope1 = builder.Build()) + { + // Scope 1 (Container) resolves default values. + var service1A = scope1.Resolve(); + var service1B = scope1.Resolve(); + Assert.IsType(service1A); + Assert.IsType(service1B); + + using (var scope2 = scope1.BeginLifetimeScope(cb => + cb.RegisterType(typeof(ServiceB2)) + .AsImplementedInterfaces() + .InstancePerLifetimeScope())) + { + // Scope 2 overrides the registration for one service + // but leaves the other in place. + var service2A = scope2.Resolve(); + var service2B = scope2.Resolve(); + Assert.IsType(service2A); + Assert.IsType(service2B); + + using (var scope3 = scope2.BeginLifetimeScope(cb => { })) + { + // Scope 3 provides an empty set of registrations + // and should retain the overrides from scope 2. + var service3A = scope3.Resolve(); + var service3B = scope3.Resolve(); + Assert.IsType(service3A); + Assert.IsType(service3B); + } + } + } + } + + [Fact] + public void TwoRegistrationsSameServicesDifferentLifetimeScopes() + { + // Issue #511 + var builder = new ContainerBuilder(); + builder.RegisterType().As().As(); + builder.RegisterType().As().InstancePerLifetimeScope(); + using (var container = builder.Build()) + { + using (var lifetimeScope = container.BeginLifetimeScope()) + { + var obj1 = lifetimeScope.Resolve(); + var obj2 = lifetimeScope.Resolve(); + Assert.Same(obj1, obj2); + } + + using (var lifetimeScope = container.BeginLifetimeScope(x => { })) + { + // Issue #511 mentions that passing a lambda configuration + // expression causes the test to fail. + var obj1 = lifetimeScope.Resolve(); + var obj2 = lifetimeScope.Resolve(); + Assert.Same(obj1, obj2); + } + } + } + + [Fact] + public void WhenRegisteringIntoADeeplyNestedLifetimeScopeParentRegistrationsAreNotDuplicated() + { + var builder = new ContainerBuilder(); + builder.RegisterType(); + var container = builder.Build(); + var child1 = container.BeginLifetimeScope(); + var child2 = child1.BeginLifetimeScope(b => b.RegisterType()); + Assert.Single(child2.Resolve>()); + } + + public class AddressBook + { + private readonly Func _partyFactory; + + public AddressBook(Func partyFactory) + { + this._partyFactory = partyFactory; + } + + public Person Add() + { + return this._partyFactory(); + } + } + + private class MyComponent : IMyService + { + } + + public class Person + { + } + + private class ServiceA : IServiceA, IServiceCommon + { + } + + private class ServiceB1 : IServiceB, IServiceCommon + { } - private sealed class MyComponent : IMyService + private class ServiceB2 : IServiceB { } } diff --git a/test/Autofac.Specification.Test/Registration/PropertyDictionaryTests.cs b/test/Autofac.Specification.Test/Registration/PropertyDictionaryTests.cs new file mode 100644 index 000000000..f080eb231 --- /dev/null +++ b/test/Autofac.Specification.Test/Registration/PropertyDictionaryTests.cs @@ -0,0 +1,81 @@ +using System; +using Xunit; + +namespace Autofac.Specification.Test.Registration +{ + public class PropertyDictionaryTests + { + [Fact] + public void ChildScopeBuilderGetsParentProperties() + { + var builder = new ContainerBuilder(); + builder.Properties["count"] = 5; + var container = builder.Build(); + + using (var outerScope = container.BeginLifetimeScope(b => + { + Assert.Equal(5, b.Properties["count"]); + b.Properties["count"] = 10; + })) + { + Assert.Equal(10, outerScope.ComponentRegistry.Properties["count"]); + using (var innerScope = outerScope.BeginLifetimeScope(b => + { + Assert.Equal(10, b.Properties["count"]); + b.Properties["count"] = 15; + })) + { + Assert.Equal(5, container.ComponentRegistry.Properties["count"]); + Assert.Equal(10, outerScope.ComponentRegistry.Properties["count"]); + Assert.Equal(15, innerScope.ComponentRegistry.Properties["count"]); + } + } + } + + [Fact] + public void LambdaRegistrationsDoNotAffectPropertyPropagation() + { + // In the past we've had issues where lambda configuration + // expressions change the behavior of builder/container semantics. + // This ensures we can use properties even when we aren't using + // lambdas in lifetime scope startup. + var builder = new ContainerBuilder(); + builder.Properties["count"] = 5; + var container = builder.Build(); + + using (var outerScope = container.BeginLifetimeScope()) + { + using (var innerScope = outerScope.BeginLifetimeScope(b => + { + b.Properties["count"] = 15; + })) + { + Assert.Equal(5, container.ComponentRegistry.Properties["count"]); + Assert.Equal(5, outerScope.ComponentRegistry.Properties["count"]); + Assert.Equal(15, innerScope.ComponentRegistry.Properties["count"]); + } + } + } + + [Fact] + public void RegistrationsCanUseContextProperties() + { + var builder = new ContainerBuilder(); + builder.Properties["count"] = 0; + builder.Register(ctx => + { + return ctx.ComponentRegistry.Properties["count"].ToString(); + }).As(); + + var container = builder.Build(); + + Assert.Equal("0", container.Resolve()); + using (var scope = container.BeginLifetimeScope(b => b.Properties["count"] = 1)) + { + Assert.Equal("1", scope.Resolve()); + } + + Assert.Equal("0", container.Resolve()); + } + } +} diff --git a/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs b/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs index c0774588c..8d4a580f4 100644 --- a/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs +++ b/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Globalization; using System.Linq; using Autofac.Core; -using Autofac.Core.Lifetime; using Autofac.Core.Registration; using Autofac.Test.Scenarios.RegistrationSources; using Xunit; @@ -12,247 +9,6 @@ namespace Autofac.Test.Core.Lifetime { public class LifetimeScopeTests { - [Fact] - public void RegistrationsMadeInLifetimeScopeCanBeResolvedThere() - { - var container = new ContainerBuilder().Build(); - var ls = container.BeginLifetimeScope(b => b.RegisterType()); - ls.AssertRegistered(); - } - - [Fact] - public void RegistrationsMadeInLifetimeScopeCannotBeResolvedInItsParent() - { - var container = new ContainerBuilder().Build(); - container.BeginLifetimeScope(b => b.RegisterType()); - container.AssertNotRegistered(); - } - - [Fact] - public void RegistrationsMadeInLifetimeScopeAreAdapted() - { - var container = new ContainerBuilder().Build(); - var ls = container.BeginLifetimeScope(b => b.RegisterType()); - ls.AssertRegistered>(); - } - - [Fact] - public void RegistrationsMadeInParentScopeAreAdapted() - { - var cb = new ContainerBuilder(); - cb.RegisterType(); - var container = cb.Build(); - var ls = container.BeginLifetimeScope(b => { }); - ls.AssertRegistered>(); - } - - [Fact] - public void BothLocalAndParentRegistrationsAreAvailable() - { - var cb = new ContainerBuilder(); - cb.RegisterType(); - var container = cb.Build(); - var ls1 = container.BeginLifetimeScope(b => b.RegisterType()); - var ls2 = ls1.BeginLifetimeScope(b => b.RegisterType()); - Assert.Equal(3, ls2.Resolve>().Count()); - } - - [Fact] - public void MostLocalRegistrationIsDefault() - { - var cb = new ContainerBuilder(); - cb.RegisterType(); - var container = cb.Build(); - var ls1 = container.BeginLifetimeScope(b => b.RegisterType()); - var o = new object(); - var ls2 = ls1.BeginLifetimeScope(b => b.RegisterInstance(o)); - Assert.Same(o, ls2.Resolve()); - } - - [Fact] - public void BothLocalAndParentRegistrationsAreAvailableViaAdapter() - { - var cb = new ContainerBuilder(); - cb.RegisterType(); - var container = cb.Build(); - var ls1 = container.BeginLifetimeScope(b => b.RegisterType()); - var ls2 = ls1.BeginLifetimeScope(b => b.RegisterType()); - Assert.Equal(3, ls2.Resolve>>().Count()); - } - - [Fact] - public void LocalRegistrationOverridesParentAsDefault() - { - var o = new object(); - var cb = new ContainerBuilder(); - cb.RegisterType(); - var container = cb.Build(); - var ls = container.BeginLifetimeScope(b => b.Register(c => o)); - Assert.Same(o, ls.Resolve()); - } - - [Fact] - public void IntermediateRegistrationOverridesParentAsDefault() - { - var o1 = new object(); - var o2 = new object(); - - var builder = new ContainerBuilder(); - builder.Register(c => o1); - var scope1 = builder.Build(); - var scope2 = scope1.BeginLifetimeScope(b => b.Register(c => o2)); - var scope3 = scope2.BeginLifetimeScope(b => { }); - - Assert.Same(o2, scope3.Resolve()); - } - - [Fact] - public void LocalRegistrationCanPreserveParentAsDefault() - { - var o = new object(); - var cb = new ContainerBuilder(); - cb.RegisterType(); - var container = cb.Build(); - var ls = container.BeginLifetimeScope(b => b.Register(c => o).PreserveExistingDefaults()); - Assert.NotSame(o, ls.Resolve()); - } - - [Fact] - public void ExplicitCollectionRegistrationsMadeInParentArePreservedInChildScope() - { - var obs = new object[5]; - var cb = new ContainerBuilder(); - cb.RegisterInstance(obs).As>(); - var container = cb.Build(); - var ls = container.BeginLifetimeScope(b => b.RegisterType()); - Assert.Same(obs, ls.Resolve>()); - } - - [Fact] - public void NestedLifetimeScopesMaintainServiceLimitTypes() - { - // Issue #365 - var cb = new ContainerBuilder(); - cb.RegisterType(); - var container = cb.Build(); - var service = new TypedService(typeof(Person)); - using (var unconfigured = container.BeginLifetimeScope()) - { - IComponentRegistration reg = null; - Assert.True(unconfigured.ComponentRegistry.TryGetRegistration(service, out reg), "The registration should have been found in the unconfigured scope."); - Assert.Equal(typeof(Person), reg.Activator.LimitType); - } - - using (var configured = container.BeginLifetimeScope(b => { })) - { - IComponentRegistration reg = null; - Assert.True(configured.ComponentRegistry.TryGetRegistration(service, out reg), "The registration should have been found in the configured scope."); - Assert.Equal(typeof(Person), reg.Activator.LimitType); - } - } - - [Fact] - public void TwoRegistrationsSameServicesDifferentLifetimeScopes() - { - // Issue #511 - var builder = new ContainerBuilder(); - builder.RegisterType().As().As(); - builder.RegisterType().As().InstancePerLifetimeScope(); - using (var container = builder.Build()) - { - using (var lifetimeScope = container.BeginLifetimeScope()) - { - var obj1 = lifetimeScope.Resolve(); - var obj2 = lifetimeScope.Resolve(); - Assert.Same(obj1, obj2); - } - - using (var lifetimeScope = container.BeginLifetimeScope(x => { })) - { - // Issue #511 mentions that passing a lambda configuration - // expression causes the test to fail. - var obj1 = lifetimeScope.Resolve(); - var obj2 = lifetimeScope.Resolve(); - Assert.Same(obj1, obj2); - } - } - } - - public class Person - { - } - - public class AddressBook - { - private readonly Func _partyFactory; - - public AddressBook(Func partyFactory) - { - _partyFactory = partyFactory; - } - - public Person Add() - { - return _partyFactory(); - } - } - - [Fact] - public void ComponentsInNestedLifetimeCanResolveDependenciesFromParent() - { - var level1Scope = new ContainerBuilder().Build(); - - var level2Scope = level1Scope.BeginLifetimeScope(cb => - cb.RegisterType()); - - var level3Scope = level2Scope.BeginLifetimeScope(cb => - cb.RegisterType()); - - level3Scope.Resolve().Add(); - } - - [Fact] - public void InstancesRegisteredInNestedScopeAreSingletonsInThatScope() - { - var rootScope = new ContainerBuilder().Build(); - - var dt = new DisposeTracker(); - - var nestedScope = rootScope.BeginLifetimeScope(cb => - cb.RegisterInstance(dt)); - - var dt1 = nestedScope.Resolve(); - Assert.Same(dt, dt1); - } - - [Fact] - public void SingletonsRegisteredInNestedScopeAreTiedToThatScope() - { - var rootScope = new ContainerBuilder().Build(); - - var nestedScope = rootScope.BeginLifetimeScope(cb => - cb.RegisterType().SingleInstance()); - - var dt = nestedScope.Resolve(); - var dt1 = nestedScope.Resolve(); - Assert.Same(dt, dt1); - - nestedScope.Dispose(); - - Assert.True(dt.IsDisposed); - } - - [Fact] - public void LifetimeScopeCreatedWithAdditionalRegistrationsUsesScopeRestrictedRegistry() - { - var rootScope = new ContainerBuilder().Build(); - - var nestedScope = rootScope.BeginLifetimeScope(cb => - cb.RegisterType().SingleInstance()); - - Assert.IsType(nestedScope.ComponentRegistry); - } - [Fact] public void AdaptersInNestedScopeOverrideAdaptersInParent() { @@ -267,37 +23,35 @@ public void AdaptersInNestedScopeOverrideAdaptersInParent() } [Fact] - public void InstancesRegisteredInParentScope_ButResolvedInChild_AreDisposedWithChild() - { - var builder = new ContainerBuilder(); - builder.RegisterType(); - var parent = builder.Build(); - var child = parent.BeginLifetimeScope(b => { }); - var dt = child.Resolve(); - child.Dispose(); - Assert.True(dt.IsDisposed); - } - - [Fact] - public void ResolvingFromAnEndedLifetimeProducesObjectDisposedException() + public void CanRegisterInstanceUsingUpdateInsideChildLifetimeScope() { var builder = new ContainerBuilder(); - builder.RegisterType(); + builder.RegisterType(); + builder.RegisterType(); var container = builder.Build(); - var lifetime = container.BeginLifetimeScope(); - lifetime.Dispose(); - Assert.Throws(() => lifetime.Resolve()); + + var scope = container.BeginLifetimeScope(); + var updatesRegistry = scope.Resolve(); + updatesRegistry.UpdateRegistry(new object()); + var instance1 = scope.Resolve(); + + scope = container.BeginLifetimeScope(); + updatesRegistry = scope.Resolve(); + updatesRegistry.UpdateRegistry(new object()); + var instance2 = scope.Resolve(); + + Assert.NotSame(instance1, instance2); } [Fact] - public void WhenRegisteringIntoADeeplyNestedLifetimeScopeParentRegistrationsAreNotDuplicated() + public void LifetimeScopeCreatedWithAdditionalRegistrationsUsesScopeRestrictedRegistry() { - var builder = new ContainerBuilder(); - builder.RegisterType(); - var container = builder.Build(); - var child1 = container.BeginLifetimeScope(); - var child2 = child1.BeginLifetimeScope(b => b.RegisterType()); - Assert.Single(child2.Resolve>()); + var rootScope = new ContainerBuilder().Build(); + + var nestedScope = rootScope.BeginLifetimeScope(cb => + cb.RegisterType().SingleInstance()); + + Assert.IsType(nestedScope.ComponentRegistry); } [Fact] @@ -323,107 +77,45 @@ public void NestedComponentRegistryIsProperlyDisposedEvenWhenRegistryUpdatedLate Assert.True(nestedRegistration.IsDisposed); } - public interface IServiceA - { - } - - public interface IServiceB - { - } - - public interface IServiceCommon - { - } - - public class ServiceA : IServiceA, IServiceCommon - { - } - - public class ServiceB1 : IServiceB, IServiceCommon - { - } - - public class ServiceB2 : IServiceB - { - } - [Fact] - public void ServiceOverrideThroughIntermediateScopeIsCorrect() + public void NestedLifetimeScopesMaintainServiceLimitTypes() { - // Issue #475 - var builder = new ContainerBuilder(); - builder.RegisterType(typeof(ServiceA)).AsImplementedInterfaces(); - builder.RegisterType(typeof(ServiceB1)).AsImplementedInterfaces(); - - using (var scope1 = builder.Build()) + // Issue #365 + var cb = new ContainerBuilder(); + cb.RegisterType(); + var container = cb.Build(); + var service = new TypedService(typeof(Person)); + using (var unconfigured = container.BeginLifetimeScope()) { - // Scope 1 (Container) resolves default values. - var service1A = scope1.Resolve(); - var service1B = scope1.Resolve(); - Assert.IsType(service1A); - Assert.IsType(service1B); - - using (var scope2 = scope1.BeginLifetimeScope(cb => - cb.RegisterType(typeof(ServiceB2)) - .AsImplementedInterfaces() - .InstancePerLifetimeScope())) - { - // Scope 2 overrides the registration for one service - // but leaves the other in place. - var service2A = scope2.Resolve(); - var service2B = scope2.Resolve(); - Assert.IsType(service2A); - Assert.IsType(service2B); - - using (var scope3 = scope2.BeginLifetimeScope(cb => { })) - { - // Scope 3 provides an empty set of registrations - // and should retain the overrides from scope 2. - var service3A = scope3.Resolve(); - var service3B = scope3.Resolve(); - Assert.IsType(service3A); - Assert.IsType(service3B); - } - } + IComponentRegistration reg = null; + Assert.True(unconfigured.ComponentRegistry.TryGetRegistration(service, out reg), "The registration should have been found in the unconfigured scope."); + Assert.Equal(typeof(Person), reg.Activator.LimitType); } - } - private class AThatDependsOnB - { - public AThatDependsOnB(BThatCreatesA bThatCreatesA) + using (var configured = container.BeginLifetimeScope(b => { })) { + IComponentRegistration reg = null; + Assert.True(configured.ComponentRegistry.TryGetRegistration(service, out reg), "The registration should have been found in the configured scope."); + Assert.Equal(typeof(Person), reg.Activator.LimitType); } } - private class BThatCreatesA + internal class DependsOnRegisteredInstance { - public BThatCreatesA(Func factory) + public DependsOnRegisteredInstance(object instance) { - factory(this); + this.Instance = instance; } + + internal object Instance { get; set; } } - [Fact] - public void InstancePerLifetimeScopeServiceCannotCreateSecondInstanceOfSelfDuringConstruction() + public class HandlerException : Exception { - var builder = new ContainerBuilder(); - builder.RegisterType().InstancePerLifetimeScope(); - builder.RegisterType().InstancePerLifetimeScope(); - var container = builder.Build(); - - var exception = Assert.Throws(() => container.Resolve()); - - Assert.Equal(exception.Message, string.Format(CultureInfo.CurrentCulture, LifetimeScopeResources.SelfConstructingDependencyDetected, typeof(AThatDependsOnB).FullName)); } - internal class DependsOnRegisteredInstance + public class Person { - internal object Instance { get; set; } - - public DependsOnRegisteredInstance(object instance) - { - Instance = instance; - } } internal class UpdatesRegistryWithInstance @@ -432,154 +124,15 @@ internal class UpdatesRegistryWithInstance public UpdatesRegistryWithInstance(IComponentContext registerContext) { - _registerContext = registerContext; + this._registerContext = registerContext; } internal void UpdateRegistry(object instance) { var builder = new ContainerBuilder(); builder.RegisterInstance(instance); - builder.UpdateRegistry(_registerContext.ComponentRegistry); + builder.UpdateRegistry(this._registerContext.ComponentRegistry); } } - - [Fact] - public void CanRegisterInstanceUsingUpdateInsideChildLifetimeScope() - { - var builder = new ContainerBuilder(); - builder.RegisterType(); - builder.RegisterType(); - var container = builder.Build(); - - var scope = container.BeginLifetimeScope(); - var updatesRegistry = scope.Resolve(); - updatesRegistry.UpdateRegistry(new object()); - var instance1 = scope.Resolve(); - - scope = container.BeginLifetimeScope(); - updatesRegistry = scope.Resolve(); - updatesRegistry.UpdateRegistry(new object()); - var instance2 = scope.Resolve(); - - Assert.NotSame(instance1, instance2); - } - - [Fact] - public void RegistrationsCanUseContextProperties() - { - var builder = new ContainerBuilder(); - builder.Properties["count"] = 0; - builder.Register(ctx => - { - return ctx.ComponentRegistry.Properties["count"].ToString(); - }).As(); - - var container = builder.Build(); - - Assert.Equal("0", container.Resolve()); - using (var scope = container.BeginLifetimeScope(b => b.Properties["count"] = 1)) - { - Assert.Equal("1", scope.Resolve()); - } - - Assert.Equal("0", container.Resolve()); - } - - [Fact] - public void ChildScopeBuilderGetsParentProperties() - { - var builder = new ContainerBuilder(); - builder.Properties["count"] = 5; - var container = builder.Build(); - - using (var outerScope = container.BeginLifetimeScope(b => - { - Assert.Equal(5, b.Properties["count"]); - b.Properties["count"] = 10; - })) - { - Assert.Equal(10, outerScope.ComponentRegistry.Properties["count"]); - using (var innerScope = outerScope.BeginLifetimeScope(b => - { - Assert.Equal(10, b.Properties["count"]); - b.Properties["count"] = 15; - })) - { - Assert.Equal(5, container.ComponentRegistry.Properties["count"]); - Assert.Equal(10, outerScope.ComponentRegistry.Properties["count"]); - Assert.Equal(15, innerScope.ComponentRegistry.Properties["count"]); - } - } - } - - [Fact] - public void LambdaRegistrationsDoNotAffectPropertyPropagation() - { - // In the past we've had issues where lambda configuration - // expressions change the behavior of builder/container semantics. - // This ensures we can use properties even when we aren't using - // lambdas in lifetime scope startup. - var builder = new ContainerBuilder(); - builder.Properties["count"] = 5; - var container = builder.Build(); - - using (var outerScope = container.BeginLifetimeScope()) - { - using (var innerScope = outerScope.BeginLifetimeScope(b => - { - b.Properties["count"] = 15; - })) - { - Assert.Equal(5, container.ComponentRegistry.Properties["count"]); - Assert.Equal(5, outerScope.ComponentRegistry.Properties["count"]); - Assert.Equal(15, innerScope.ComponentRegistry.Properties["count"]); - } - } - } - - public class HandlerException : Exception - { - } - - [Fact] - public void ContainerIsDisposedEvenIfHandlerThrowsException() - { - var rootScope = new ContainerBuilder().Build(); - - var nestedScope = rootScope.BeginLifetimeScope(cb => - cb.RegisterType().SingleInstance()); - - nestedScope.CurrentScopeEnding += (sender, args) => throw new HandlerException(); - - var dt = nestedScope.Resolve(); - - try - { - nestedScope.Dispose(); - } - catch (HandlerException) - { - } - - Assert.True(dt.IsDisposed); - } - - [Fact] - public void BeginLifetimeScopeCannotBeCalledWithDuplicateTag() - { - var rootScope = new ContainerBuilder().Build(); - const string duplicateTagName = "ABC"; - var taggedScope = rootScope.BeginLifetimeScope(duplicateTagName); - var differentTaggedScope = taggedScope.BeginLifetimeScope("DEF"); - - var exception = Assert.Throws(() => differentTaggedScope.BeginLifetimeScope(duplicateTagName)); - - var expectedMessage = string.Format( - CultureInfo.CurrentCulture, - LifetimeScopeResources.DuplicateTagDetected, - duplicateTagName); - - Assert.Equal(expectedMessage, exception.Message); - } } }