├── .vscode ├── settings.json ├── launch.json └── tasks.json ├── packlocal.sh ├── omnisharp.json ├── src ├── LightInject.Microsoft.DependencyInjection.Benchmarks │ ├── Program.cs │ ├── LightInject.Microsoft.DependencyInjection.Benchmarks.csproj │ ├── SingletonBenchmarks.cs │ ├── Singleton.cs │ ├── ScopedService.cs │ ├── BeginScopeBenchmarks.cs │ ├── TestController.cs │ └── RepositoryTransients.cs ├── LightInject.Microsoft.DependencyInjection.Tests │ ├── LightInjectSpecificationTests.cs │ ├── LightInjectSpecificationTestsWithCurrentScopeEnabled.cs │ ├── LightInject.Microsoft.DependencyInjection.Tests.csproj │ ├── ServiceCollectionTests.cs │ ├── AsyncDisposableTests.cs │ ├── DefaultServiceTests.cs │ ├── ServiceProviderFactoryTests.cs │ ├── LightInjectKeyedSpecificationTests.cs │ └── ServiceContainerTests.cs └── LightInject.Microsoft.DependencyInjection │ ├── LightInject.Microsoft.DependencyInjection.csproj │ └── LightInject.Microsoft.DependencyInjection.cs ├── .github └── workflows │ └── main.yml ├── license.md ├── readme.md ├── CHANGELOG.md ├── .gitattributes ├── LightInject.Microsoft.DependencyInjection.sln ├── .gitignore └── .idea └── .idea.LightInject.Microsoft.DependencyInjection └── .idea └── workspace.xml /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "coverage-gutters.lcovname": "../build/Artifacts/TestCoverage/coverage.info", 3 | "cSpell.words": [ 4 | "Xunit" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /packlocal.sh: -------------------------------------------------------------------------------- 1 | dotnet pack "src/LightInject.Microsoft.DependencyInjection/LightInject.Microsoft.DependencyInjection.csproj" --configuration Release --output "build/Artifacts/NuGet" /property:Version=$1 2 | -------------------------------------------------------------------------------- /omnisharp.json: -------------------------------------------------------------------------------- 1 | { 2 | "FormattingOptions": { 3 | "EnableEditorConfigSupport": true, 4 | "OrganizeImports": true 5 | }, 6 | "RoslynExtensionsOptions": { 7 | "EnableAnalyzersSupport": true, 8 | "EnableDecompilationSupport": true 9 | }, 10 | "script": { 11 | "enableScriptNuGetReferences": true, 12 | "defaultTargetFramework": "net8.0" 13 | } 14 | } -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BenchmarkDotNet.Attributes; 3 | using BenchmarkDotNet.Configs; 4 | using BenchmarkDotNet.Running; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace LightInject.Benchmarks 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Tests/LightInjectSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | namespace LightInject.Microsoft.DependencyInjection.Tests 2 | { 3 | using System; 4 | using global::Microsoft.Extensions.DependencyInjection; 5 | using global::Microsoft.Extensions.DependencyInjection.Specification; 6 | 7 | public class LightInjectSpecificationTests : DependencyInjectionSpecificationTests 8 | { 9 | protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection) 10 | { 11 | return serviceCollection.CreateLightInjectServiceProvider(new ContainerOptions() { EnableCurrentScope = false }); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Tests/LightInjectSpecificationTestsWithCurrentScopeEnabled.cs: -------------------------------------------------------------------------------- 1 | namespace LightInject.Microsoft.DependencyInjection.Tests 2 | { 3 | using System; 4 | using global::Microsoft.Extensions.DependencyInjection; 5 | using global::Microsoft.Extensions.DependencyInjection.Specification; 6 | 7 | public class LightInjectSpecificationTestsWithCurrentScopeEnabled : DependencyInjectionSpecificationTests 8 | { 9 | protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection) 10 | { 11 | return serviceCollection.CreateLightInjectServiceProvider(new ContainerOptions() { EnableCurrentScope = true }); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v1 11 | - name: Install .Net Core 12 | uses: actions/setup-dotnet@v2.0.0 13 | with: 14 | dotnet-version: 8.0.x 15 | - name: Install dotnet-script 16 | run: dotnet tool install dotnet-script --tool-path dotnet-script-tool 17 | 18 | - name: Run build script 19 | run: dotnet-script-tool/dotnet-script build/build.csx 20 | env: # Or as an environment variable 21 | GITHUB_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | IS_SECURE_BUILDENVIRONMENT: ${{ secrets.IS_SECURE_BUILDENVIRONMENT }} 23 | NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} 24 | -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Benchmarks/LightInject.Microsoft.DependencyInjection.Benchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | false 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Bernhard Richter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Benchmarks/SingletonBenchmarks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BenchmarkDotNet.Attributes; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace LightInject.Microsoft.DependencyInjection.Benchmarks 6 | { 7 | public class SingletonBenchmarks 8 | { 9 | private IServiceProvider lightInjectServiceProvider; 10 | private IServiceProvider defaultServiceProvider; 11 | 12 | [GlobalSetup] 13 | public void Setup() 14 | { 15 | var serviceCollection = new ServiceCollection(); 16 | serviceCollection.AddSingleton(); 17 | 18 | lightInjectServiceProvider = serviceCollection.CreateLightInjectServiceProvider(); 19 | defaultServiceProvider = serviceCollection.BuildServiceProvider(); 20 | } 21 | 22 | [Benchmark] 23 | public void UsingLightInject() 24 | { 25 | var instance = lightInjectServiceProvider.GetService(); 26 | } 27 | 28 | [Benchmark] 29 | public void UsingMicrosoft() 30 | { 31 | var instance = defaultServiceProvider.GetService(); 32 | } 33 | 34 | } 35 | } -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Tests/LightInject.Microsoft.DependencyInjection.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | runtime; build; native; contentfiles; analyzers; buildtransitive 9 | all 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/src/LightInject.Microsoft.DependencyInjection.Benchmarks/bin/Debug/netcoreapp2.2/LightInject.Microsoft.DependencyInjection.Benchmarks.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/LightInject.Microsoft.DependencyInjection.Benchmarks", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "externalTerminal", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | }, 26 | { 27 | "name": ".NET Script Debug", 28 | "type": "coreclr", 29 | "request": "launch", 30 | "program": "${env:HOME}/.dotnet/tools/dotnet-script", 31 | "args": [ 32 | "${file}" 33 | ], 34 | "windows": { 35 | "program": "${env:USERPROFILE}/.dotnet/tools/dotnet-script.exe", 36 | }, 37 | "cwd": "${workspaceFolder}", 38 | "stopAtEntry": true, 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Tests/ServiceCollectionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Text; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Xunit; 6 | 7 | namespace LightInject.Microsoft.DependencyInjection.Tests 8 | { 9 | public class ServiceCollectionTests 10 | { 11 | [Fact] 12 | public void ShouldCreateServiceProviderFromServiceCollection() 13 | { 14 | var serviceCollection = new ServiceCollection(); 15 | var provider = serviceCollection.CreateLightInjectServiceProvider(); 16 | Assert.IsAssignableFrom(provider); 17 | } 18 | 19 | [Fact] 20 | public void ShouldCreateServiceProviderWithOptionsFromServiceCollection() 21 | { 22 | StringBuilder log = new StringBuilder(); 23 | ContainerOptions.Default.LogFactory = (t) => l => log.AppendLine(l.Message); 24 | 25 | var serviceCollection = new ServiceCollection(); 26 | serviceCollection.AddSingleton("42"); 27 | var provider = serviceCollection.CreateLightInjectServiceProvider(); 28 | var instance = provider.GetService(); 29 | Assert.IsAssignableFrom(provider); 30 | 31 | Assert.NotEmpty(log.ToString()); 32 | } 33 | 34 | [Fact] 35 | public void ShouldSupportNonRuntimeTypeFactoryRegistrations() 36 | { 37 | var serviceCollection = new ServiceCollection(); 38 | serviceCollection.AddSingleton(new TypeDelegator(typeof(int)), _ => 42); 39 | Assert.NotNull(serviceCollection.CreateLightInjectServiceProvider()); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection/LightInject.Microsoft.DependencyInjection.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0;netstandard2.0 5 | Bernhard Richter 6 | Enables LightInject to be used as the service container in ASP.NET Core and Entity Framework 7 applications. 7 | Bernhard Richter 8 | http://opensource.org/licenses/MIT 9 | 10 | https://github.com/seesharper/LightInject.Microsoft.DependencyInjection 11 | git 12 | Ioc Dependency-Injection Inversion-of-Control LightInject ASP.NET Entity-Framework 13 | true 14 | portable 15 | true 16 | true 17 | true 18 | latest 19 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 20 | 21 | 22 | 23 | 24 | 25 | all 26 | runtime; build; native; contentfiles; analyzers 27 | 28 | 29 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # LightInject.Microsoft.DependencyInjection 2 | [![AppVeyor](https://img.shields.io/appveyor/ci/gruntjs/grunt.svg?maxAge=2592000)](https://ci.appveyor.com/project/seesharper/lightinject-microsoft-dependencyinjection) 3 | [![NuGet](https://img.shields.io/nuget/v/LightInject.Microsoft.DependencyInjection.svg?maxAge=2592000)]() 4 | [![GitHub tag](https://img.shields.io/github/tag/seesharper/LightInject.Microsoft.DependencyInjection.svg?maxAge=2592000)]() 5 | 6 | Implements the [Microsoft.Extensions.DependencyInjection.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection.Abstractions/) and makes it possible to create an `IServiceProvider` that is 100% compatible with the [Microsoft.Extensions.DependencyInjection.Specification.Tests](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection.Specification.Tests). 7 | 8 | > Note: This package is NOT meant to be used directly with AspNetCore applications. If the target application is an AspNetCore application, use the [LightInject.Microsoft.Hosting](https://www.nuget.org/packages/LightInject.Microsoft.Hosting/) package instead. 9 | 10 | ## Installing 11 | 12 | ```shell 13 | dotnet add package LightInject.Microsoft.DependencyInjection 14 | ``` 15 | 16 | ## Usage 17 | ```c# 18 | var services = new ServiceCollection(); 19 | services.AddTransient(); 20 | var provider = services.CreateLightInjectServiceProvider(); 21 | ``` 22 | 23 | It is also possible to create an `IServiceProvider` directly from an `IServiceContainer` instance. 24 | 25 | ```c# 26 | var container = new ServiceContainer(Options.Default.WithMicrosoftSettings); 27 | var provider = container.CreateServiceProvider(); 28 | ``` 29 | 30 | > Note: Make sure that the `Options.Default.WithMicrosoftSettings` is passed in as `options` when creating the container. This makes the provider compliant with the default provider from Microsoft. 31 | 32 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "args": [ 8 | "build", 9 | "/property:GenerateFullPaths=true" 10 | ], 11 | "type": "shell", 12 | "options": { 13 | "cwd": "${workspaceFolder}" 14 | }, 15 | "group": "build", 16 | "presentation": { 17 | "reveal": "always" 18 | }, 19 | "problemMatcher": "$msCompile" 20 | }, 21 | { 22 | "label": "rebuild", 23 | "command": "dotnet", 24 | "args": [ 25 | "build", 26 | "--no-incremental", 27 | "/property:GenerateFullPaths=true" 28 | ], 29 | "type": "shell", 30 | "options": { 31 | "cwd": "${workspaceFolder}" 32 | }, 33 | "group": "build", 34 | "presentation": { 35 | "reveal": "always", 36 | "clear": true 37 | }, 38 | "problemMatcher": "$msCompile" 39 | }, 40 | { 41 | "label": "test", 42 | "command": "dotnet", 43 | "type": "process", 44 | "args": [ 45 | "script", 46 | "${workspaceFolder}/build/build.csx", 47 | "test" 48 | ], 49 | "problemMatcher": "$msCompile", 50 | "group": { 51 | "kind": "test", 52 | "isDefault": true 53 | }, 54 | "presentation": { 55 | "echo": true, 56 | "reveal": "always", 57 | "focus": false, 58 | "panel": "shared", 59 | "showReuseMessage": true, 60 | "clear": true 61 | } 62 | }, 63 | { 64 | "label": "test with coverage", 65 | "command": "dotnet", 66 | "type": "process", 67 | "args": [ 68 | "script", 69 | "${workspaceFolder}/build/build.csx", 70 | "testcoverage" 71 | ], 72 | "problemMatcher": "$msCompile", 73 | "group": { 74 | "kind": "test", 75 | "isDefault": true 76 | } 77 | } 78 | ] 79 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v1.1.0](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/tree/v1.1.0) (2016-08-30) 4 | [Full Changelog](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/compare/v1.0.1...v1.1.0) 5 | 6 | **Closed issues:** 7 | 8 | - Implement explicit registration of enumerable services to ensure deterministic ordering of services [\#3](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/issues/3) 9 | 10 | **Merged pull requests:** 11 | 12 | - Updated to 4.1.0 [\#5](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/pull/5) ([geirsagberg](https://github.com/geirsagberg)) 13 | 14 | ## [v1.0.1](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/tree/v1.0.1) (2016-07-05) 15 | [Full Changelog](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/compare/v1.0.1-rc2-2...v1.0.1) 16 | 17 | ## [v1.0.1-rc2-2](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/tree/v1.0.1-rc2-2) (2016-05-31) 18 | [Full Changelog](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/compare/v1.0.2-rc2...v1.0.1-rc2-2) 19 | 20 | **Closed issues:** 21 | 22 | - Update to RC2 and build with dotnet [\#2](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/issues/2) 23 | 24 | ## [v1.0.2-rc2](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/tree/v1.0.2-rc2) (2016-05-20) 25 | [Full Changelog](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/compare/v1.0.1-rc2...v1.0.2-rc2) 26 | 27 | **Closed issues:** 28 | 29 | - Use PerContainerScopeManager [\#1](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/issues/1) 30 | 31 | ## [v1.0.1-rc2](https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/tree/v1.0.1-rc2) (2016-01-28) 32 | 33 | 34 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Benchmarks/Singleton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LightInject.Microsoft.DependencyInjection.Benchmarks 4 | { 5 | 6 | public interface ISingleton1 7 | { 8 | void DoSomething(); 9 | } 10 | 11 | 12 | public interface ISingleton2 13 | { 14 | void DoSomething(); 15 | } 16 | 17 | 18 | public interface ISingleton3 19 | { 20 | void DoSomething(); 21 | } 22 | 23 | public class Singleton1 : ISingleton1 24 | { 25 | private static int counter; 26 | 27 | public Singleton1() 28 | { 29 | System.Threading.Interlocked.Increment(ref counter); 30 | } 31 | 32 | public static int Instances 33 | { 34 | get { return counter; } 35 | set { counter = value; } 36 | } 37 | 38 | public void DoSomething() 39 | { 40 | Console.WriteLine("Hello"); 41 | } 42 | } 43 | 44 | public class Singleton2 : ISingleton2 45 | { 46 | private static int counter; 47 | 48 | public Singleton2() 49 | { 50 | System.Threading.Interlocked.Increment(ref counter); 51 | } 52 | 53 | public static int Instances 54 | { 55 | get { return counter; } 56 | set { counter = value; } 57 | } 58 | 59 | public void DoSomething() 60 | { 61 | Console.WriteLine("Hello"); 62 | } 63 | } 64 | 65 | public class Singleton3 : ISingleton3 66 | { 67 | private static int counter; 68 | 69 | public Singleton3() 70 | { 71 | System.Threading.Interlocked.Increment(ref counter); 72 | } 73 | 74 | public static int Instances 75 | { 76 | get { return counter; } 77 | set { counter = value; } 78 | } 79 | 80 | public void DoSomething() 81 | { 82 | Console.WriteLine("Hello"); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /LightInject.Microsoft.DependencyInjection.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{15856E1C-AB65-4DAA-8C57-3478DE8C8420}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightInject.Microsoft.DependencyInjection", "src\LightInject.Microsoft.DependencyInjection\LightInject.Microsoft.DependencyInjection.csproj", "{F66CEE6C-90B1-4BF8-80E0-5B2594819F59}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightInject.Microsoft.DependencyInjection.Tests", "src\LightInject.Microsoft.DependencyInjection.Tests\LightInject.Microsoft.DependencyInjection.Tests.csproj", "{8B4FA81E-8014-44D5-8FF1-09287630355C}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightInject.Microsoft.DependencyInjection.Benchmarks", "src\LightInject.Microsoft.DependencyInjection.Benchmarks\LightInject.Microsoft.DependencyInjection.Benchmarks.csproj", "{28EE4FC0-43BB-458A-B186-256207B660B5}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {F66CEE6C-90B1-4BF8-80E0-5B2594819F59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {F66CEE6C-90B1-4BF8-80E0-5B2594819F59}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {F66CEE6C-90B1-4BF8-80E0-5B2594819F59}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {F66CEE6C-90B1-4BF8-80E0-5B2594819F59}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {8B4FA81E-8014-44D5-8FF1-09287630355C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {8B4FA81E-8014-44D5-8FF1-09287630355C}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {8B4FA81E-8014-44D5-8FF1-09287630355C}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {8B4FA81E-8014-44D5-8FF1-09287630355C}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {28EE4FC0-43BB-458A-B186-256207B660B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {28EE4FC0-43BB-458A-B186-256207B660B5}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {28EE4FC0-43BB-458A-B186-256207B660B5}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {28EE4FC0-43BB-458A-B186-256207B660B5}.Release|Any CPU.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {F66CEE6C-90B1-4BF8-80E0-5B2594819F59} = {15856E1C-AB65-4DAA-8C57-3478DE8C8420} 38 | {8B4FA81E-8014-44D5-8FF1-09287630355C} = {15856E1C-AB65-4DAA-8C57-3478DE8C8420} 39 | {28EE4FC0-43BB-458A-B186-256207B660B5} = {15856E1C-AB65-4DAA-8C57-3478DE8C8420} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Tests/AsyncDisposableTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Xunit; 6 | 7 | namespace LightInject.Microsoft.DependencyInjection.Tests 8 | { 9 | public class AsyncDisposableTests 10 | { 11 | [Fact] 12 | public async Task ShouldDisposeAsyncDisposable() 13 | { 14 | var serviceCollection = new ServiceCollection(); 15 | List disposedObjects = new(); 16 | serviceCollection.AddScoped(sp => new AsyncDisposable(disposedObject => disposedObjects.Add(disposedObject))); 17 | 18 | var serviceProvider = serviceCollection.CreateLightInjectServiceProvider(); 19 | 20 | AsyncDisposable asyncDisposable = null; 21 | await using (var scope = serviceProvider.CreateAsyncScope()) 22 | { 23 | asyncDisposable = scope.ServiceProvider.GetService(); 24 | } 25 | 26 | Assert.Contains(asyncDisposable, disposedObjects); 27 | } 28 | 29 | [Fact] 30 | public async Task ShouldDisposeAsyncDisposableFromRootScope() 31 | { 32 | var serviceCollection = new ServiceCollection(); 33 | List disposedObjects = new(); 34 | serviceCollection.AddSingleton(sp => new AsyncDisposable(disposedObject => disposedObjects.Add(disposedObject))); 35 | 36 | var serviceProvider = serviceCollection.CreateLightInjectServiceProvider(); 37 | 38 | AsyncDisposable asyncDisposable = null; 39 | 40 | asyncDisposable = serviceProvider.GetService(); 41 | await ((IAsyncDisposable)serviceProvider).DisposeAsync(); 42 | 43 | // Call it twice to ensure only disposed once. 44 | await ((IAsyncDisposable)serviceProvider).DisposeAsync(); 45 | 46 | 47 | Assert.Contains(asyncDisposable, disposedObjects); 48 | Assert.Single(disposedObjects); 49 | } 50 | } 51 | 52 | public class AsyncDisposable : IAsyncDisposable 53 | { 54 | private readonly Action onDisposed; 55 | 56 | public AsyncDisposable(Action onDisposed) 57 | { 58 | this.onDisposed = onDisposed; 59 | } 60 | public ValueTask DisposeAsync() 61 | { 62 | onDisposed(this); 63 | return ValueTask.CompletedTask; 64 | } 65 | } 66 | 67 | public class Disposable : IDisposable 68 | { 69 | private readonly Action onDisposed; 70 | 71 | public Disposable(Action onDisposed) 72 | { 73 | this.onDisposed = onDisposed; 74 | } 75 | 76 | public void Dispose() 77 | { 78 | onDisposed(this); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | Artifacts 19 | BenchmarkDotNet.Artifacts 20 | *.exe 21 | dotnet-script-tool/ 22 | 23 | # External tool builders 24 | .externalToolBuilders/ 25 | 26 | # Locally stored "Eclipse launch configurations" 27 | *.launch 28 | 29 | # CDT-specific 30 | .cproject 31 | 32 | # PDT-specific 33 | .buildpath 34 | 35 | 36 | ################# 37 | ## Visual Studio 38 | ################# 39 | 40 | ## Ignore Visual Studio temporary files, build results, and 41 | ## files generated by popular Visual Studio add-ons. 42 | 43 | # User-specific files 44 | *.suo 45 | *.user 46 | *.sln.docstates 47 | .vs/ 48 | 49 | # Build results 50 | [Dd]ebug/ 51 | [Rr]elease/ 52 | *_i.c 53 | *_p.c 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.vspscc 68 | .builds 69 | *.dotCover 70 | *.cs.pp 71 | *.nupkg 72 | project.lock.json 73 | LightInject.PreProcessor.dll 74 | lib/ 75 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 76 | packages/ 77 | scriptcs_packages/ 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opensdf 84 | *.sdf 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | 90 | # ReSharper is a .NET coding add-in 91 | _ReSharper* 92 | 93 | # Installshield output folder 94 | [Ee]xpress 95 | 96 | # DocProject is a documentation generator add-in 97 | DocProject/buildhelp/ 98 | DocProject/Help/*.HxT 99 | DocProject/Help/*.HxC 100 | DocProject/Help/*.hhc 101 | DocProject/Help/*.hhk 102 | DocProject/Help/*.hhp 103 | DocProject/Help/Html2 104 | DocProject/Help/html 105 | 106 | 107 | # Click-Once directory 108 | publish 109 | 110 | # Others 111 | [Bb]in 112 | [Oo]bj 113 | sql 114 | TestResults 115 | *.Cache 116 | ClientBin 117 | stylecop.* 118 | ~$* 119 | *.dbmdl 120 | Generated_Code #added for RIA/Silverlight projects 121 | *.ncrunch* 122 | 123 | # Backup & report files from converting an old project file to a newer 124 | # Visual Studio version. Backup files are not needed, because we have git ;-) 125 | _UpgradeReport_Files/ 126 | Backup*/ 127 | UpgradeLog*.XML 128 | 129 | 130 | ############ 131 | ## Windows 132 | ############ 133 | 134 | # Windows image file caches 135 | Thumbs.db 136 | 137 | # Folder config file 138 | Desktop.ini 139 | 140 | 141 | ############# 142 | ## Python 143 | ############# 144 | 145 | *.py[co] 146 | 147 | # Packages 148 | *.egg 149 | *.egg-info 150 | dist 151 | eggs 152 | parts 153 | bin 154 | var 155 | sdist 156 | develop-eggs 157 | .installed.cfg 158 | 159 | # Installer logs 160 | pip-log.txt 161 | 162 | # Unit test / coverage reports 163 | .coverage 164 | .tox 165 | *.info 166 | 167 | #Translations 168 | *.mo 169 | 170 | #Mr Developer 171 | .mr.developer.cfg 172 | 173 | 174 | /NuGet/Build/Net/LightInject.Annotation/LightInject.Annotation.cs 175 | /NuGet/Build/Net45/LightInject/LightInject.cs 176 | 177 | 178 | -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Benchmarks/ScopedService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace LightInject.Microsoft.DependencyInjection.Benchmarks 9 | { 10 | public interface IScopedService1 11 | { 12 | void DoSomething(); 13 | } 14 | 15 | public class ScopedService1 : IScopedService1 16 | { 17 | private static int counter; 18 | 19 | public ScopedService1() 20 | { 21 | Interlocked.Increment(ref counter); 22 | } 23 | 24 | public static int Instances 25 | { 26 | get { return counter; } 27 | set { counter = value; } 28 | } 29 | 30 | public void DoSomething() 31 | { 32 | } 33 | } 34 | 35 | public interface IScopedService2 36 | { 37 | void DoSomething(); 38 | } 39 | 40 | public class ScopedService2 : IScopedService2 41 | { 42 | private static int counter; 43 | 44 | public ScopedService2() 45 | { 46 | Interlocked.Increment(ref counter); 47 | } 48 | 49 | public static int Instances 50 | { 51 | get { return counter; } 52 | set { counter = value; } 53 | } 54 | 55 | public void DoSomething() 56 | { 57 | } 58 | } 59 | 60 | public interface IScopedService3 61 | { 62 | void DoSomething(); 63 | } 64 | 65 | public class ScopedService3 : IScopedService3 66 | { 67 | private static int counter; 68 | 69 | public ScopedService3() 70 | { 71 | Interlocked.Increment(ref counter); 72 | } 73 | 74 | public static int Instances 75 | { 76 | get { return counter; } 77 | set { counter = value; } 78 | } 79 | 80 | public void DoSomething() 81 | { 82 | } 83 | } 84 | 85 | public interface IScopedService4 86 | { 87 | void DoSomething(); 88 | } 89 | 90 | public class ScopedService4 : IScopedService4 91 | { 92 | private static int counter; 93 | 94 | public ScopedService4() 95 | { 96 | Interlocked.Increment(ref counter); 97 | } 98 | 99 | public static int Instances 100 | { 101 | get { return counter; } 102 | set { counter = value; } 103 | } 104 | 105 | public void DoSomething() 106 | { 107 | } 108 | } 109 | 110 | public interface IScopedService5 111 | { 112 | void DoSomething(); 113 | } 114 | 115 | public class ScopedService5 : IScopedService5 116 | { 117 | private static int counter; 118 | 119 | public ScopedService5() 120 | { 121 | Interlocked.Increment(ref counter); 122 | } 123 | 124 | public static int Instances 125 | { 126 | get { return counter; } 127 | set { counter = value; } 128 | } 129 | 130 | public void DoSomething() 131 | { 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Benchmarks/BeginScopeBenchmarks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BenchmarkDotNet.Attributes; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace LightInject.Microsoft.DependencyInjection.Benchmarks 6 | { 7 | public class BeginScopeBenchmarks 8 | { 9 | private IServiceProvider lightInjectServiceProvider; 10 | private IServiceProvider defaultServiceProvider; 11 | 12 | [GlobalSetup] 13 | public void Setup() 14 | { 15 | var serviceCollection = new ServiceCollection(); 16 | serviceCollection.AddTransient(); 17 | serviceCollection.AddTransient(); 18 | serviceCollection.AddTransient(); 19 | serviceCollection.AddTransient(); 20 | serviceCollection.AddTransient(); 21 | serviceCollection.AddTransient(); 22 | serviceCollection.AddTransient(); 23 | serviceCollection.AddTransient(); 24 | serviceCollection.AddScoped(); 25 | serviceCollection.AddScoped(); 26 | serviceCollection.AddScoped(); 27 | serviceCollection.AddScoped(); 28 | serviceCollection.AddScoped(); 29 | serviceCollection.AddSingleton(); 30 | serviceCollection.AddSingleton(); 31 | serviceCollection.AddSingleton(); 32 | lightInjectServiceProvider = serviceCollection.CreateLightInjectServiceProvider(new ContainerOptions() { EnableCurrentScope = false }); 33 | defaultServiceProvider = serviceCollection.BuildServiceProvider(); 34 | } 35 | 36 | [Benchmark] 37 | public void UsingLightInject() 38 | { 39 | MethodToBenchmark(lightInjectServiceProvider); 40 | } 41 | 42 | [Benchmark] 43 | public void UsingMicrosoft() 44 | { 45 | MethodToBenchmark(defaultServiceProvider); 46 | } 47 | 48 | private void MethodToBenchmark(IServiceProvider serviceProvider) 49 | { 50 | var factory = (IServiceScopeFactory)serviceProvider.GetService(typeof(IServiceScopeFactory)); 51 | 52 | using (var scope = factory.CreateScope()) 53 | { 54 | var controller = scope.ServiceProvider.GetService(typeof(TestController1)); 55 | } 56 | 57 | factory = (IServiceScopeFactory)serviceProvider.GetService(typeof(IServiceScopeFactory)); 58 | 59 | using (var scope = factory.CreateScope()) 60 | { 61 | var controller = scope.ServiceProvider.GetService(typeof(TestController2)); 62 | } 63 | 64 | factory = (IServiceScopeFactory)serviceProvider.GetService(typeof(IServiceScopeFactory)); 65 | 66 | using (var scope = factory.CreateScope()) 67 | { 68 | var controller = scope.ServiceProvider.GetService(typeof(TestController3)); 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Tests/DefaultServiceTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Xunit; 4 | 5 | namespace LightInject.Microsoft.DependencyInjection.Tests; 6 | 7 | public class DefaultServiceTests 8 | { 9 | [Fact] 10 | public void ShouldOverrideDefaultRegistrationInServiceContainer() 11 | { 12 | var container = new ServiceContainer(options => options.WithMicrosoftSettings()); 13 | container.RegisterTransient(); 14 | var serviceCollection = new ServiceCollection(); 15 | 16 | serviceCollection.AddSingleton(); 17 | 18 | var provider = container.CreateServiceProvider(serviceCollection); 19 | 20 | var foo = provider.GetRequiredService(); 21 | 22 | Assert.IsType(foo); 23 | } 24 | 25 | [Fact] 26 | public void ShouldOverrideNamedRegistrationInServiceContainer() 27 | { 28 | var container = new ServiceContainer(options => options.WithMicrosoftSettings()); 29 | container.RegisterTransient("Foo"); 30 | var serviceCollection = new ServiceCollection(); 31 | 32 | serviceCollection.AddSingleton(); 33 | 34 | var provider = container.CreateServiceProvider(serviceCollection); 35 | 36 | var foo = provider.GetRequiredService(); 37 | 38 | Assert.IsType(foo); 39 | } 40 | 41 | [Fact] 42 | public void ShouldOverrideMultipleNamedRegistrationInServiceContainer() 43 | { 44 | var container = new ServiceContainer(options => options.WithMicrosoftSettings()); 45 | container.RegisterTransient("Foo1"); 46 | container.RegisterTransient("Foo2"); 47 | var serviceCollection = new ServiceCollection(); 48 | 49 | serviceCollection.AddSingleton(); 50 | 51 | var provider = container.CreateServiceProvider(serviceCollection); 52 | 53 | var foo = provider.GetRequiredService(); 54 | 55 | Assert.IsType(foo); 56 | } 57 | 58 | [Fact] 59 | public void ShouldOverrideNamedAndDefaultRegistrationInServiceContainer() 60 | { 61 | var container = new ServiceContainer(options => options.WithMicrosoftSettings()); 62 | container.RegisterTransient(); 63 | container.RegisterTransient("Foo"); 64 | var serviceCollection = new ServiceCollection(); 65 | 66 | serviceCollection.AddSingleton(); 67 | 68 | var provider = container.CreateServiceProvider(serviceCollection); 69 | 70 | var foo = provider.GetRequiredService(); 71 | 72 | Assert.IsType(foo); 73 | } 74 | 75 | 76 | [Fact] 77 | public void ShouldUseLast() 78 | { 79 | var container = new ServiceContainer(options => options.WithMicrosoftSettings()); 80 | container.RegisterTransient("Foo1"); 81 | container.RegisterTransient("Foo2"); 82 | var serviceCollection = new ServiceCollection(); 83 | 84 | serviceCollection.AddSingleton(); 85 | 86 | var provider = container.CreateServiceProvider(serviceCollection); 87 | 88 | var foo = provider.GetRequiredService(); 89 | 90 | Assert.IsType(foo); 91 | } 92 | 93 | public interface IFoo { } 94 | 95 | public class Foo : IFoo { } 96 | 97 | public class AnotherFoo : IFoo { } 98 | } -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Tests/ServiceProviderFactoryTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Xunit; 7 | 8 | namespace LightInject.Microsoft.DependencyInjection.Tests 9 | { 10 | public class ServiceProviderFactoryTests 11 | { 12 | [Fact] 13 | public void ShouldCreateBuilder() 14 | { 15 | var serviceCollection = new ServiceCollection(); 16 | var factory = new LightInjectServiceProviderFactory(); 17 | var container = factory.CreateBuilder(serviceCollection); 18 | 19 | } 20 | 21 | [Fact] 22 | public void ShouldCreateServiceProvider() 23 | { 24 | var serviceCollection = new ServiceCollection(); 25 | var factory = new LightInjectServiceProviderFactory(); 26 | var container = factory.CreateBuilder(serviceCollection); 27 | var provider = factory.CreateServiceProvider(container); 28 | 29 | Assert.IsAssignableFrom(container); 30 | } 31 | 32 | [Fact] 33 | public void ShouldUseExistingContainer() 34 | { 35 | var serviceCollection = new ServiceCollection(); 36 | var container = new ServiceContainer(ContainerOptions.Default.WithMicrosoftSettings()); 37 | var factory = new LightInjectServiceProviderFactory(container); 38 | var builder = factory.CreateBuilder(serviceCollection); 39 | Assert.Same(container, builder); 40 | } 41 | 42 | [Fact] 43 | public void ShouldDisposePerContainerServicesWhenProviderIsDisposed() 44 | { 45 | var serviceCollection = new ServiceCollection(); 46 | var factory = new LightInjectServiceProviderFactory(); 47 | var container = factory.CreateBuilder(serviceCollection); 48 | container.Register(new PerContainerLifetime()); 49 | DisposableFoo foo; 50 | using (var provider = (IDisposable)container.CreateServiceProvider(serviceCollection)) 51 | { 52 | foo = ((IServiceProvider)provider).GetService(); 53 | } 54 | 55 | Assert.True(foo.IsDisposed); 56 | } 57 | 58 | [Fact] 59 | public void ShouldDisposeRootScopeWhenProviderIsDisposed() 60 | { 61 | var serviceCollection = new ServiceCollection(); 62 | serviceCollection.AddSingleton(); 63 | var factory = new LightInjectServiceProviderFactory(); 64 | var container = factory.CreateBuilder(serviceCollection); 65 | var provider = factory.CreateServiceProvider(container); 66 | 67 | var foo = provider.GetService(); 68 | ((IDisposable)provider).Dispose(); 69 | 70 | Assert.True(foo.IsDisposed); 71 | } 72 | 73 | [Fact] 74 | public void ShouldDisposeRootScopeWhenProviderRequestedFromContainerIsDisposed() 75 | { 76 | var serviceCollection = new ServiceCollection(); 77 | serviceCollection.AddSingleton(); 78 | 79 | var factory = new LightInjectServiceProviderFactory(new ServiceContainer(ContainerOptions.Default.WithMicrosoftSettings())); 80 | var container = factory.CreateBuilder(serviceCollection); 81 | var provider = factory.CreateServiceProvider(container); 82 | var requestedProvider = provider.GetService(); 83 | 84 | var foo = provider.GetService(); 85 | ((IDisposable)requestedProvider).Dispose(); 86 | 87 | Assert.True(foo.IsDisposed); 88 | } 89 | 90 | [Fact] 91 | public void ShouldCallConfigureAction() 92 | { 93 | bool wasCalled = false; 94 | var factory = new LightInjectServiceProviderFactory(o => wasCalled = true); 95 | Assert.True(wasCalled); 96 | var container = factory.CreateBuilder(new ServiceCollection()); 97 | Assert.IsType(container); 98 | } 99 | 100 | 101 | public class DisposableFoo : IDisposable 102 | { 103 | public bool IsDisposed; 104 | 105 | public void Dispose() 106 | { 107 | IsDisposed = true; 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Benchmarks/TestController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace LightInject.Microsoft.DependencyInjection.Benchmarks 5 | { 6 | public class TestController1 : IDisposable 7 | { 8 | private static int counter; 9 | 10 | private static int disposeCount; 11 | 12 | public TestController1( 13 | IRepositoryTransient1 transient1, 14 | IRepositoryTransient2 repositoryTransient2, 15 | IRepositoryTransient3 repositoryTransient3, 16 | IRepositoryTransient4 repositoryTransient4, 17 | IRepositoryTransient5 repositoryTransient5) 18 | { 19 | if (transient1 == null) 20 | { 21 | throw new ArgumentNullException(nameof(transient1)); 22 | } 23 | 24 | if (repositoryTransient2 == null) 25 | { 26 | throw new ArgumentNullException(nameof(repositoryTransient2)); 27 | } 28 | 29 | if (repositoryTransient3 == null) 30 | { 31 | throw new ArgumentNullException(nameof(repositoryTransient3)); 32 | } 33 | 34 | if (repositoryTransient4 == null) 35 | { 36 | throw new ArgumentNullException(nameof(repositoryTransient4)); 37 | } 38 | 39 | if (repositoryTransient5 == null) 40 | { 41 | throw new ArgumentNullException(nameof(repositoryTransient5)); 42 | } 43 | 44 | Interlocked.Increment(ref counter); 45 | } 46 | 47 | public static int Instances 48 | { 49 | get { return counter; } 50 | set { counter = value; } 51 | } 52 | 53 | public static int DisposeCount 54 | { 55 | get { return disposeCount; } 56 | set { disposeCount = value; } 57 | } 58 | 59 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 60 | public void Dispose() 61 | { 62 | Interlocked.Increment(ref disposeCount); 63 | } 64 | } 65 | 66 | public class TestController2 : IDisposable 67 | { 68 | private static int counter; 69 | private static int disposeCount; 70 | 71 | public TestController2( 72 | IRepositoryTransient1 transient1, 73 | IRepositoryTransient2 repositoryTransient2, 74 | IRepositoryTransient3 repositoryTransient3, 75 | IRepositoryTransient4 repositoryTransient4, 76 | IRepositoryTransient5 repositoryTransient5) 77 | { 78 | if (transient1 == null) 79 | { 80 | throw new ArgumentNullException(nameof(transient1)); 81 | } 82 | 83 | if (repositoryTransient2 == null) 84 | { 85 | throw new ArgumentNullException(nameof(repositoryTransient2)); 86 | } 87 | 88 | if (repositoryTransient3 == null) 89 | { 90 | throw new ArgumentNullException(nameof(repositoryTransient3)); 91 | } 92 | 93 | if (repositoryTransient4 == null) 94 | { 95 | throw new ArgumentNullException(nameof(repositoryTransient4)); 96 | } 97 | 98 | if (repositoryTransient5 == null) 99 | { 100 | throw new ArgumentNullException(nameof(repositoryTransient5)); 101 | } 102 | 103 | Interlocked.Increment(ref counter); 104 | } 105 | 106 | public static int Instances 107 | { 108 | get { return counter; } 109 | set { counter = value; } 110 | } 111 | 112 | public static int DisposeCount 113 | { 114 | get { return disposeCount; } 115 | set { disposeCount = value; } 116 | } 117 | 118 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 119 | public void Dispose() 120 | { 121 | Interlocked.Increment(ref disposeCount); 122 | } 123 | } 124 | 125 | public class TestController3 : IDisposable 126 | { 127 | private static int counter; 128 | private static int disposeCount; 129 | 130 | public TestController3( 131 | IRepositoryTransient1 transient1, 132 | IRepositoryTransient2 repositoryTransient2, 133 | IRepositoryTransient3 repositoryTransient3, 134 | IRepositoryTransient4 repositoryTransient4, 135 | IRepositoryTransient5 repositoryTransient5) 136 | { 137 | if (transient1 == null) 138 | { 139 | throw new ArgumentNullException(nameof(transient1)); 140 | } 141 | 142 | if (repositoryTransient2 == null) 143 | { 144 | throw new ArgumentNullException(nameof(repositoryTransient2)); 145 | } 146 | 147 | if (repositoryTransient3 == null) 148 | { 149 | throw new ArgumentNullException(nameof(repositoryTransient3)); 150 | } 151 | 152 | if (repositoryTransient4 == null) 153 | { 154 | throw new ArgumentNullException(nameof(repositoryTransient4)); 155 | } 156 | 157 | if (repositoryTransient5 == null) 158 | { 159 | throw new ArgumentNullException(nameof(repositoryTransient5)); 160 | } 161 | 162 | Interlocked.Increment(ref counter); 163 | } 164 | 165 | public static int Instances 166 | { 167 | get { return counter; } 168 | set { counter = value; } 169 | } 170 | 171 | public static int DisposeCount 172 | { 173 | get { return disposeCount; } 174 | set { disposeCount = value; } 175 | } 176 | 177 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 178 | public void Dispose() 179 | { 180 | Interlocked.Increment(ref disposeCount); 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /.idea/.idea.LightInject.Microsoft.DependencyInjection/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | src/LightInject.Microsoft.DependencyInjection.Benchmarks/LightInject.Microsoft.DependencyInjection.Benchmarks.csproj 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 31 | 32 | 33 | 36 | 56 | 57 | 58 | 75 | 76 | 77 | 78 | 79 | 80 | 1729181599256 81 | 86 | 87 | 88 | 89 | 91 | 92 | 94 | 95 | 96 | 97 | 98 | file://$PROJECT_DIR$/../LightInject/src/LightInject/LightInject.cs 99 | 4250 100 | 101 | 102 | 104 | 105 | 107 | 108 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Benchmarks/RepositoryTransients.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace LightInject.Microsoft.DependencyInjection.Benchmarks 9 | { 10 | public interface IRepositoryTransient1 11 | { 12 | void DoSomething(); 13 | } 14 | 15 | public interface IRepositoryTransient2 16 | { 17 | void DoSomething(); 18 | } 19 | 20 | public interface IRepositoryTransient3 21 | { 22 | void DoSomething(); 23 | } 24 | 25 | public interface IRepositoryTransient4 26 | { 27 | void DoSomething(); 28 | } 29 | 30 | public interface IRepositoryTransient5 31 | { 32 | void DoSomething(); 33 | } 34 | 35 | public class RepositoryTransient1 : IRepositoryTransient1 36 | { 37 | private static int counter; 38 | 39 | public RepositoryTransient1(ISingleton1 singleton, IScopedService1 scopedService1, IScopedService2 scopedService2, IScopedService3 scopedService3, IScopedService4 scopedService4, IScopedService5 scopedService5) 40 | { 41 | if (singleton == null) 42 | { 43 | throw new ArgumentNullException(nameof(singleton)); 44 | } 45 | 46 | if (scopedService1 == null) 47 | { 48 | throw new ArgumentNullException(nameof(scopedService1)); 49 | } 50 | 51 | if (scopedService2 == null) 52 | { 53 | throw new ArgumentNullException(nameof(scopedService2)); 54 | } 55 | 56 | if (scopedService3 == null) 57 | { 58 | throw new ArgumentNullException(nameof(scopedService3)); 59 | } 60 | 61 | if (scopedService4 == null) 62 | { 63 | throw new ArgumentNullException(nameof(scopedService4)); 64 | } 65 | 66 | if (scopedService5 == null) 67 | { 68 | throw new ArgumentNullException(nameof(scopedService5)); 69 | } 70 | 71 | Interlocked.Increment(ref counter); 72 | } 73 | 74 | public static int Instances 75 | { 76 | get { return counter; } 77 | set { counter = value; } 78 | } 79 | 80 | public void DoSomething() 81 | { 82 | } 83 | } 84 | 85 | public class RepositoryTransient2 : IRepositoryTransient2 86 | { 87 | private static int counter; 88 | 89 | public RepositoryTransient2(ISingleton1 singleton, IScopedService1 scopedService1, IScopedService2 scopedService2, IScopedService3 scopedService3, IScopedService4 scopedService4, IScopedService5 scopedService5) 90 | { 91 | if (singleton == null) 92 | { 93 | throw new ArgumentNullException(nameof(singleton)); 94 | } 95 | 96 | if (scopedService1 == null) 97 | { 98 | throw new ArgumentNullException(nameof(scopedService1)); 99 | } 100 | 101 | if (scopedService2 == null) 102 | { 103 | throw new ArgumentNullException(nameof(scopedService2)); 104 | } 105 | 106 | if (scopedService3 == null) 107 | { 108 | throw new ArgumentNullException(nameof(scopedService3)); 109 | } 110 | 111 | if (scopedService4 == null) 112 | { 113 | throw new ArgumentNullException(nameof(scopedService4)); 114 | } 115 | 116 | if (scopedService5 == null) 117 | { 118 | throw new ArgumentNullException(nameof(scopedService5)); 119 | } 120 | 121 | Interlocked.Increment(ref counter); 122 | } 123 | 124 | public static int Instances 125 | { 126 | get { return counter; } 127 | set { counter = value; } 128 | } 129 | 130 | public void DoSomething() 131 | { 132 | } 133 | } 134 | 135 | public class RepositoryTransient3 : IRepositoryTransient3 136 | { 137 | private static int counter; 138 | 139 | public RepositoryTransient3(ISingleton1 singleton, IScopedService1 scopedService1, IScopedService2 scopedService2, IScopedService3 scopedService3, IScopedService4 scopedService4, IScopedService5 scopedService5) 140 | { 141 | if (singleton == null) 142 | { 143 | throw new ArgumentNullException(nameof(singleton)); 144 | } 145 | 146 | if (scopedService1 == null) 147 | { 148 | throw new ArgumentNullException(nameof(scopedService1)); 149 | } 150 | 151 | if (scopedService2 == null) 152 | { 153 | throw new ArgumentNullException(nameof(scopedService2)); 154 | } 155 | 156 | if (scopedService3 == null) 157 | { 158 | throw new ArgumentNullException(nameof(scopedService3)); 159 | } 160 | 161 | if (scopedService4 == null) 162 | { 163 | throw new ArgumentNullException(nameof(scopedService4)); 164 | } 165 | 166 | if (scopedService5 == null) 167 | { 168 | throw new ArgumentNullException(nameof(scopedService5)); 169 | } 170 | 171 | Interlocked.Increment(ref counter); 172 | } 173 | 174 | public static int Instances 175 | { 176 | get { return counter; } 177 | set { counter = value; } 178 | } 179 | 180 | public void DoSomething() 181 | { 182 | } 183 | } 184 | 185 | public class RepositoryTransient4 : IRepositoryTransient4 186 | { 187 | private static int counter; 188 | 189 | public RepositoryTransient4(ISingleton1 singleton, IScopedService1 scopedService1, IScopedService2 scopedService2, IScopedService3 scopedService3, IScopedService4 scopedService4, IScopedService5 scopedService5) 190 | { 191 | if (singleton == null) 192 | { 193 | throw new ArgumentNullException(nameof(singleton)); 194 | } 195 | 196 | if (scopedService1 == null) 197 | { 198 | throw new ArgumentNullException(nameof(scopedService1)); 199 | } 200 | 201 | if (scopedService2 == null) 202 | { 203 | throw new ArgumentNullException(nameof(scopedService2)); 204 | } 205 | 206 | if (scopedService3 == null) 207 | { 208 | throw new ArgumentNullException(nameof(scopedService3)); 209 | } 210 | 211 | if (scopedService4 == null) 212 | { 213 | throw new ArgumentNullException(nameof(scopedService4)); 214 | } 215 | 216 | if (scopedService5 == null) 217 | { 218 | throw new ArgumentNullException(nameof(scopedService5)); 219 | } 220 | 221 | Interlocked.Increment(ref counter); 222 | } 223 | 224 | public static int Instances 225 | { 226 | get { return counter; } 227 | set { counter = value; } 228 | } 229 | 230 | public void DoSomething() 231 | { 232 | } 233 | } 234 | 235 | public class RepositoryTransient5 : IRepositoryTransient5 236 | { 237 | private static int counter; 238 | 239 | public RepositoryTransient5(ISingleton1 singleton, IScopedService1 scopedService1, IScopedService2 scopedService2, IScopedService3 scopedService3, IScopedService4 scopedService4, IScopedService5 scopedService5) 240 | { 241 | if (singleton == null) 242 | { 243 | throw new ArgumentNullException(nameof(singleton)); 244 | } 245 | 246 | if (scopedService1 == null) 247 | { 248 | throw new ArgumentNullException(nameof(scopedService1)); 249 | } 250 | 251 | if (scopedService2 == null) 252 | { 253 | throw new ArgumentNullException(nameof(scopedService2)); 254 | } 255 | 256 | if (scopedService3 == null) 257 | { 258 | throw new ArgumentNullException(nameof(scopedService3)); 259 | } 260 | 261 | if (scopedService4 == null) 262 | { 263 | throw new ArgumentNullException(nameof(scopedService4)); 264 | } 265 | 266 | if (scopedService5 == null) 267 | { 268 | throw new ArgumentNullException(nameof(scopedService5)); 269 | } 270 | 271 | Interlocked.Increment(ref counter); 272 | } 273 | 274 | public static int Instances 275 | { 276 | get { return counter; } 277 | set { counter = value; } 278 | } 279 | 280 | public void DoSomething() 281 | { 282 | } 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Tests/LightInjectKeyedSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Text; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.DependencyInjection.Specification; 6 | using Xunit; 7 | using Xunit.Sdk; 8 | 9 | namespace LightInject.Microsoft.DependencyInjection.Tests; 10 | 11 | public class LightInjectKeyedSpecificationTests : KeyedDependencyInjectionSpecificationTests 12 | { 13 | protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection) 14 | { 15 | return serviceCollection.CreateLightInjectServiceProvider(new ContainerOptions() { EnableCurrentScope = false }); 16 | } 17 | 18 | [Fact] 19 | public void ShouldHandleKeyedServiceWithEnumKey() 20 | { 21 | var serviceCollection = new ServiceCollection(); 22 | serviceCollection.AddKeyedTransient(Key.A); 23 | var provider = CreateServiceProvider(serviceCollection); 24 | provider.GetKeyedService(Key.A); 25 | } 26 | 27 | [Fact] 28 | public void ShouldHandleKeyedServiceWithStringServiceKey() 29 | { 30 | var serviceCollection = new ServiceCollection(); 31 | serviceCollection.AddKeyedTransient("A"); 32 | var provider = CreateServiceProvider(serviceCollection); 33 | var instance = provider.GetKeyedService("A"); 34 | Assert.Equal("A", ((KeyedServiceWithStringServiceKey)instance).ServiceKey); 35 | } 36 | 37 | [Fact] 38 | public void ShouldHandleRequiredKeyedServiceWithStringServiceKeyUsingFactory() 39 | { 40 | var serviceCollection = new ServiceCollection(); 41 | serviceCollection.AddKeyedTransient(Key.A, (sp, key) => new KeyedServiceWithStringServiceKey((string)key)); 42 | var provider = CreateServiceProvider(serviceCollection); 43 | var instance = provider.GetRequiredKeyedService("A"); 44 | Assert.Equal("A", ((KeyedServiceWithStringServiceKey)instance).ServiceKey); 45 | } 46 | 47 | [Fact] 48 | public void ShouldHandleKeyedServiceWithEnumServiceKey() 49 | { 50 | var serviceCollection = new ServiceCollection(); 51 | serviceCollection.AddKeyedTransient(Key.A); 52 | var provider = CreateServiceProvider(serviceCollection); 53 | var instance = provider.GetKeyedService(Key.A); 54 | Assert.Equal(Key.A, ((KeyedServiceWithEnumServiceKey)instance).ServiceKey); 55 | } 56 | 57 | [Fact] 58 | public void ShouldHandleKeyedServiceWithIntServiceKey() 59 | { 60 | var serviceCollection = new ServiceCollection(); 61 | serviceCollection.AddKeyedTransient(1); 62 | var provider = CreateServiceProvider(serviceCollection); 63 | var instance = provider.GetKeyedService(1); 64 | Assert.Equal(1, ((KeyedServiceWithIntKey)instance).ServiceKey); 65 | } 66 | 67 | [Fact] 68 | public void ShouldHandlePassingServiceKeyAsInteger() 69 | { 70 | var serviceCollection = new ServiceCollection(); 71 | serviceCollection.AddKeyedTransient(1, (sp, key) => new KeyedServiceWithIntKey((int)key)); 72 | var provider = CreateServiceProvider(serviceCollection); 73 | var instance = provider.GetKeyedService(1); 74 | Assert.Equal(1, ((KeyedServiceWithIntKey)instance).ServiceKey); 75 | } 76 | 77 | [Fact] 78 | public void ShouldHandlePassingServiceKeyAsEnum() 79 | { 80 | var serviceCollection = new ServiceCollection(); 81 | serviceCollection.AddKeyedTransient(Key.A, (sp, key) => new KeyedServiceWithEnumServiceKey((Key)key)); 82 | var provider = CreateServiceProvider(serviceCollection); 83 | var instance = provider.GetKeyedService(Key.A); 84 | Assert.Equal(Key.A, ((KeyedServiceWithEnumServiceKey)instance).ServiceKey); 85 | } 86 | 87 | [Fact] 88 | public void ShouldThrowExceptionWhenUsingInvalidKeyType() 89 | { 90 | var serviceCollection = new ServiceCollection(); 91 | serviceCollection.AddKeyedTransient(new StringBuilder(), (sp, key) => new KeyedServiceWithInvalidServiceKeyType()); 92 | var provider = CreateServiceProvider(serviceCollection); 93 | Assert.Throws(() => provider.GetKeyedService(new StringBuilder())); 94 | } 95 | 96 | [Fact] 97 | public void ShouldHandleConvertibleType() 98 | { 99 | var serviceCollection = new ServiceCollection(); 100 | serviceCollection.AddKeyedTransient(1L, (sp, key) => new KeyedServiceWithLongServiceKey((long)key)); 101 | var provider = CreateServiceProvider(serviceCollection); 102 | var instance = provider.GetKeyedService(1L); 103 | Assert.Equal(1L, ((KeyedServiceWithLongServiceKey)instance).ServiceKey); 104 | } 105 | 106 | [Fact] 107 | public void ShouldHandlePassingNullAsServiceKeyForRequiredService() 108 | { 109 | var serviceCollection = new ServiceCollection(); 110 | serviceCollection.AddKeyedTransient(Key.A); 111 | var provider = CreateServiceProvider(serviceCollection); 112 | Assert.Throws(() => provider.GetRequiredKeyedService(null)); 113 | } 114 | 115 | [Fact] 116 | public void ShouldInjectKeyedService() 117 | { 118 | var serviceCollection = new ServiceCollection(); 119 | serviceCollection.AddKeyedScoped("KeyedService", (sp, key) => new KeyedService()); 120 | serviceCollection.AddKeyedScoped("AnotherKeyedService", (sp, key) => new AnotherKeyedService()); 121 | serviceCollection.AddScoped(); 122 | var provider = CreateServiceProvider(serviceCollection); 123 | using var scope = provider.CreateScope(); 124 | var instance = scope.ServiceProvider.GetRequiredService(); 125 | Assert.IsType(instance.Service); 126 | } 127 | 128 | [Fact] 129 | public void ShouldInjectServiceFromDerivedServiceKey() 130 | { 131 | var serviceCollection = new ServiceCollection(); 132 | serviceCollection.AddKeyedTransient("KeyedService"); 133 | serviceCollection.AddKeyedTransient("AnotherKeyedService"); 134 | serviceCollection.AddTransient(); 135 | var provider = CreateServiceProvider(serviceCollection); 136 | var instance = provider.GetRequiredService(); 137 | Assert.IsType(instance.Service); 138 | } 139 | } 140 | 141 | 142 | public interface IKeyedServiceWithInvalidServiceKeyType 143 | { 144 | } 145 | 146 | public class KeyedServiceWithInvalidServiceKeyType() : IKeyedServiceWithInvalidServiceKeyType 147 | { 148 | 149 | } 150 | 151 | 152 | public interface IKeyedService 153 | { 154 | } 155 | 156 | public class KeyedService : IKeyedService 157 | { 158 | 159 | } 160 | 161 | public interface IKeyedServiceWithStringServiceKey 162 | { 163 | string ServiceKey { get; } 164 | } 165 | 166 | public class KeyedServiceWithStringServiceKey([ServiceKey] string serviceKey) : IKeyedServiceWithStringServiceKey 167 | { 168 | public string ServiceKey { get; } = serviceKey; 169 | } 170 | 171 | public interface IKeyedServiceWithEnumServiceKey 172 | { 173 | Key ServiceKey { get; } 174 | } 175 | 176 | public class KeyedServiceWithEnumServiceKey([ServiceKey] Key serviceKey) : IKeyedServiceWithEnumServiceKey 177 | { 178 | public Key ServiceKey { get; } = serviceKey; 179 | } 180 | 181 | public interface IKeyedServiceWithIntKey 182 | { 183 | int ServiceKey { get; } 184 | } 185 | 186 | public class KeyedServiceWithIntKey([ServiceKey] int serviceKey) : IKeyedServiceWithIntKey 187 | { 188 | public int ServiceKey { get; } = serviceKey; 189 | } 190 | 191 | public interface IKeyedServiceWithLongServiceKey 192 | { 193 | long ServiceKey { get; } 194 | } 195 | 196 | public class KeyedServiceWithLongServiceKey([ServiceKey] long serviceKey) : IKeyedServiceWithLongServiceKey 197 | { 198 | public long ServiceKey { get; } = serviceKey; 199 | } 200 | 201 | public enum Key 202 | { 203 | A, 204 | B 205 | } 206 | 207 | public class AnotherKeyedService : IKeyedService 208 | { 209 | } 210 | 211 | public interface IServiceWithKeyedService 212 | { 213 | IKeyedService Service { get; } 214 | } 215 | 216 | public class ServiceWithKeyedService([FromKeyedServices("AnotherKeyedService")] IKeyedService service) : IServiceWithKeyedService 217 | { 218 | public IKeyedService Service { get; } = service; 219 | } 220 | 221 | 222 | public class DerivedServiceKey : FromKeyedServicesAttribute 223 | { 224 | public DerivedServiceKey() : base("AnotherKeyedService") 225 | { 226 | } 227 | } 228 | 229 | 230 | public class ServiceWithDerivedServiceKey([DerivedServiceKey] IKeyedService service) 231 | { 232 | public IKeyedService Service { get; } = service; 233 | } -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection.Tests/ServiceContainerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Xunit; 5 | 6 | namespace LightInject.Microsoft.DependencyInjection.Tests 7 | { 8 | public class ServiceContainerTests 9 | { 10 | [Fact] 11 | public void ShouldThrowExceptionWhenProviderIsCreatedTwice() 12 | { 13 | var container = new ServiceContainer(); 14 | var serviceCollection = new ServiceCollection(); 15 | 16 | var provider = container.CreateServiceProvider(serviceCollection); 17 | 18 | Assert.Throws(() => container.CreateServiceProvider(serviceCollection)); 19 | } 20 | 21 | [Fact] 22 | public void ShouldThrowExceptionWhenProviderIsCreatedTwiceAndCurrentScopeIsDisabled() 23 | { 24 | var container = new ServiceContainer(c => c.EnableCurrentScope = false); 25 | var serviceCollection = new ServiceCollection(); 26 | 27 | var provider = container.CreateServiceProvider(serviceCollection); 28 | 29 | Assert.Throws(() => container.CreateServiceProvider(serviceCollection)); 30 | } 31 | 32 | [Fact] 33 | public void ShouldCallConfigureActionWhenCreatingServiceProvider() 34 | { 35 | bool wasCalled = false; 36 | 37 | var serviceCollection = new ServiceCollection(); 38 | serviceCollection.CreateLightInjectServiceProvider(o => wasCalled = true); 39 | 40 | Assert.True(wasCalled); 41 | } 42 | 43 | [Fact] 44 | public void ShouldNotUseRootScopeForFactoryDelegate() 45 | { 46 | var serviceCollection = new ServiceCollection(); 47 | serviceCollection.AddScoped(); 48 | 49 | serviceCollection.AddScoped(sp => 50 | { 51 | return sp.GetService(); 52 | }); 53 | 54 | var provider = serviceCollection.CreateLightInjectServiceProvider(options => options.EnableCurrentScope = false); 55 | 56 | Foo firstFoo; 57 | Foo secondFoo; 58 | 59 | using (var serviceScope = provider.CreateScope()) 60 | { 61 | firstFoo = serviceScope.ServiceProvider.GetService(); 62 | } 63 | 64 | using (var serviceScope = provider.CreateScope()) 65 | { 66 | secondFoo = serviceScope.ServiceProvider.GetService(); 67 | } 68 | 69 | Assert.NotSame(firstFoo, secondFoo); 70 | } 71 | 72 | 73 | [Fact] 74 | public void SingletonServiceCanBeResolvedFromScope() 75 | { 76 | // Arrange 77 | var collection = new ServiceCollection(); 78 | collection.AddSingleton(); 79 | var provider = collection.CreateLightInjectServiceProvider(); 80 | 81 | // Act 82 | IServiceProvider scopedSp1 = null; 83 | IServiceProvider scopedSp2 = null; 84 | ClassWithServiceProvider instance1 = null; 85 | ClassWithServiceProvider instance2 = null; 86 | 87 | using (var scope1 = provider.CreateScope()) 88 | { 89 | scopedSp1 = scope1.ServiceProvider; 90 | instance1 = scope1.ServiceProvider.GetRequiredService(); 91 | } 92 | 93 | using (var scope2 = provider.CreateScope()) 94 | { 95 | scopedSp2 = scope2.ServiceProvider; 96 | instance2 = scope2.ServiceProvider.GetRequiredService(); 97 | } 98 | 99 | // Assert 100 | Assert.Same(instance1.ServiceProvider, instance2.ServiceProvider); 101 | Assert.NotSame(instance1.ServiceProvider, scopedSp1); 102 | Assert.NotSame(instance2.ServiceProvider, scopedSp2); 103 | } 104 | 105 | 106 | [Fact] 107 | public void ShouldHandleDefaultArgument() 108 | { 109 | var collection = new ServiceCollection(); 110 | collection.AddTransient(); 111 | var provider = collection.CreateLightInjectServiceProvider(); 112 | 113 | var instance = provider.GetService(); 114 | Assert.Equal("42", instance.Value); 115 | } 116 | 117 | [Fact] 118 | public void ShouldHandleCorrectScopeWhenInjectedAlongWithHttpClient() 119 | { 120 | var collection = new ServiceCollection(); 121 | collection.AddScoped(sp => new Scoped()); 122 | collection.AddHttpClient(); 123 | var provider = collection.CreateLightInjectServiceProvider(); 124 | IScoped firstScoped; 125 | IScoped secondScoped; 126 | using (var scope1 = provider.CreateScope()) 127 | { 128 | var instance = scope1.ServiceProvider.GetService(); 129 | firstScoped = instance.Scoped; 130 | } 131 | 132 | using (var scope2 = provider.CreateScope()) 133 | { 134 | var instance = scope2.ServiceProvider.GetService(); 135 | secondScoped = instance.Scoped; 136 | } 137 | 138 | Assert.NotSame(firstScoped, secondScoped); 139 | } 140 | 141 | [Fact] 142 | public void ShouldNotDisposeScopedServiceWhenInjectedIntoSingletonUsingDefaultProvider() 143 | { 144 | var collection = new ServiceCollection(); 145 | collection.AddSingleton(); 146 | collection.AddScoped(); 147 | 148 | var provider = collection.BuildServiceProvider(); 149 | 150 | SingletonWithScopedDisposable singletonDisposable; 151 | using (var scope = provider.CreateScope()) 152 | { 153 | singletonDisposable = scope.ServiceProvider.GetService(); 154 | } 155 | 156 | Assert.False(singletonDisposable.Disposable.IsDisposed); 157 | } 158 | 159 | [Fact] 160 | public void ShouldNotDisposeScopedServiceWhenInjectedIntoSingletonUsingLightInjectProvider() 161 | { 162 | var collection = new ServiceCollection(); 163 | collection.AddSingleton(); 164 | collection.AddScoped(); 165 | 166 | var provider = collection.CreateLightInjectServiceProvider(); 167 | 168 | SingletonWithScopedDisposable singletonDisposable; 169 | using (var scope = provider.CreateScope()) 170 | { 171 | singletonDisposable = scope.ServiceProvider.GetService(); 172 | } 173 | 174 | Assert.False(singletonDisposable.Disposable.IsDisposed); 175 | } 176 | 177 | public class SingletonWithScopedDisposable 178 | { 179 | public SingletonWithScopedDisposable(ScopedDisposable disposable) 180 | { 181 | Disposable = disposable; 182 | } 183 | 184 | public ScopedDisposable Disposable { get; } 185 | 186 | } 187 | 188 | public class ScopedDisposable : IDisposable 189 | { 190 | public bool IsDisposed; 191 | 192 | public ScopedDisposable() 193 | { 194 | } 195 | 196 | public void Dispose() 197 | { 198 | IsDisposed = true; 199 | } 200 | } 201 | 202 | public class MyClient 203 | { 204 | public MyClient(HttpClient httpClient, IScoped scoped) 205 | { 206 | Scoped = scoped; 207 | } 208 | 209 | public IScoped Scoped { get; } 210 | } 211 | 212 | 213 | public interface IFoo 214 | { 215 | 216 | } 217 | 218 | public class Foo 219 | { 220 | } 221 | 222 | public class DerivedFoo : Foo 223 | { 224 | } 225 | 226 | public class ClassWithServiceProvider 227 | { 228 | public ClassWithServiceProvider(IServiceProvider serviceProvider) 229 | { 230 | ServiceProvider = serviceProvider; 231 | } 232 | 233 | public IServiceProvider ServiceProvider { get; } 234 | } 235 | 236 | public class ClassWithDefaultStringArgument 237 | { 238 | public ClassWithDefaultStringArgument(string value = "42") 239 | { 240 | Value = value; 241 | } 242 | 243 | public string Value { get; } 244 | } 245 | 246 | 247 | 248 | public interface IScoped 249 | { 250 | 251 | } 252 | 253 | public class Scoped : IScoped 254 | { 255 | public static int InstanceCount; 256 | 257 | public Scoped() 258 | { 259 | InstanceCount++; 260 | } 261 | } 262 | } 263 | 264 | 265 | public class Foo 266 | { 267 | } 268 | 269 | public class DerivedFoo : Foo 270 | { 271 | } 272 | 273 | public class ClassWithServiceProvider 274 | { 275 | public ClassWithServiceProvider(IServiceProvider serviceProvider) 276 | { 277 | ServiceProvider = serviceProvider; 278 | } 279 | 280 | public IServiceProvider ServiceProvider { get; } 281 | } 282 | 283 | public class ClassWithDefaultStringArgument 284 | { 285 | public ClassWithDefaultStringArgument(string value = "42") 286 | { 287 | Value = value; 288 | } 289 | 290 | public string Value { get; } 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /src/LightInject.Microsoft.DependencyInjection/LightInject.Microsoft.DependencyInjection.cs: -------------------------------------------------------------------------------- 1 | /********************************************************************************* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2024 bernhard.richter@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************** 24 | http://www.lightinject.net/ 25 | http://twitter.com/bernhardrichter 26 | ******************************************************************************/ 27 | #if NET6_0_OR_GREATER 28 | #define USE_ASYNCDISPOSABLE 29 | #endif 30 | [module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1126:PrefixCallsCorrectly", Justification = "Reviewed")] 31 | [module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:PrefixLocalCallsWithThis", Justification = "No inheritance")] 32 | [module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Single source file deployment.")] 33 | [module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:FileMustHaveHeader", Justification = "Custom header.")] 34 | [module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "All public members are documented.")] 35 | [module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Performance")] 36 | [module: System.Diagnostics.CodeAnalysis.SuppressMessage("MaintainabilityRules", "SA1403", Justification = "One source file")] 37 | [module: System.Diagnostics.CodeAnalysis.SuppressMessage("DocumentationRules", "SA1649", Justification = "One source file")] 38 | 39 | namespace LightInject.Microsoft.DependencyInjection; 40 | 41 | using System; 42 | using System.Collections.Concurrent; 43 | using System.Collections.Generic; 44 | using System.Diagnostics.CodeAnalysis; 45 | using System.Globalization; 46 | using System.Linq; 47 | using System.Reflection; 48 | using System.Runtime.CompilerServices; 49 | using System.Threading.Tasks; 50 | using global::Microsoft.Extensions.DependencyInjection; 51 | 52 | /// 53 | /// Extends the interface. 54 | /// 55 | public static class LightInjectServiceCollectionExtensions 56 | { 57 | /// 58 | /// Create a new from the given . 59 | /// 60 | /// The from which to create an . 61 | /// An that is backed by an . 62 | public static IServiceProvider CreateLightInjectServiceProvider(this IServiceCollection serviceCollection) 63 | => serviceCollection.CreateLightInjectServiceProvider(ContainerOptions.Default); 64 | 65 | /// 66 | /// Create a new from the given . 67 | /// 68 | /// The from which to create an . 69 | /// The to be used when creating the . 70 | /// An that is backed by an . 71 | public static IServiceProvider CreateLightInjectServiceProvider(this IServiceCollection serviceCollection, ContainerOptions options) 72 | { 73 | var clonedOptions = options.Clone(); 74 | clonedOptions.WithMicrosoftSettings(); 75 | var container = new ServiceContainer(clonedOptions); 76 | container.ConstructorDependencySelector = new AnnotatedConstructorDependencySelector(); 77 | container.ConstructorSelector = new AnnotatedConstructorSelector(container.CanGetInstance); 78 | return container.CreateServiceProvider(serviceCollection); 79 | } 80 | 81 | /// 82 | /// Create a new from the given . 83 | /// 84 | /// The from which to create an . 85 | /// A delegate used to configure . 86 | /// An that is backed by an . 87 | public static IServiceProvider CreateLightInjectServiceProvider(this IServiceCollection serviceCollection, Action configureOptions) 88 | { 89 | var options = ContainerOptions.Default.Clone().WithMicrosoftSettings(); 90 | configureOptions(options); 91 | return CreateLightInjectServiceProvider(serviceCollection, options); 92 | } 93 | } 94 | 95 | /// 96 | /// Extends the interface. 97 | /// 98 | public static class DependencyInjectionContainerExtensions 99 | { 100 | /// 101 | /// Creates an based on the given . 102 | /// 103 | /// The target . 104 | /// The that contains information about the services to be registered. 105 | /// A configured . 106 | public static IServiceProvider CreateServiceProvider(this IServiceContainer container, IServiceCollection serviceCollection) 107 | { 108 | if (container.AvailableServices.Any(sr => sr.ServiceType == typeof(IServiceProvider))) 109 | { 110 | throw new InvalidOperationException("CreateServiceProvider can only be called once per IServiceContainer instance."); 111 | } 112 | 113 | var rootScope = container.BeginScope(); 114 | rootScope.Completed += (a, s) => container.Dispose(); 115 | container.Register(f => new LightInjectServiceProvider((Scope)f)); 116 | container.RegisterSingleton(f => new LightInjectServiceScopeFactory(container)); 117 | container.RegisterSingleton(factory => new LightInjectIsServiceProviderIsService((serviceType, serviceName) => container.CanGetInstance(serviceType, serviceName))); 118 | container.RegisterSingleton(factory => new LightInjectIsServiceProviderIsService((serviceType, serviceName) => container.CanGetInstance(serviceType, serviceName))); 119 | RegisterServices(container, rootScope, serviceCollection); 120 | return new LightInjectServiceScope(rootScope).ServiceProvider; 121 | } 122 | 123 | private static void RegisterServices(IServiceContainer container, Scope rootScope, IServiceCollection serviceCollection) 124 | { 125 | var registrations = serviceCollection.Select(d => CreateServiceRegistration(d, rootScope)).ToList(); 126 | 127 | foreach (var registration in registrations) 128 | { 129 | container.Register(registration); 130 | } 131 | } 132 | 133 | private static ServiceRegistration CreateServiceRegistration(ServiceDescriptor serviceDescriptor, Scope rootScope) 134 | { 135 | if (serviceDescriptor.IsKeyedService) 136 | { 137 | if (serviceDescriptor.KeyedImplementationFactory != null) 138 | { 139 | return CreateServiceRegistrationForKeyedFactoryDelegate(serviceDescriptor, rootScope); 140 | } 141 | 142 | if (serviceDescriptor.KeyedImplementationInstance != null) 143 | { 144 | return CreateServiceRegistrationForKeyedImplementationInstance(serviceDescriptor, rootScope); 145 | } 146 | 147 | return CreateServiceRegistrationForKeyedImplementationType(serviceDescriptor, rootScope); 148 | } 149 | else 150 | { 151 | if (serviceDescriptor.ImplementationFactory != null) 152 | { 153 | return CreateServiceRegistrationForFactoryDelegate(serviceDescriptor, rootScope); 154 | } 155 | 156 | if (serviceDescriptor.ImplementationInstance != null) 157 | { 158 | return CreateServiceRegistrationForImplementationInstance(serviceDescriptor, rootScope); 159 | } 160 | 161 | return CreateServiceRegistrationForImplementationType(serviceDescriptor, rootScope); 162 | } 163 | } 164 | 165 | private static ServiceRegistration CreateServiceRegistrationForImplementationType(ServiceDescriptor serviceDescriptor, Scope rootScope) 166 | { 167 | ServiceRegistration registration = CreateBasicServiceRegistration(serviceDescriptor, rootScope); 168 | registration.ImplementingType = serviceDescriptor.ImplementationType; 169 | return registration; 170 | } 171 | 172 | private static ServiceRegistration CreateServiceRegistrationForKeyedImplementationType(ServiceDescriptor serviceDescriptor, Scope rootScope) 173 | { 174 | ServiceRegistration registration = CreateBasicServiceRegistration(serviceDescriptor, rootScope); 175 | registration.ImplementingType = serviceDescriptor.KeyedImplementationType; 176 | registration.ServiceName = serviceDescriptor.ServiceKey.ToString(); 177 | return registration; 178 | } 179 | 180 | private static ServiceRegistration CreateServiceRegistrationForImplementationInstance(ServiceDescriptor serviceDescriptor, Scope rootScope) 181 | { 182 | ServiceRegistration registration = CreateBasicServiceRegistration(serviceDescriptor, rootScope); 183 | registration.Value = serviceDescriptor.ImplementationInstance; 184 | return registration; 185 | } 186 | 187 | private static ServiceRegistration CreateServiceRegistrationForKeyedImplementationInstance(ServiceDescriptor serviceDescriptor, Scope rootScope) 188 | { 189 | ServiceRegistration registration = CreateBasicServiceRegistration(serviceDescriptor, rootScope); 190 | registration.Value = serviceDescriptor.KeyedImplementationInstance; 191 | LightInjectServiceProvider.KeyedServiceTypeCache.TryAdd(serviceDescriptor.ServiceType, serviceDescriptor.ServiceKey.GetType()); 192 | registration.ServiceName = serviceDescriptor.ServiceKey.ToString(); 193 | return registration; 194 | } 195 | 196 | private static ServiceRegistration CreateServiceRegistrationForFactoryDelegate(ServiceDescriptor serviceDescriptor, Scope rootScope) 197 | { 198 | ServiceRegistration registration = CreateBasicServiceRegistration(serviceDescriptor, rootScope); 199 | registration.FactoryExpression = CreateFactoryDelegate(serviceDescriptor); 200 | return registration; 201 | } 202 | 203 | private static ServiceRegistration CreateServiceRegistrationForKeyedFactoryDelegate(ServiceDescriptor serviceDescriptor, Scope rootScope) 204 | { 205 | ServiceRegistration registration = CreateBasicServiceRegistration(serviceDescriptor, rootScope); 206 | registration.FactoryExpression = CreateKeyedFactoryDelegate(serviceDescriptor); 207 | LightInjectServiceProvider.KeyedServiceTypeCache.TryAdd(serviceDescriptor.ServiceType, serviceDescriptor.ServiceKey.GetType()); 208 | registration.FactoryType = FactoryType.ServiceWithServiceKey; 209 | return registration; 210 | } 211 | 212 | private static ServiceRegistration CreateBasicServiceRegistration(ServiceDescriptor serviceDescriptor, Scope rootScope) 213 | { 214 | ServiceRegistration registration = new() 215 | { 216 | Lifetime = ResolveLifetime(serviceDescriptor, rootScope), 217 | ServiceType = serviceDescriptor.ServiceType, 218 | }; 219 | if (serviceDescriptor.IsKeyedService) 220 | { 221 | registration.ServiceName = serviceDescriptor.ServiceKey.ToString(); 222 | } 223 | 224 | return registration; 225 | } 226 | 227 | private static ILifetime ResolveLifetime(ServiceDescriptor serviceDescriptor, Scope rootScope) 228 | { 229 | ILifetime lifetime = null; 230 | 231 | switch (serviceDescriptor.Lifetime) 232 | { 233 | case ServiceLifetime.Scoped: 234 | lifetime = new PerScopeLifetime(); 235 | break; 236 | case ServiceLifetime.Singleton: 237 | lifetime = new PerRootScopeLifetime(rootScope); 238 | break; 239 | case ServiceLifetime.Transient: 240 | lifetime = NeedsTracking(serviceDescriptor) ? new PerRequestLifeTime() : null; 241 | break; 242 | } 243 | 244 | return lifetime; 245 | } 246 | 247 | private static bool NeedsTracking(ServiceDescriptor serviceDescriptor) 248 | { 249 | if (typeof(IDisposable).IsAssignableFrom(serviceDescriptor.ServiceType)) 250 | { 251 | return true; 252 | } 253 | 254 | if (serviceDescriptor.IsKeyedService) 255 | { 256 | if (serviceDescriptor.KeyedImplementationType != null && typeof(IDisposable).IsAssignableFrom(serviceDescriptor.KeyedImplementationType)) 257 | { 258 | return true; 259 | } 260 | } 261 | else if (serviceDescriptor.ImplementationType != null && typeof(IDisposable).IsAssignableFrom(serviceDescriptor.ImplementationType)) 262 | { 263 | return true; 264 | } 265 | 266 | return false; 267 | } 268 | 269 | private static Delegate CreateFactoryDelegate(ServiceDescriptor serviceDescriptor) 270 | { 271 | var openGenericMethod = typeof(DependencyInjectionContainerExtensions).GetTypeInfo().GetDeclaredMethod("CreateTypedFactoryDelegate"); 272 | var closedGenericMethod = openGenericMethod.MakeGenericMethod(serviceDescriptor.ServiceType.UnderlyingSystemType); 273 | return (Delegate)closedGenericMethod.Invoke(null, [serviceDescriptor]); 274 | } 275 | 276 | #pragma warning disable IDE0051 277 | private static Func CreateTypedFactoryDelegate(ServiceDescriptor serviceDescriptor) 278 | => serviceFactory => (T)serviceDescriptor.ImplementationFactory(new LightInjectServiceProvider((Scope)serviceFactory)); 279 | #pragma warning restore IDE0051 280 | 281 | private static Delegate CreateKeyedFactoryDelegate(ServiceDescriptor serviceDescriptor) 282 | { 283 | var openGenericMethod = typeof(DependencyInjectionContainerExtensions).GetTypeInfo().GetDeclaredMethod("CreateTypedKeyedFactoryDelegate"); 284 | var closedGenericMethod = openGenericMethod.MakeGenericMethod(serviceDescriptor.ServiceType.UnderlyingSystemType); 285 | return (Delegate)closedGenericMethod.Invoke(null, [serviceDescriptor]); 286 | } 287 | 288 | #pragma warning disable IDE0051 289 | private static Func CreateTypedKeyedFactoryDelegate(ServiceDescriptor serviceDescriptor) 290 | { 291 | return (serviceFactory, serviceName) => 292 | { 293 | LightInjectServiceProvider.KeyedServiceTypeCache.TryGetValue(serviceDescriptor.ServiceType, out var serviceKeyType); 294 | object key; 295 | if (serviceKeyType.IsEnum) 296 | { 297 | key = Enum.Parse(serviceKeyType, serviceName); 298 | } 299 | else if (serviceKeyType == typeof(int)) 300 | { 301 | key = int.Parse(serviceName, CultureInfo.InvariantCulture); 302 | } 303 | else if (serviceKeyType == typeof(string)) 304 | { 305 | key = serviceName; 306 | } 307 | else 308 | { 309 | try 310 | { 311 | key = Convert.ChangeType(serviceName, serviceKeyType, CultureInfo.InvariantCulture); 312 | } 313 | catch (Exception ex) 314 | { 315 | throw new InvalidOperationException($"Unable to convert service key '{serviceName}' to type '{serviceKeyType}'.", ex); 316 | } 317 | } 318 | 319 | return (T)serviceDescriptor.KeyedImplementationFactory(new LightInjectServiceProvider((Scope)serviceFactory), key); 320 | }; 321 | } 322 | #pragma warning restore IDE0051 323 | } 324 | 325 | /// 326 | /// Extends the class. 327 | /// 328 | public static class ContainerOptionsExtensions 329 | { 330 | /// 331 | /// Sets up the to be compliant with the conventions used in Microsoft.Extensions.DependencyInjection. 332 | /// 333 | /// The target . 334 | /// . 335 | public static ContainerOptions WithMicrosoftSettings(this ContainerOptions options) 336 | { 337 | options.EnablePropertyInjection = false; 338 | options.EnableCurrentScope = false; 339 | options.EnableOptionalArguments = true; 340 | options.EnableMicrosoftCompatibility = true; 341 | return options; 342 | } 343 | 344 | /// 345 | /// Creates a clone of the given paramref name="containerOptions". 346 | /// 347 | /// The for which to create a clone. 348 | /// A clone of the given paramref name="containerOptions". 349 | public static ContainerOptions Clone(this ContainerOptions containerOptions) => new() 350 | { 351 | DefaultServiceSelector = containerOptions.DefaultServiceSelector, 352 | EnableCurrentScope = containerOptions.EnableCurrentScope, 353 | EnablePropertyInjection = containerOptions.EnablePropertyInjection, 354 | EnableVariance = containerOptions.EnableVariance, 355 | LogFactory = containerOptions.LogFactory, 356 | VarianceFilter = containerOptions.VarianceFilter, 357 | EnableOptionalArguments = containerOptions.EnableOptionalArguments, 358 | EnableMicrosoftCompatibility = containerOptions.EnableMicrosoftCompatibility, 359 | }; 360 | } 361 | 362 | /// 363 | /// Creates a LightInject container builder. 364 | /// 365 | public class LightInjectServiceProviderFactory : IServiceProviderFactory 366 | { 367 | private readonly Func containerFactory; 368 | 369 | private IServiceCollection services; 370 | 371 | /// 372 | /// Initializes a new instance of the class. 373 | /// 374 | public LightInjectServiceProviderFactory() 375 | : this(ContainerOptions.Default) 376 | { 377 | } 378 | 379 | /// 380 | /// Initializes a new instance of the class. 381 | /// 382 | /// The to be used when creating the . 383 | public LightInjectServiceProviderFactory(ContainerOptions options) 384 | { 385 | var clonedOptions = options.Clone(); 386 | clonedOptions.WithMicrosoftSettings(); 387 | containerFactory = () => new ServiceContainer(clonedOptions); 388 | } 389 | 390 | /// 391 | /// Initializes a new instance of the class. 392 | /// 393 | /// A delegate used to configure . 394 | public LightInjectServiceProviderFactory(Action configureOptions) 395 | { 396 | var options = ContainerOptions.Default.Clone().WithMicrosoftSettings(); 397 | configureOptions(options); 398 | containerFactory = () => new ServiceContainer(options); 399 | } 400 | 401 | /// 402 | /// Initializes a new instance of the class. 403 | /// 404 | /// The to be used. 405 | public LightInjectServiceProviderFactory(IServiceContainer serviceContainer) 406 | => containerFactory = () => serviceContainer; 407 | 408 | /// 409 | public IServiceContainer CreateBuilder(IServiceCollection services) 410 | { 411 | this.services = services; 412 | return containerFactory(); 413 | } 414 | 415 | /// 416 | public IServiceProvider CreateServiceProvider(IServiceContainer containerBuilder) 417 | => containerBuilder.CreateServiceProvider(services); 418 | } 419 | 420 | /// 421 | /// An that uses LightInject as the underlying container. 422 | /// 423 | /// 424 | /// Initializes a new instance of the class. 425 | /// 426 | /// The from which this service provider requests services. 427 | #if USE_ASYNCDISPOSABLE 428 | internal class LightInjectServiceProvider(Scope scope) : IServiceProvider, ISupportRequiredService, IKeyedServiceProvider, IDisposable, IAsyncDisposable 429 | #else 430 | internal class LightInjectServiceProvider(Scope scope) : IServiceProvider, ISupportRequiredService, IDisposable 431 | #endif 432 | { 433 | private bool isDisposed = false; 434 | 435 | public static ConcurrentDictionary KeyedServiceTypeCache { get; } = new ConcurrentDictionary(); 436 | 437 | public void Dispose() 438 | { 439 | if (isDisposed) 440 | { 441 | return; 442 | } 443 | 444 | isDisposed = true; 445 | scope.Dispose(); 446 | } 447 | #if USE_ASYNCDISPOSABLE 448 | 449 | public ValueTask DisposeAsync() 450 | { 451 | if (isDisposed) 452 | { 453 | return ValueTask.CompletedTask; 454 | } 455 | 456 | isDisposed = true; 457 | 458 | return scope.DisposeAsync(); 459 | } 460 | 461 | public object GetKeyedService(Type serviceType, object serviceKey) 462 | { 463 | if (serviceKey != null) 464 | { 465 | KeyedServiceTypeCache.AddOrUpdate(serviceType, serviceKey.GetType(), (t, _) => serviceKey.GetType()); 466 | } 467 | 468 | return scope.TryGetInstance(serviceType, serviceKey?.ToString()); 469 | } 470 | 471 | public object GetRequiredKeyedService(Type serviceType, object serviceKey) 472 | { 473 | if (serviceKey != null) 474 | { 475 | KeyedServiceTypeCache.AddOrUpdate(serviceType, serviceKey.GetType(), (t, _) => serviceKey.GetType()); 476 | } 477 | 478 | return scope.GetInstance(serviceType, serviceKey?.ToString()); 479 | } 480 | #endif 481 | 482 | /// 483 | /// Gets an instance of the given . 484 | /// 485 | /// The service type to return. 486 | /// An instance of the given . 487 | /// Throws an exception if it cannot be created. 488 | public object GetRequiredService(Type serviceType) 489 | => scope.GetInstance(serviceType); 490 | 491 | /// 492 | /// Gets an instance of the given . 493 | /// 494 | /// The service type to return. 495 | /// An instance of the given . 496 | public object GetService(Type serviceType) 497 | => scope.TryGetInstance(serviceType); 498 | } 499 | 500 | /// 501 | /// An that uses an to create new scopes. 502 | /// 503 | /// 504 | /// Initializes a new instance of the class. 505 | /// 506 | /// The used to create new scopes. 507 | internal class LightInjectServiceScopeFactory(IServiceContainer container) : IServiceScopeFactory 508 | { 509 | 510 | /// 511 | public IServiceScope CreateScope() 512 | => new LightInjectServiceScope(container.BeginScope()); 513 | } 514 | 515 | /// 516 | /// An implementation that wraps a . 517 | /// 518 | /// 519 | /// Initializes a new instance of the class. 520 | /// 521 | /// The wrapped by this class. 522 | #if USE_ASYNCDISPOSABLE 523 | internal class LightInjectServiceScope(Scope scope) : IServiceScope, IAsyncDisposable 524 | #else 525 | internal class LightInjectServiceScope(Scope scope) : IServiceScope 526 | #endif 527 | { 528 | public IServiceProvider ServiceProvider { get; } = new LightInjectServiceProvider(scope); 529 | 530 | /// 531 | public void Dispose() => scope.Dispose(); 532 | 533 | #if USE_ASYNCDISPOSABLE 534 | /// 535 | public ValueTask DisposeAsync() => scope.DisposeAsync(); 536 | #endif 537 | } 538 | 539 | /// 540 | /// An implementation that makes it possible to mimic the notion of a root scope. 541 | /// 542 | /// 543 | /// Initializes a new instance of the class. 544 | /// 545 | /// The root . 546 | [LifeSpan(30)] 547 | internal class PerRootScopeLifetime(Scope rootScope) : ILifetime, ICloneableLifeTime 548 | { 549 | private readonly object syncRoot = new(); 550 | private object instance; 551 | 552 | /// 553 | [ExcludeFromCodeCoverage] 554 | public object GetInstance(Func createInstance, Scope scope) 555 | => throw new NotImplementedException("Uses optimized non closing method"); 556 | 557 | /// 558 | public ILifetime Clone() 559 | => new PerRootScopeLifetime(rootScope); 560 | 561 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 562 | #pragma warning disable IDE0060 563 | public object GetInstance(GetInstanceDelegate createInstance, Scope scope, object[] arguments) 564 | { 565 | #pragma warning restore IDE0060 566 | if (instance != null) 567 | { 568 | return instance; 569 | } 570 | 571 | lock (syncRoot) 572 | { 573 | if (instance == null) 574 | { 575 | instance = createInstance(arguments, rootScope); 576 | RegisterForDisposal(instance); 577 | } 578 | } 579 | 580 | return instance; 581 | } 582 | 583 | private void RegisterForDisposal(object instance) 584 | { 585 | if (instance is IDisposable disposable) 586 | { 587 | rootScope.TrackInstance(disposable); 588 | } 589 | else if (instance is IAsyncDisposable asyncDisposable) 590 | { 591 | rootScope.TrackInstance(asyncDisposable); 592 | } 593 | } 594 | } 595 | 596 | internal class LightInjectIsServiceProviderIsService(Func canGetService) : IServiceProviderIsKeyedService 597 | { 598 | public bool IsKeyedService(Type serviceType, object serviceKey) 599 | { 600 | if (serviceType.IsGenericTypeDefinition) 601 | { 602 | return false; 603 | } 604 | 605 | return canGetService(serviceType, serviceKey?.ToString() ?? string.Empty); 606 | } 607 | 608 | public bool IsService(Type serviceType) 609 | { 610 | if (serviceType.IsGenericTypeDefinition) 611 | { 612 | return false; 613 | } 614 | 615 | return canGetService(serviceType, string.Empty); 616 | } 617 | } 618 | 619 | /// 620 | /// A that looks for the 621 | /// to determine the name of service to be injected. 622 | /// 623 | public class AnnotatedConstructorDependencySelector : ConstructorDependencySelector 624 | { 625 | /// 626 | /// Selects the constructor dependencies for the given . 627 | /// 628 | /// The for which to select the constructor dependencies. 629 | /// A list of instances that represents the constructor 630 | /// dependencies for the given . 631 | public override IEnumerable Execute(ConstructorInfo constructor) 632 | { 633 | var constructorDependencies = base.Execute(constructor).ToArray(); 634 | foreach (var constructorDependency in constructorDependencies) 635 | { 636 | var injectAttribute = 637 | constructorDependency.Parameter.GetFromKeyedServicesAttributes().FirstOrDefault(); 638 | if (injectAttribute != null) 639 | { 640 | constructorDependency.ServiceName = injectAttribute.Key.ToString(); 641 | } 642 | } 643 | 644 | return constructorDependencies; 645 | } 646 | } 647 | 648 | /// 649 | /// A implementation that uses information 650 | /// from the to determine if a given service can be resolved. 651 | /// 652 | /// 653 | /// Initializes a new instance of the class. 654 | /// 655 | /// A function delegate that determines if a service type can be resolved. 656 | public class AnnotatedConstructorSelector(Func canGetInstance) : MostResolvableConstructorSelector(canGetInstance) 657 | { 658 | 659 | /// 660 | /// Gets the service name based on the given . 661 | /// 662 | /// The for which to get the service name. 663 | /// The name of the service for the given . 664 | protected override string GetServiceName(ParameterInfo parameter) 665 | { 666 | var injectAttribute = parameter.GetFromKeyedServicesAttributes().FirstOrDefault(); 667 | return injectAttribute != null ? injectAttribute.Key.ToString() : base.GetServiceName(parameter); 668 | } 669 | } 670 | 671 | internal static class AttributeExtensions 672 | { 673 | internal static T[] GetFromKeyedServicesAttributes(this ParameterInfo member) where T : FromKeyedServicesAttribute 674 | { 675 | return member.GetCustomAttributes(inherit: true) 676 | .OfType() 677 | .ToArray(); 678 | } 679 | } --------------------------------------------------------------------------------