public void ExecutionContextScopeDispose_TwoScopedRegistationsForTheSameServiceType_DisposesBothInstances()
        {
            // Arrange
            var disposedInstances = new HashSet <object>();

            var lifestyle = new ExecutionContextScopeLifestyle();

            var container = new Container();

            var reg1 = lifestyle.CreateRegistration <ICommand, DisposableCommand>(container);
            var reg2 = lifestyle.CreateRegistration <ICommand, DisposableCommand>(container);

            container.AppendToCollection(typeof(ICommand), reg1);
            container.AppendToCollection(typeof(ICommand), reg2);

            using (container.BeginExecutionContextScope())
            {
                var commands = container.GetAllInstances <ICommand>().Cast <DisposableCommand>().ToArray();

                Assert.AreNotSame(commands[0], commands[1], "Test setup failed.");

                commands[0].Disposing += sender => disposedInstances.Add(sender);
                commands[1].Disposing += sender => disposedInstances.Add(sender);

                // Act
            }

            // Assert
            Assert.AreEqual(2, disposedInstances.Count, "Two instances were expected to be disposed.");
        }
        public void ExecutionContextScopeDispose_WithTransientRegisteredForDisposal_DisposesThatInstance()
        {
            // Arrange
            DisposableCommand transientInstanceToDispose = null;

            var container = new Container();

            var lifestyle = new ExecutionContextScopeLifestyle();

            container.RegisterInitializer <DisposableCommand>(command =>
            {
                lifestyle.RegisterForDisposal(container, command);
            });

            var scope = container.BeginExecutionContextScope();

            try
            {
                transientInstanceToDispose = container.GetInstance <DisposableCommand>();
            }
            finally
            {
                // Act
                scope.Dispose();
            }

            // Assert
            Assert.IsTrue(transientInstanceToDispose.HasBeenDisposed);
        }
        public void ExecutionContextScopeDispose_WithWhenScopeEndsRegistration_CallsTheRegisteredAction()
        {
            // Arrange
            int actionCallCount = 0;

            var container = new Container();

            var lifestyle = new ExecutionContextScopeLifestyle();

            container.Register <DisposableCommand, DisposableCommand>(lifestyle);

            container.RegisterInitializer <DisposableCommand>(command =>
            {
                lifestyle.WhenScopeEnds(container, () => { actionCallCount++; });
            });

            var scope = container.BeginExecutionContextScope();

            try
            {
                container.GetInstance <DisposableCommand>();
            }
            finally
            {
                // Act
                scope.Dispose();
            }

            // Assert
            Assert.AreEqual(1, actionCallCount, "Delegate is expected to be called exactly once.");
        }
        public void GetInstance_ResolveMultipleExecutionContextScopedServicesWithStrangeEqualsImplementations_CorrectlyDisposesAllInstances()
        {
            // Arrange
            var container = new Container();

            var lifestyle = new ExecutionContextScopeLifestyle();

            container.Register <DisposableCommandWithOverriddenEquality1>(lifestyle);
            container.Register <DisposableCommandWithOverriddenEquality2>(lifestyle);

            // Act
            DisposableCommandWithOverriddenEquality1 command1;
            DisposableCommandWithOverriddenEquality2 command2;

            // Act
            using (container.BeginExecutionContextScope())
            {
                command1 = container.GetInstance <DisposableCommandWithOverriddenEquality1>();
                command2 = container.GetInstance <DisposableCommandWithOverriddenEquality2>();

                // Give both instances the same hash code. Both have an equals implementation that compared
                // using the hash code, which make them look like they're the same instance.
                command1.HashCode = 1;
                command2.HashCode = 1;
            }

            // Assert
            string assertMessage =
                "Dispose is expected to be called on this command, even when it contains a GetHashCode and " +
                "Equals implementation that is totally screwed up, since storing disposable objects, " +
                "should be completely independant to this implementation. ";

            Assert.AreEqual(1, command1.DisposeCount, assertMessage + "command1");
            Assert.AreEqual(1, command2.DisposeCount, assertMessage + "command2");
        }
        public void WhenScopeEnds_WithMultipleDisposableComponentsAndPropertyDependencyDependingOnEachOther_DependsComponentsInExpectedOrder()
        {
            // Arrange
            var expectedOrderOfDisposal = new List <Type>
            {
                typeof(Middle),
                typeof(Inner),
                typeof(PropertyDependency),
            };

            var actualOrderOfDisposal = new List <Type>();

            var container = new Container();

            // Allow PropertyDependency to be injected as property on Inner
            container.Options.PropertySelectionBehavior = new InjectProperties <ImportAttribute>();

            // PropertyDependency, Middle and Inner all depend on Func<object> and call it when disposed.
            // This way we can check in which order the instances are disposed.
            container.RegisterSingleton <Action <object> >(instance => actualOrderOfDisposal.Add(instance.GetType()));

            // Middle depends on Inner that depends on property PropertyDependency.
            // Registration is deliberately made in a different order to prevent that the order of
            // registration might influence the order of disposing.
            var lifestyle = new ExecutionContextScopeLifestyle();

            container.Register <PropertyDependency>(lifestyle);
            container.Register <Middle>(lifestyle);
            container.Register <Inner>(lifestyle);

            // Act
            var scope = container.BeginExecutionContextScope();

            try
            {
                // Resolve the outer most object.
                container.GetInstance <Middle>();
            }
            finally
            {
                // Act
                scope.Dispose();
            }

            // Assert
            Assert.IsTrue(
                expectedOrderOfDisposal.SequenceEqual(actualOrderOfDisposal),
                "Types were expected to be disposed in the following order: {0}, " +
                "but they actually were disposed in the order: {1}. " +
                "Since PropertyDependency is injected as property into Inner, it is important that " +
                "PropertyDependency is disposed after Inner.",
                string.Join(", ", expectedOrderOfDisposal.Select(type => type.Name)),
                string.Join(", ", actualOrderOfDisposal.Select(type => type.Name)));
        }
        public void WhenScopeEnds_NullActionArgument_ThrowsException()
        {
            // Arrange
            Action invalidArgument = null;

            var lifestyle = new ExecutionContextScopeLifestyle();

            // Act
            Action action = () => lifestyle.WhenScopeEnds(new Container(), invalidArgument);

            // Assert
            AssertThat.Throws <ArgumentNullException>(action);
        }
        public void WhenScopeEnds_WithMultipleDisposableComponentsDependingOnEachOther_DependsComponentsInExpectedOrder()
        {
            // Arrange
            var expectedOrderOfDisposal = new List <Type>
            {
                typeof(Outer),
                typeof(Middle),
                typeof(Inner),
            };

            var actualOrderOfDisposal = new List <Type>();

            var container = new Container();

            // Outer, Middle and Inner all depend on Func<object> and call it when disposed.
            // This way we can check in which order the instances are disposed.
            container.RegisterSingleton <Action <object> >(instance => actualOrderOfDisposal.Add(instance.GetType()));

            // Outer depends on Middle that depends on Inner.
            // Registration is deliberately made in a different order to prevent that the order of
            // registration might influence the order of disposing.
            var lifestyle = new ExecutionContextScopeLifestyle();

            container.Register <Middle>(lifestyle);
            container.Register <Inner>(lifestyle);
            container.Register <Outer>(lifestyle);

            var scope = container.BeginExecutionContextScope();

            try
            {
                // Resolve the outer most object.
                container.GetInstance <Outer>();
            }
            finally
            {
                // Act
                scope.Dispose();
            }

            // Assert
            Assert.IsTrue(
                expectedOrderOfDisposal.SequenceEqual(actualOrderOfDisposal),
                "Types were expected to be disposed in the following order: {0}, " +
                "but they actually were disposed in the order: {1}. " +
                "This order is important, because when a components gets disposed, it might still want to " +
                "call the components it depends on, but at that time those components are already disposed.",
                string.Join(", ", expectedOrderOfDisposal.Select(type => type.Name)),
                string.Join(", ", actualOrderOfDisposal.Select(type => type.Name)));
        }
        public void ContainerVerify_WithWhenScopeEndsRegistration_Succeeds()
        {
            // Arrange
            var container = new Container();

            var lifestyle = new ExecutionContextScopeLifestyle();

            container.Register <ICommand, DisposableCommand>(lifestyle);

            container.RegisterInitializer <DisposableCommand>(command =>
            {
                lifestyle.WhenScopeEnds(container, () => { });
            });

            // Act
            container.Verify();
        }
        public void ExecutionContextScope_TwoScopedRegistationsForTheSameServiceType_CreatesTwoInstances()
        {
            // Arrange
            var lifestyle = new ExecutionContextScopeLifestyle();

            var container = new Container();

            var reg1 = lifestyle.CreateRegistration <ICommand, DisposableCommand>(container);
            var reg2 = lifestyle.CreateRegistration <ICommand, DisposableCommand>(container);

            container.AppendToCollection(typeof(ICommand), reg1);
            container.AppendToCollection(typeof(ICommand), reg2);

            using (container.BeginExecutionContextScope())
            {
                // Act
                var commands = container.GetAllInstances <ICommand>().Cast <DisposableCommand>().ToArray();

                // Assert
                Assert.AreNotSame(commands[0], commands[1], "Two instances were expected.");
            }
        }
        public void ExecutionContextScopeDispose_WithWhenScopeEndsRegistration_CallsTheRegisteredActionBeforeCallingDispose()
        {
            // Arrange
            bool delegateHasBeenCalled          = false;
            DisposableCommand instanceToDispose = null;

            var container = new Container();

            var lifestyle = new ExecutionContextScopeLifestyle();

            container.Register <DisposableCommand, DisposableCommand>(lifestyle);

            container.RegisterInitializer <DisposableCommand>(command =>
            {
                lifestyle.WhenScopeEnds(container, () =>
                {
                    Assert.IsFalse(command.HasBeenDisposed,
                                   "The action should be called before disposing the instance, because users are " +
                                   "to use those instances.");
                    delegateHasBeenCalled = true;
                });
            });

            var scope = container.BeginExecutionContextScope();

            try
            {
                instanceToDispose = container.GetInstance <DisposableCommand>();
            }
            finally
            {
                // Act
                scope.Dispose();
            }

            // Assert
            Assert.IsTrue(delegateHasBeenCalled, "Delegate is expected to be called.");
        }
        public void WhenScopeEnds_CalledOutOfTheContextOfAExecutionContextScope_ThrowsException()
        {
            // Arrange
            var lifestyle = new ExecutionContextScopeLifestyle();

            var container = new Container();

            container.Register <ConcreteCommand>(new ExecutionContextScopeLifestyle());

            try
            {
                // Act
                lifestyle.WhenScopeEnds(container, () => { });

                // Assert
                Assert.Fail("Exception expected.");
            }
            catch (InvalidOperationException ex)
            {
                Assert.IsTrue(ex.Message.Contains(
                                  "This method can only be called within the context of an active Execution Context Scope."),
                              "Actual: " + ex.Message);
            }
        }