Ensure that other IoC containers don't dispose things multiple times

Fixes #16
This commit is contained in:
Antony Male 2016-11-25 17:04:50 +00:00
parent 3fd89871b3
commit 9e9514a795
14 changed files with 219 additions and 139 deletions

View File

@ -38,11 +38,11 @@ namespace Bootstrappers
};
builder.RegisterInstance<IViewManager>(new ViewManager(viewManagerConfig));
builder.RegisterInstance<IWindowManagerConfig>(this);
builder.RegisterInstance<IWindowManagerConfig>(this).ExternallyOwned();
builder.RegisterType<WindowManager>().As<IWindowManager>().SingleInstance();
builder.RegisterType<EventAggregator>().As<IEventAggregator>().SingleInstance();
builder.RegisterType<MessageBoxViewModel>().As<IMessageBoxViewModel>(); // Not singleton!
builder.RegisterAssemblyTypes(this.GetType().Assembly);
builder.RegisterType<MessageBoxViewModel>().As<IMessageBoxViewModel>().ExternallyOwned(); // Not singleton!
builder.RegisterAssemblyTypes(this.GetType().Assembly).ExternallyOwned();
}
/// <summary>

View File

@ -1,129 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E37E66CD-04DE-4D48-AC90-26B331C1E7C0}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Bootstrappers</RootNamespace>
<AssemblyName>Bootstrappers</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autofac">
<HintPath>packages\Autofac.3.5.2\lib\net40\Autofac.dll</HintPath>
</Reference>
<Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Castle.Windsor">
<HintPath>packages\Castle.Windsor.3.3.0\lib\net45\Castle.Windsor.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity">
<HintPath>packages\Unity.3.5.1404.0\lib\net45\Microsoft.Practices.Unity.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity.Configuration">
<HintPath>packages\Unity.3.5.1404.0\lib\net45\Microsoft.Practices.Unity.Configuration.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity.RegistrationByConvention">
<HintPath>packages\Unity.3.5.1404.0\lib\net45\Microsoft.Practices.Unity.RegistrationByConvention.dll</HintPath>
</Reference>
<Reference Include="Ninject">
<HintPath>packages\Ninject.3.2.2.0\lib\net45-full\Ninject.dll</HintPath>
</Reference>
<Reference Include="nunit.core, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<HintPath>packages\NUnitTestAdapter.2.0.0\lib\nunit.core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.core.interfaces, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<HintPath>packages\NUnitTestAdapter.2.0.0\lib\nunit.core.interfaces.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<HintPath>packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.util, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<HintPath>packages\NUnitTestAdapter.2.0.0\lib\nunit.util.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NUnit.VisualStudio.TestAdapter, Version=2.0.0.0, Culture=neutral, PublicKeyToken=4cb40d35494691ac, processorArchitecture=MSIL">
<HintPath>packages\NUnitTestAdapter.2.0.0\lib\NUnit.VisualStudio.TestAdapter.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationFramework" />
<Reference Include="StructureMap, Version=3.1.6.186, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\structuremap.3.1.6.186\lib\net40\StructureMap.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="StructureMap.Net4, Version=3.1.6.186, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\structuremap.3.1.6.186\lib\net40\StructureMap.Net4.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AutofacBootstrapper.cs" />
<Compile Include="CastleWindsorBootstrapper.cs" />
<Compile Include="NinjectBootstrapper.cs" />
<Compile Include="NoIoCContainerBootstrapper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StructureMapBootstrapper.cs" />
<Compile Include="Tests\AutofacTests.cs" />
<Compile Include="Tests\BootstrapperTests.cs" />
<Compile Include="Tests\CastleWindsorTests.cs" />
<Compile Include="Tests\NinjectTests.cs" />
<Compile Include="Tests\NoIoCContainerTests.cs" />
<Compile Include="Tests\StructureMapTests.cs" />
<Compile Include="Tests\StubType.cs" />
<Compile Include="Tests\UnityTests.cs" />
<Compile Include="UnityBootstrapper.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Stylet\Stylet.csproj">
<Project>{2435bd00-ac12-48b0-ad36-9bab2fdec3f5}</Project>
<Name>Stylet</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E37E66CD-04DE-4D48-AC90-26B331C1E7C0}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Bootstrappers</RootNamespace>
<AssemblyName>Bootstrappers</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autofac, Version=4.2.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>packages\Autofac.4.2.1\lib\net45\Autofac.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Castle.Windsor">
<HintPath>packages\Castle.Windsor.3.3.0\lib\net45\Castle.Windsor.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity">
<HintPath>packages\Unity.3.5.1404.0\lib\net45\Microsoft.Practices.Unity.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity.Configuration">
<HintPath>packages\Unity.3.5.1404.0\lib\net45\Microsoft.Practices.Unity.Configuration.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity.RegistrationByConvention">
<HintPath>packages\Unity.3.5.1404.0\lib\net45\Microsoft.Practices.Unity.RegistrationByConvention.dll</HintPath>
</Reference>
<Reference Include="Ninject">
<HintPath>packages\Ninject.3.2.2.0\lib\net45-full\Ninject.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>packages\NUnit.3.5.0\lib\net45\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationFramework" />
<Reference Include="StructureMap, Version=3.1.6.186, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\structuremap.3.1.6.186\lib\net40\StructureMap.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="StructureMap.Net4, Version=3.1.6.186, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\structuremap.3.1.6.186\lib\net40\StructureMap.Net4.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AutofacBootstrapper.cs" />
<Compile Include="CastleWindsorBootstrapper.cs" />
<Compile Include="NinjectBootstrapper.cs" />
<Compile Include="NoIoCContainerBootstrapper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StructureMapBootstrapper.cs" />
<Compile Include="Tests\AutofacTests.cs" />
<Compile Include="Tests\BootstrapperTests.cs" />
<Compile Include="Tests\CastleWindsorTests.cs" />
<Compile Include="Tests\NinjectTests.cs" />
<Compile Include="Tests\NoIoCContainerTests.cs" />
<Compile Include="Tests\StructureMapTests.cs" />
<Compile Include="Tests\StubType.cs" />
<Compile Include="Tests\UnityTests.cs" />
<Compile Include="UnityBootstrapper.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Stylet\Stylet.csproj">
<Project>{2435bd00-ac12-48b0-ad36-9bab2fdec3f5}</Project>
<Name>Stylet</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
-->
</Project>

View File

@ -1,5 +1,6 @@
using Castle.Facilities.TypedFactory;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.Releasers;
using Castle.Windsor;
using Stylet;
using System;
@ -36,6 +37,15 @@ namespace Bootstrappers
ViewFactory = this.GetInstance,
ViewAssemblies = new List<Assembly>() { this.GetType().Assembly }
};
// Stylet does its own disposal of ViewModels: Castle Windsor shouldn't be doing the same
// Castle Windsor seems to be ver opinionated on this point, insisting that the container
// should be responsible for disposing all components. This is at odds with Stylet's approach
// (and indeed common sense).
#pragma warning disable CS0618 // Type or member is obsolete
container.Kernel.ReleasePolicy = new NoTrackingReleasePolicy();
#pragma warning restore CS0618 // Type or member is obsolete
container.Register(
Component.For<IViewManager>().Instance(new ViewManager(viewManagerConfig)),
Component.For<IWindowManagerConfig>().Instance(this),

View File

@ -36,7 +36,7 @@ namespace Bootstrappers
};
kernel.Bind<IViewManager>().ToConstant(new ViewManager(viewManagerConfig));
kernel.Bind<IWindowManagerConfig>().ToConstant(this);
kernel.Bind<IWindowManagerConfig>().ToConstant(this).InTransientScope();
kernel.Bind<IWindowManager>().ToMethod(c => new WindowManager(c.Kernel.Get<IViewManager>(), () => c.Kernel.Get<IMessageBoxViewModel>(), c.Kernel.Get<IWindowManagerConfig>())).InSingletonScope();
kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope();
kernel.Bind<IMessageBoxViewModel>().To<MessageBoxViewModel>(); // Not singleton!

View File

@ -39,7 +39,8 @@ namespace Bootstrappers
};
config.For<IViewManager>().Add(new ViewManager(viewManagerConfig));
config.For<IWindowManagerConfig>().Add(this);
// Trick it into not taking ownership of (and disposing) the instance
config.For<IWindowManagerConfig>().Add(c => this).LifecycleIs<UniquePerRequestLifecycle>();
config.For<IWindowManager>().Add<WindowManager>().LifecycleIs<SingletonLifecycle>();
config.For<IEventAggregator>().Add<EventAggregator>().LifecycleIs<SingletonLifecycle>();
config.For<IMessageBoxViewModel>().Add<MessageBoxViewModel>().LifecycleIs<UniquePerRequestLifecycle>();

View File

@ -12,6 +12,8 @@ namespace Bootstrappers.Tests
{
public List<string> ConfigureLog { get; set; }
public int DisposeCount { get; private set; }
public MyAutofacBootstrapper()
{
this.ConfigureLog = new List<string>();
@ -44,6 +46,12 @@ namespace Bootstrappers.Tests
{
base.ConfigureBootstrapper();
}
public override void Dispose()
{
base.Dispose();
this.DisposeCount++;
}
}
[TestFixture(Category = "Autofac")]

View File

@ -12,12 +12,13 @@ namespace Bootstrappers.Tests
public interface ITestBootstrapper
{
int DisposeCount { get; }
object GetInstance(Type type);
void ConfigureBootstrapper();
List<string> ConfigureLog { get; }
}
public abstract class BootstrapperTests<TBootstrapper> where TBootstrapper : ITestBootstrapper
public abstract class BootstrapperTests<TBootstrapper> where TBootstrapper : ITestBootstrapper, IDisposable
{
protected TBootstrapper bootstrapper;
@ -125,5 +126,29 @@ namespace Bootstrappers.Tests
Assert.NotNull(vm1);
Assert.AreNotEqual(vm1, vm2);
}
[Test]
public void DoesNotMultiplyDisposeWindowManagerConfig()
{
// The bootstrapper implements the IWindowManagerConfig. Fetch the IWindowManager to force the
// IWindowManagerConfig to be constructed, then dispose the bootstrapper, and make sure that
// the container doesn't dispose the IWindowManagerConfig again
var windowManager = this.bootstrapper.GetInstance(typeof(IWindowManager));
this.bootstrapper.Dispose();
Assert.AreEqual(1, this.bootstrapper.DisposeCount);
}
[Test]
public void DoesNotDisposeTransientInstances()
{
StubType.Reset();
var vm = this.bootstrapper.GetInstance(typeof(StubType));
this.bootstrapper.Dispose();
Assert.AreEqual(0, StubType.DisposeCount);
}
}
}

View File

@ -12,6 +12,8 @@ namespace Bootstrappers.Tests
{
public List<string> ConfigureLog { get; set; }
public int DisposeCount { get; private set; }
public MyCastleWindsorBootstrapper()
{
this.ConfigureLog = new List<string>();
@ -44,6 +46,12 @@ namespace Bootstrappers.Tests
{
base.ConfigureBootstrapper();
}
public override void Dispose()
{
base.Dispose();
this.DisposeCount++;
}
}
[TestFixture(Category = "Castle Windsor")]

View File

@ -12,6 +12,8 @@ namespace Bootstrappers.Tests
{
public List<string> ConfigureLog { get; set; }
public int DisposeCount { get; private set; }
public MyNinjectBootstrapper()
{
this.ConfigureLog = new List<string>();
@ -44,6 +46,12 @@ namespace Bootstrappers.Tests
{
base.ConfigureBootstrapper();
}
public override void Dispose()
{
base.Dispose();
this.DisposeCount++;
}
}
[TestFixture(Category="Ninject")]

View File

@ -11,6 +11,8 @@ namespace Bootstrappers.Tests
{
public List<string> ConfigureLog { get; set; }
public int DisposeCount { get; private set; }
public MyNoIocContainerBootstrapper()
{
this.ConfigureLog = new List<string>();
@ -48,6 +50,12 @@ namespace Bootstrappers.Tests
{
base.ConfigureBootstrapper();
}
public override void Dispose()
{
base.Dispose();
this.DisposeCount++;
}
}
[TestFixture(Category = "NoIoCContainer")]

View File

@ -12,6 +12,8 @@ namespace Bootstrappers.Tests
{
public List<string> ConfigureLog { get; set; }
public int DisposeCount { get; private set; }
public MyStructureMapBootstrapper()
{
this.ConfigureLog = new List<string>();
@ -44,6 +46,12 @@ namespace Bootstrappers.Tests
{
base.ConfigureBootstrapper();
}
public override void Dispose()
{
base.Dispose();
this.DisposeCount++;
}
}
[TestFixture(Category = "StructureMap")]

View File

@ -6,7 +6,18 @@ using System.Threading.Tasks;
namespace Bootstrappers.Tests
{
public class StubType
public class StubType : IDisposable
{
public static int DisposeCount { get; private set; }
public static void Reset()
{
DisposeCount = 0;
}
public void Dispose()
{
DisposeCount++;
}
}
}

View File

@ -12,6 +12,8 @@ namespace Bootstrappers.Tests
{
public List<string> ConfigureLog { get; set; }
public int DisposeCount { get; private set; }
public MyUnityBootstrapper()
{
this.ConfigureLog = new List<string>();
@ -44,6 +46,12 @@ namespace Bootstrappers.Tests
{
base.ConfigureBootstrapper();
}
public override void Dispose()
{
base.Dispose();
this.DisposeCount++;
}
}
[TestFixture(Category = "Unity")]

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="3.5.2" targetFramework="net45" />
<package id="Castle.Core" version="3.3.3" targetFramework="net45" />
<package id="Castle.Windsor" version="3.3.0" targetFramework="net45" />
<package id="Ninject" version="3.2.2.0" targetFramework="net45" />
<package id="NUnit" version="2.6.4" targetFramework="net45" />
<package id="NUnitTestAdapter" version="2.0.0" targetFramework="net45" />
<package id="structuremap" version="3.1.6.186" targetFramework="net45" />
<package id="Unity" version="3.5.1404.0" targetFramework="net45" />
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="4.2.1" targetFramework="net45" />
<package id="Castle.Core" version="3.3.3" targetFramework="net45" />
<package id="Castle.Windsor" version="3.3.0" targetFramework="net45" />
<package id="Ninject" version="3.2.2.0" targetFramework="net45" />
<package id="NUnit" version="3.5.0" targetFramework="net45" />
<package id="NUnit3TestAdapter" version="3.6.0" targetFramework="net45" />
<package id="structuremap" version="3.1.6.186" targetFramework="net45" />
<package id="Unity" version="3.5.1404.0" targetFramework="net45" />
</packages>