Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for .NET Core 3.x #517

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
de1621e
Add support for .NET Core 3.x as a container for IServiceProvider
ltines Apr 15, 2020
f723d87
Add readme
ltines Apr 15, 2020
b6655d5
Fix renaming
ltines Apr 15, 2020
90f31a2
Add to solution
ltines Apr 15, 2020
864e3c6
Remove the need for InternalsVisible
ltines Apr 15, 2020
fb8365c
Remove Nesting and fixed layout
ltines Apr 19, 2020
e8a766a
Fix layout
ltines Apr 19, 2020
d2e2c0a
Remove warning about target framework
ltines Apr 19, 2020
0c1ca73
Small fixes for coding style
ltines Apr 19, 2020
858c4b7
Fix typos
ltines Apr 22, 2020
de7e469
Fix another typo
ltines Apr 22, 2020
d620c7f
Move starting root scope bit earilier
ltines Apr 24, 2020
92891b7
Fix accessing root scope from different thread
ltines Apr 25, 2020
70743f1
Adding .NET 4.6.1 + some explicit dependendecies
ltines May 3, 2020
0c94508
Add comment why we're using MS namespace
ltines May 3, 2020
fea6656
Typos and intendation
ltines May 3, 2020
4f0d338
Remove coverage dependency
ltines May 3, 2020
043fcdb
Fix targets
ltines May 3, 2020
9557c87
Add tests for net461
ltines May 3, 2020
e1b13b8
Rename project and fix intendation to tabs
ltines May 24, 2020
240c9f9
add both restore and tests to build
ltines May 24, 2020
e5537c7
Reverse order of resolved collection.
ltines May 25, 2020
70586b5
Keep registration order of instances when resolving a collection
ltines Jun 6, 2020
e79a539
Remove NetCore from names and split code into folders
ltines Jun 6, 2020
5699f61
Fix intendation and remove debug test
ltines Jun 7, 2020
598740a
Cosmetic changes
ltines Jun 13, 2020
be676fa
Fix rename
ltines Jun 13, 2020
21da7e0
Update docu
ltines Jun 13, 2020
6b86d13
Move to docs
ltines Jun 15, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Castle.Windsor.sln
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Castle.Facilities.AspNet.We
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Castle.Facilities.AspNet.WebApi.Tests", "src\Castle.Facilities.AspNet.WebApi.Tests\Castle.Facilities.AspNet.WebApi.Tests.csproj", "{5CD7AE3F-105F-4C7A-940D-B7D130940E66}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Castle.Windsor.Extensions.DependencyInjection", "src\Castle.Windsor.Extensions.DependencyInjection\Castle.Windsor.Extensions.DependencyInjection.csproj", "{7710F8A2-33D8-40C1-89F5-648577B5DD01}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Castle.Windsor.Extensions.DependencyInjection.Tests", "src\Castle.Windsor.Extensions.DependencyInjection.Tests\Castle.Windsor.Extensions.DependencyInjection.Tests.csproj", "{7C2F5733-0E06-4201-A15A-1ED1F3308DB4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -121,6 +125,14 @@ Global
{5CD7AE3F-105F-4C7A-940D-B7D130940E66}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5CD7AE3F-105F-4C7A-940D-B7D130940E66}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5CD7AE3F-105F-4C7A-940D-B7D130940E66}.Release|Any CPU.Build.0 = Release|Any CPU
{7710F8A2-33D8-40C1-89F5-648577B5DD01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7710F8A2-33D8-40C1-89F5-648577B5DD01}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7710F8A2-33D8-40C1-89F5-648577B5DD01}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7710F8A2-33D8-40C1-89F5-648577B5DD01}.Release|Any CPU.Build.0 = Release|Any CPU
{7C2F5733-0E06-4201-A15A-1ED1F3308DB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C2F5733-0E06-4201-A15A-1ED1F3308DB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C2F5733-0E06-4201-A15A-1ED1F3308DB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C2F5733-0E06-4201-A15A-1ED1F3308DB4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -140,6 +152,8 @@ Global
{175EF5FC-C3A5-4EE4-BFE6-77F0A53CBA3B} = {7935AFF5-BF6D-4D59-8D66-058B6557F70F}
{501276B2-166F-40CA-AFF0-2F2D70BF4E4F} = {7935AFF5-BF6D-4D59-8D66-058B6557F70F}
{5CD7AE3F-105F-4C7A-940D-B7D130940E66} = {7935AFF5-BF6D-4D59-8D66-058B6557F70F}
{7710F8A2-33D8-40C1-89F5-648577B5DD01} = {7935AFF5-BF6D-4D59-8D66-058B6557F70F}
{7C2F5733-0E06-4201-A15A-1ED1F3308DB4} = {7935AFF5-BF6D-4D59-8D66-058B6557F70F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CC2A1EB6-49EC-4064-AD8B-29439E8A45E4}
Expand Down
4 changes: 4 additions & 0 deletions buildscripts/build.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,14 @@ dotnet restore ./src/Castle.Facilities.AspNet.WebApi/Castle.Facilities.AspNet.We
dotnet restore ./src/Castle.Facilities.AspNet.WebApi.Tests/Castle.Facilities.AspNet.WebApi.Tests.csproj
dotnet restore ./src/Castle.Facilities.AspNetCore/Castle.Facilities.AspNetCore.csproj
dotnet restore ./src/Castle.Facilities.AspNetCore.Tests/Castle.Facilities.AspNetCore.Tests.csproj
dotnet restore ./src/Castle.Windsor.Extensions.DependencyInjection.Tests/Castle.Windsor.Extensions.DependencyInjection.Tests.csproj
dotnet restore ./src/Castle.Windsor.Extensions.DependencyInjection/Castle.Windsor.Extensions.DependencyInjection.csproj
dotnet restore ./src/Castle.Facilities.WcfIntegration/Castle.Facilities.WcfIntegration.csproj
dotnet restore ./src/Castle.Facilities.WcfIntegration.Demo/Castle.Facilities.WcfIntegration.Demo.csproj
dotnet restore ./src/Castle.Facilities.WcfIntegration.Tests/Castle.Facilities.WcfIntegration.Tests.csproj
dotnet restore ./src/Castle.Windsor.Tests/Castle.Windsor.Tests.csproj


GOTO build

:build
Expand All @@ -62,6 +65,7 @@ dotnet test src\Castle.Facilities.AspNet.SystemWeb.Tests || exit /b 1
dotnet test src\Castle.Facilities.AspNet.Mvc.Tests || exit /b 1
dotnet test src\Castle.Facilities.AspNet.WebApi.Tests || exit /b 1
dotnet test src\Castle.Facilities.WcfIntegration.Tests || exit /b 1
dotnet test src\Castle.Windsor.Extensions.DependencyInjection.Tests || exit /b 1

GOTO nuget_explicit_versions

Expand Down
2 changes: 1 addition & 1 deletion buildscripts/common.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>

<PropertyGroup>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn);CS1591;NU5048</NoWarn>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/castleproject/Windsor</RepositoryUrl>
<BuildVersion>0.0.0</BuildVersion>
Expand Down
39 changes: 39 additions & 0 deletions docs/net-dependency-extension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# .NET Core 3.0 Dependency injection extension
Service Provider using Castle Windsor container for ASP.NET Core 3.x. Fully replaces ASP.NET Core Default Service Provider (container).

## How does it work?

### Registering services
You can register service either `IServiceCollection` in startup class. Just as you would with standard .NET Dependency Injection add your registration to the `Startup` class.

Alternatively you can create `ConfigureServices` method in `Startup` class that gets passed the `IWindsorContainer` instance and register the services there
```
public void ConfigureContainer (IWindsorContainer container)
{
container.Install(new MyInstaller());
}
```

To match lifestyle of the registered components to .NET Service Scope, use `LifeStyle.ScopedToNetServiceScope()`.

### Services lifestyle
Because there are subtle differences between .NET and Castle Windsor lifestyle semantics services registered via `IServiceCollection` are following .NET semantics:


| .NET lifestyle | Description |
|:-:|:-:|
| Scoped | Lifecycle of the component is bound to the scope associated with `IServiceProvider` that resolved it. Even if it's not the closest one. The instance is disposed when `IServiceScope` is disposed. |
| Transient | New instance is provided when resolved but lifecycle is bound to the scope associated with `IServiceProvider` that resolved it. All instances are disposed when `IServiceScope` is disposed. |
| Singleton | Only one instance ever exists and it's disposed once outermost `IServiceProvider` is disposed (usually application shutdown). |

## What do I need to set it up?
1. Add `Castle.Windsor.Extensions.DependencyInjection` package to your application.
2. Add `UseWindsorContainerServiceProvider()` when creating the Host
```
Host.CreateDefaultBuilder(args)
.UseWindsorContainerServiceProvider()
```
This will register an `IServiceProviderFactory`
2. Any services registred in `Startup.ConfigureServices` will be registered with `IWindsorContainer`. No need to cross-wire since `IWindsorContainer` is the only `IServiceProvider`
4. To access the container directly inject either `IWindsorContainer` or `IServiceProvider`

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netcoreapp2.2;net461</TargetFrameworks>

<IsPackable>false</IsPackable>

<AssemblyName>Castle.Windsor.Extensions.DependencyInjection.Tests</AssemblyName>

<RootNamespace>Castle.Windsor.Extensions.DependencyInjection.Tests</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Specification.Tests" Version="3.1.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.assert" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Castle.Windsor.Extensions.DependencyInjection\Castle.Windsor.Extensions.DependencyInjection.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Castle.Windsor.Extensions.DependencyInjection.Tests
{
using System;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Specification;

public class WindsorScopedServiceProviderTests : DependencyInjectionSpecificationTests
{
protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection)
{
var factory = new WindsorServiceProviderFactory();
var container = factory.CreateBuilder(serviceCollection);
return factory.CreateServiceProvider(container);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
</PropertyGroup>

<Import Project="..\..\buildscripts\common.props"></Import>

<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>Castle.Windsor.Extensions.DependencyInjection</PackageId>
<Title>Castle Windsor extension for .NET Extensions DependencyInjection</Title>
<Description>Allows to use Castle Windsor as a container using IServiceProvider</Description>
<PackageTags>castle, windsor, inversionOfControl, DependencyInjection, aspnet, core</PackageTags>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);NU5125</NoWarn> <!-- remove once tools are truly ready for NuGet's new 'license' element -->
<AssemblyName>Castle.Windsor.Extensions.DependencyInjection</AssemblyName>
<RootNamespace>Castle.Windsor.Extensions.DependencyInjection</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.3" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.3" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.3" />
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Castle.Windsor\Castle.Windsor.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//Microsoft namespace is intentional - suggested by Microsoft
namespace Microsoft.Extensions.Hosting
{
using Castle.Windsor;
using Castle.Windsor.Extensions.DependencyInjection;

public static class HostBuilderExtensions
{
/// <summary>
/// Uses <see name="IWindsorContainer" /> as the DI container for the host
/// </summary>
/// <param name="hostBuilder">Host builder</param>
/// <returns>Host builder</returns>
public static IHostBuilder UseWindsorContainerServiceProvider(this IHostBuilder hostBuilder)
{
return hostBuilder.UseServiceProviderFactory<IWindsorContainer>(new WindsorServiceProviderFactory());
}
jonorossi marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Castle.Windsor.Extensions.DependencyInjection.Extensions
{
using System;
using System.Reflection;

using Castle.MicroKernel;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Castle.Windsor.Extensions.DependencyInjection.Resolvers;

using Microsoft.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
public static IWindsorContainer CreateContainer(this IServiceCollection serviceCollection, WindsorServiceProviderFactory factory)
{
var container = new WindsorContainer();
container.Kernel.AddSubSystem(
SubSystemConstants.NamingKey,
new SubSystems.DependencyInjectionNamingSubsystem()
);

if (serviceCollection == null)
{
return container;
}

container.Register(
Component
.For<IWindsorContainer>()
.Instance(container),
Component
.For<IServiceProvider, ISupportRequiredService>()
.ImplementedBy<WindsorScopedServiceProvider>()
.LifeStyle.ScopedToNetServiceScope(),
Component
.For<IServiceScopeFactory>()
.ImplementedBy<WindsorScopeFactory>()
.LifestyleSingleton(),
Component
.For<WindsorServiceProviderFactory>()
.Instance(factory)
.LifestyleSingleton()
);

container.Kernel.Resolver.AddSubResolver(new RegisteredCollectionResolver(container.Kernel));
container.Kernel.Resolver.AddSubResolver(new OptionsSubResolver(container.Kernel));
container.Kernel.Resolver.AddSubResolver(new LoggerDependencyResolver(container.Kernel));

foreach (var service in serviceCollection)
{
container.Register(service.CreateWindsorRegistration());
}

return container;
}


public static IRegistration CreateWindsorRegistration(this Microsoft.Extensions.DependencyInjection.ServiceDescriptor service)
{
if (service.ServiceType.ContainsGenericParameters)
{
return RegistrationAdapter.FromOpenGenericServiceDescriptor(service);
}
else
{
var method = typeof(RegistrationAdapter).GetMethod("FromServiceDescriptor", BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(service.ServiceType);
return method.Invoke(null, new object[] { service }) as IRegistration;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Castle.Windsor.Extensions.DependencyInjection.Extensions
{
using System;

using Castle.MicroKernel.Registration;
using Castle.MicroKernel.Registration.Lifestyle;

using Castle.Windsor.Extensions.DependencyInjection.Scope;

public static class WindsorExtensions
{
/// <summary>
/// Scopes the lifestyle of the component to a scope started by <see name="IServiceScopeFactory.CreateScope" />
/// </summary>
/// <typeparam name="TService">Service type</typeparam>
public static ComponentRegistration<TService> ScopedToNetServiceScope<TService>(this LifestyleGroup<TService> lifestyle) where TService : class
{
return lifestyle.Scoped<ExtensionContainerScopeAccessor>();
}

/// <summary>
/// Returns new instances everytime it's resolved but disposes it on <see name="IServiceScope" /> end
/// </summary>
/// <typeparam name="TService">Service type</typeparam>
public static ComponentRegistration<TService> LifestyleNetTransient<TService>(this ComponentRegistration<TService> registration) where TService : class
{
return registration
.Attribute(ExtensionContainerScope.TransientMarker).Eq(Boolean.TrueString)
.LifeStyle.ScopedToNetServiceScope(); //.NET core expects new instances but release on scope dispose
}

/// <summary>
/// Singleton instance with .NET Core semantics
/// </summary>
/// <typeparam name="TService"></typeparam>
public static ComponentRegistration<TService> NetStatic<TService>(this LifestyleGroup<TService> lifestyle) where TService : class
{
return lifestyle
.Scoped<ExtensionContainerRootScopeAccessor>();
}
}
}
Loading