From 29a5d6fe92b0c5b9810d45fdf599ec8012d5b05e Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 10:09:27 +0100 Subject: [PATCH 01/53] - update copyright --- .../LightweightIocContainer.Validation.csproj | 2 +- LightweightIocContainer/LightweightIocContainer.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj b/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj index f7f5a19..15b7b53 100644 --- a/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj +++ b/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj @@ -3,7 +3,7 @@ net6.0 SimonG - Copyright(c) 2024 SimonG. All Rights Reserved. + Copyright(c) 2025 SimonG. All Rights Reserved. A lightweight IOC Container Validator. https://github.com/SimonG96/LightweightIocContainer latest diff --git a/LightweightIocContainer/LightweightIocContainer.csproj b/LightweightIocContainer/LightweightIocContainer.csproj index de2ccac..0ffc0ec 100644 --- a/LightweightIocContainer/LightweightIocContainer.csproj +++ b/LightweightIocContainer/LightweightIocContainer.csproj @@ -3,7 +3,7 @@ net6.0 SimonG - Copyright(c) 2024 SimonG. All Rights Reserved. + Copyright(c) 2025 SimonG. All Rights Reserved. A lightweight IOC Container that is powerful enough to do all the things you need it to do. https://github.com/SimonG96/LightweightIocContainer latest From 578a021cf8c655dcacfa37b14c3576a6d94e701b Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 10:11:51 +0100 Subject: [PATCH 02/53] - update to .net 10 - update nuget packages --- .../LightweightIocContainer.Validation.csproj | 2 +- LightweightIocContainer/LightweightIocContainer.csproj | 2 +- .../Test.LightweightIocContainer.Validation.csproj | 10 +++++----- .../Test.LightweightIocContainer.csproj | 10 +++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj b/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj index 15b7b53..e9ff946 100644 --- a/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj +++ b/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj @@ -1,7 +1,7 @@ - net6.0 + net10.0 SimonG Copyright(c) 2025 SimonG. All Rights Reserved. A lightweight IOC Container Validator. diff --git a/LightweightIocContainer/LightweightIocContainer.csproj b/LightweightIocContainer/LightweightIocContainer.csproj index 0ffc0ec..d0fda26 100644 --- a/LightweightIocContainer/LightweightIocContainer.csproj +++ b/LightweightIocContainer/LightweightIocContainer.csproj @@ -1,7 +1,7 @@ - net6.0 + net10.0 SimonG Copyright(c) 2025 SimonG. All Rights Reserved. A lightweight IOC Container that is powerful enough to do all the things you need it to do. diff --git a/Test.LightweightIocContainer.Validation/Test.LightweightIocContainer.Validation.csproj b/Test.LightweightIocContainer.Validation/Test.LightweightIocContainer.Validation.csproj index e716491..9af3782 100644 --- a/Test.LightweightIocContainer.Validation/Test.LightweightIocContainer.Validation.csproj +++ b/Test.LightweightIocContainer.Validation/Test.LightweightIocContainer.Validation.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 false SimonG default @@ -9,11 +9,11 @@ - - + + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Test.LightweightIocContainer/Test.LightweightIocContainer.csproj b/Test.LightweightIocContainer/Test.LightweightIocContainer.csproj index ce75ffa..ddbde95 100644 --- a/Test.LightweightIocContainer/Test.LightweightIocContainer.csproj +++ b/Test.LightweightIocContainer/Test.LightweightIocContainer.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 false SimonG default @@ -9,11 +9,11 @@ - - + + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive From 50b188a7ff9b1431a0d23b69b50a135d2cd4aacb Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 11:04:43 +0100 Subject: [PATCH 03/53] - add generator project --- ...weightIocContainer.FactoryGenerator.csproj | 36 +++++++++++++++++++ .../README.md | 9 +++++ LightweightIocContainer.sln | 6 ++++ 3 files changed, 51 insertions(+) create mode 100644 LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj create mode 100644 LightweightIocContainer.FactoryGenerator/README.md diff --git a/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj b/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj new file mode 100644 index 0000000..bcefa9c --- /dev/null +++ b/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj @@ -0,0 +1,36 @@ + + + + netstandard2.0 + SimonG + Copyright(c) 2025 SimonG. All Rights Reserved. + Incremental generator to generate factories + https://github.com/SimonG96/LightweightIocContainer + preview + enable + enable + true + true + 5.0.0 + + + + true + false + true + https://github.com/SimonG96/LightweightIocContainer + MIT + README.md + true + snupkg + + + + + + + + + + + \ No newline at end of file diff --git a/LightweightIocContainer.FactoryGenerator/README.md b/LightweightIocContainer.FactoryGenerator/README.md new file mode 100644 index 0000000..e9617c7 --- /dev/null +++ b/LightweightIocContainer.FactoryGenerator/README.md @@ -0,0 +1,9 @@ +## Get started with the Lightweight IOC Container Factory Generator + +### How to install + +The easiest way to [install](https://github.com/SimonG96/LghtweightIocContainer/wiki/Install-Lightweight-IOC-Container) the Lightweight IOC Container is by using [NuGet](https://www.nuget.org/packages/LightweightIocContainer.FactoryGenerator/) through the [`.NET CLI`](https://github.com/SimonG96/LightweightIocContainer/wiki/Install-Lightweight-IOC-Container#net-cli): + +```.net +> dotnet add package LightweightIocContainer.FactoryGenerator --version 5.0.0 +``` diff --git a/LightweightIocContainer.sln b/LightweightIocContainer.sln index e4fa8ab..809848f 100644 --- a/LightweightIocContainer.sln +++ b/LightweightIocContainer.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightweightIocContainer.Val EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.LightweightIocContainer.Validation", "Test.LightweightIocContainer.Validation\Test.LightweightIocContainer.Validation.csproj", "{82818CE7-6219-4A2E-A506-3AE09A00578C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightweightIocContainer.FactoryGenerator", "LightweightIocContainer.FactoryGenerator\LightweightIocContainer.FactoryGenerator.csproj", "{3AAB43AC-1CBA-4A81-81CB-1EAC2AB792E3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +35,10 @@ Global {82818CE7-6219-4A2E-A506-3AE09A00578C}.Debug|Any CPU.Build.0 = Debug|Any CPU {82818CE7-6219-4A2E-A506-3AE09A00578C}.Release|Any CPU.ActiveCfg = Release|Any CPU {82818CE7-6219-4A2E-A506-3AE09A00578C}.Release|Any CPU.Build.0 = Release|Any CPU + {3AAB43AC-1CBA-4A81-81CB-1EAC2AB792E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3AAB43AC-1CBA-4A81-81CB-1EAC2AB792E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AAB43AC-1CBA-4A81-81CB-1EAC2AB792E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3AAB43AC-1CBA-4A81-81CB-1EAC2AB792E3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From a51be3b819b39fd3c57f0225154ffc4fd26c7417 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 11:05:14 +0100 Subject: [PATCH 04/53] - update versions and readmes --- .../LightweightIocContainer.Validation.csproj | 2 +- LightweightIocContainer.Validation/README.md | 2 +- LightweightIocContainer/LightweightIocContainer.csproj | 2 +- README.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj b/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj index e9ff946..b2e11a4 100644 --- a/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj +++ b/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj @@ -10,7 +10,7 @@ enable enable LightweightIocContainer.Validation.xml - 4.4.0 + 5.0.0 diff --git a/LightweightIocContainer.Validation/README.md b/LightweightIocContainer.Validation/README.md index 3127460..9dc2c3f 100644 --- a/LightweightIocContainer.Validation/README.md +++ b/LightweightIocContainer.Validation/README.md @@ -5,7 +5,7 @@ The easiest way to [install](https://github.com/SimonG96/LghtweightIocContainer/wiki/Install-Lightweight-IOC-Container) the Lightweight IOC Container is by using [NuGet](https://www.nuget.org/packages/LightweightIocContainer.Validation/) through the [`.NET CLI`](https://github.com/SimonG96/LightweightIocContainer/wiki/Install-Lightweight-IOC-Container#net-cli): ```.net -> dotnet add package LightweightIocContainer.Validaton --version 4.4.0-beta2 +> dotnet add package LightweightIocContainer.Validation --version 5.0.0 ``` ### Validation diff --git a/LightweightIocContainer/LightweightIocContainer.csproj b/LightweightIocContainer/LightweightIocContainer.csproj index d0fda26..5216bc4 100644 --- a/LightweightIocContainer/LightweightIocContainer.csproj +++ b/LightweightIocContainer/LightweightIocContainer.csproj @@ -10,7 +10,7 @@ enable enable LightweightIocContainer.xml - 4.4.0 + 5.0.0 diff --git a/README.md b/README.md index 2039dbe..157cb99 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ A lightweight IOC Container that is powerful enough to do all the things you nee The easiest way to [install](https://github.com/SimonG96/LightweightIocContainer/wiki/Install-Lightweight-IOC-Container) the Lightweight IOC Container is by using [NuGet](https://www.nuget.org/packages/LightweightIocContainer/) through the [`.NET CLI`](https://github.com/SimonG96/LightweightIocContainer/wiki/Install-Lightweight-IOC-Container#net-cli): ```.net -> dotnet add package LightweightIocContainer --version 4.4.0 +> dotnet add package LightweightIocContainer --version 5.0.0 ``` ### Example usage @@ -59,7 +59,7 @@ The easiest way to [install](https://github.com/SimonG96/LightweightIocContainer There is the option to install the [LightweightIocContainer.Validation](https://www.nuget.org/packages/LightweightIocContainer.Validation/) package: ```.net -> dotnet add package LightweightIocContainer.Validaton --version 4.4.0 +> dotnet add package LightweightIocContainer.Validaton --version 5.0.0 ``` With this you can validate your `IocContainer` setup by using the `IocValidator` in a unit test: From e29a3ebdf8909d6f9f8852df68cde8877a1c659e Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 11:09:07 +0100 Subject: [PATCH 05/53] - add readme to package --- .../LightweightIocContainer.FactoryGenerator.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj b/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj index bcefa9c..ad8177d 100644 --- a/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj +++ b/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj @@ -24,6 +24,10 @@ true snupkg + + + + From 2bd3fbfbad7a362cc789abfd290f5f63196b742a Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 11:26:56 +0100 Subject: [PATCH 06/53] - add debug project --- ...ghtweightIocContainer.FactoryGenerator.csproj | 16 ++++++++++++++++ LightweightIocContainer.sln | 6 ++++++ 2 files changed, 22 insertions(+) create mode 100644 Debug.LightweightIocContainer.FactoryGenerator/Debug.LightweightIocContainer.FactoryGenerator.csproj diff --git a/Debug.LightweightIocContainer.FactoryGenerator/Debug.LightweightIocContainer.FactoryGenerator.csproj b/Debug.LightweightIocContainer.FactoryGenerator/Debug.LightweightIocContainer.FactoryGenerator.csproj new file mode 100644 index 0000000..90b574d --- /dev/null +++ b/Debug.LightweightIocContainer.FactoryGenerator/Debug.LightweightIocContainer.FactoryGenerator.csproj @@ -0,0 +1,16 @@ + + + + net10.0 + preview + enable + enable + + + + + + + \ No newline at end of file diff --git a/LightweightIocContainer.sln b/LightweightIocContainer.sln index 809848f..e44123e 100644 --- a/LightweightIocContainer.sln +++ b/LightweightIocContainer.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.LightweightIocContaine EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightweightIocContainer.FactoryGenerator", "LightweightIocContainer.FactoryGenerator\LightweightIocContainer.FactoryGenerator.csproj", "{3AAB43AC-1CBA-4A81-81CB-1EAC2AB792E3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debug.LightweightIocContainer.FactoryGenerator", "Debug.LightweightIocContainer.FactoryGenerator\Debug.LightweightIocContainer.FactoryGenerator.csproj", "{578ED8B2-A2BE-4E10-9422-4D5AF2E605E0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {3AAB43AC-1CBA-4A81-81CB-1EAC2AB792E3}.Debug|Any CPU.Build.0 = Debug|Any CPU {3AAB43AC-1CBA-4A81-81CB-1EAC2AB792E3}.Release|Any CPU.ActiveCfg = Release|Any CPU {3AAB43AC-1CBA-4A81-81CB-1EAC2AB792E3}.Release|Any CPU.Build.0 = Release|Any CPU + {578ED8B2-A2BE-4E10-9422-4D5AF2E605E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {578ED8B2-A2BE-4E10-9422-4D5AF2E605E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {578ED8B2-A2BE-4E10-9422-4D5AF2E605E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {578ED8B2-A2BE-4E10-9422-4D5AF2E605E0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 13878b8b3fa0e3f8322f606c8f7694013653b314 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 11:29:22 +0100 Subject: [PATCH 07/53] - don't ignore all launchSettings --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 96e904e..516a722 100644 --- a/.gitignore +++ b/.gitignore @@ -52,7 +52,6 @@ BenchmarkDotNet.Artifacts/ project.lock.json project.fragment.lock.json artifacts/ -**/Properties/launchSettings.json # StyleCop StyleCopReport.xml From ff33b25ef1d83dc0b08a77d1342a4acc55cb71c1 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 11:29:37 +0100 Subject: [PATCH 08/53] - add debug launch settings --- .../Properties/launchSettings.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 LightweightIocContainer.FactoryGenerator/Properties/launchSettings.json diff --git a/LightweightIocContainer.FactoryGenerator/Properties/launchSettings.json b/LightweightIocContainer.FactoryGenerator/Properties/launchSettings.json new file mode 100644 index 0000000..534c373 --- /dev/null +++ b/LightweightIocContainer.FactoryGenerator/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "DebugRoslynSourceGenerator": { + "commandName": "DebugRoslynComponent", + "targetProject": "../Debug.LightweightIocContainer.FactoryGenerator/Debug.LightweightIocContainer.FactoryGenerator.csproj" + } + } +} \ No newline at end of file From 17bd0fad0830a48f644abe795efa20a77d864f4a Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 13:12:36 +0100 Subject: [PATCH 09/53] - can't include symbols for sourceGenerator --- .../LightweightIocContainer.FactoryGenerator.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj b/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj index ad8177d..e2efa5d 100644 --- a/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj +++ b/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj @@ -21,8 +21,6 @@ https://github.com/SimonG96/LightweightIocContainer MIT README.md - true - snupkg From e7e347a477afe462a2632cb03a30db1845240a94 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 13:52:59 +0100 Subject: [PATCH 10/53] - add samle classes --- .../SampleClass.cs | 13 +++++++++++++ .../SampleInstaller.cs | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs create mode 100644 Debug.LightweightIocContainer.FactoryGenerator/SampleInstaller.cs diff --git a/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs b/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs new file mode 100644 index 0000000..57ee8cf --- /dev/null +++ b/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs @@ -0,0 +1,13 @@ +// Author: Simon.Gockner +// Created: 2025-12-01 +// Copyright(c) 2025 SimonG. All Rights Reserved. + +namespace Debug.LightweightIocContainer.FactoryGenerator; + +public class SampleClass : ISampleClass; +public interface ISampleClass; + +public interface ISampleClassFactory +{ + ISampleClass Create(); +} \ No newline at end of file diff --git a/Debug.LightweightIocContainer.FactoryGenerator/SampleInstaller.cs b/Debug.LightweightIocContainer.FactoryGenerator/SampleInstaller.cs new file mode 100644 index 0000000..3f63134 --- /dev/null +++ b/Debug.LightweightIocContainer.FactoryGenerator/SampleInstaller.cs @@ -0,0 +1,17 @@ +// Author: Simon.Gockner +// Created: 2025-12-01 +// Copyright(c) 2025 SimonG. All Rights Reserved. + +using LightweightIocContainer.FactoryGenerator; +using LightweightIocContainer.Interfaces.Installers; +using LightweightIocContainer.Interfaces.Registrations; + +namespace Debug.LightweightIocContainer.FactoryGenerator; + +public class SampleInstaller : IIocInstaller +{ + public void Install(IRegistrationCollector registration) + { + registration.Add().WithGeneratedFactory(); + } +} \ No newline at end of file From 230a030b04a46dd23d36598d5df7229e8bf793d6 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 13:53:39 +0100 Subject: [PATCH 11/53] - add ctor to pass generated factory instance --- LightweightIocContainer/Factories/TypedFactory.cs | 8 +++++++- LightweightIocContainer/LightweightIocContainer.xml | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/LightweightIocContainer/Factories/TypedFactory.cs b/LightweightIocContainer/Factories/TypedFactory.cs index 9f88148..f0dad10 100644 --- a/LightweightIocContainer/Factories/TypedFactory.cs +++ b/LightweightIocContainer/Factories/TypedFactory.cs @@ -19,10 +19,16 @@ public class TypedFactory : TypedFactoryBase, ITypedFactory< private const string CLEAR_MULTITON_INSTANCE_METHOD_NAME = "ClearMultitonInstance"; /// - /// The + /// Constructor for creating factories dynamically /// /// The current instance of the public TypedFactory(IocContainer container) => Factory = CreateFactory(container); + + /// + /// Constructor for generated factories + /// + /// + public TypedFactory(TFactory factory) => Factory = factory; /// /// The implemented abstract typed factory/> diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index ab26ed9..4fd07f4 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -384,10 +384,16 @@ - The + Constructor for creating factories dynamically The current instance of the + + + Constructor for generated factories + + + The implemented abstract typed factory/> From 543018a5601e70c4e2a843185a007833956469ed Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 14:03:51 +0100 Subject: [PATCH 12/53] - add method to add generated factory --- LightweightIocContainer/Registrations/RegistrationBase.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/LightweightIocContainer/Registrations/RegistrationBase.cs b/LightweightIocContainer/Registrations/RegistrationBase.cs index f9c08a3..ef89a0a 100644 --- a/LightweightIocContainer/Registrations/RegistrationBase.cs +++ b/LightweightIocContainer/Registrations/RegistrationBase.cs @@ -120,6 +120,14 @@ internal abstract class RegistrationBase : IRegistrationBase, IWithFactoryIntern return this; } + + internal void AddGeneratedFactory(TFactory generatedFactory) + { + TypedFactory factory = new(generatedFactory); + Factory = factory; + + _container.RegisterFactory(factory); + } /// /// Register a custom implemented factory for the From 1d9bfa88f650026f2aaffe66ef815ffeba9b4ac9 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Mon, 1 Dec 2025 14:04:06 +0100 Subject: [PATCH 13/53] - start implementing factoryGenerator --- .../FactoryGenerator.cs | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs new file mode 100644 index 0000000..f969dff --- /dev/null +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -0,0 +1,151 @@ +// Author: Simon.Gockner +// Created: 2025-12-01 +// Copyright(c) 2025 SimonG. All Rights Reserved. + +using System.Collections.Immutable; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace LightweightIocContainer.FactoryGenerator; + +[Generator] +public class FactoryGenerator : IIncrementalGenerator +{ + private const string EXTENSION_CLASS_NAME = "FactoryExtensions"; + private const string BUILDER_CLASS_NAME = "GeneratedFactoryBuilder"; + + private const string GENERATED_FILE_HEADER = "//---GENERATED by FactoryGenerator! DO NOT EDIT!---"; + private const string INDENT = " "; + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + string? classNamespace = typeof(FactoryGenerator).Namespace; + context.RegisterPostInitializationOutput(c => c.AddSource($"{EXTENSION_CLASS_NAME}.g.cs", GenerateFactoryExtensionsClass(classNamespace, EXTENSION_CLASS_NAME))); + + IncrementalValuesProvider syntaxProvider = context.SyntaxProvider.CreateSyntaxProvider(IsCallToGenerateFactory, GetTypeArgument); + context.RegisterSourceOutput(syntaxProvider.Collect(), GenerateTypeDependentClasses); + } + + private string GenerateFactoryExtensionsClass(string? classNamespace, string className) + { + StringBuilder stringBuilder = new(); + + stringBuilder.AppendLine(GENERATED_FILE_HEADER); + stringBuilder.AppendLine(); + + stringBuilder.AppendLine("using LightweightIocContainer.Interfaces.Registrations;"); + stringBuilder.AppendLine("using LightweightIocContainer.Interfaces.Registrations.Fluent;"); + stringBuilder.AppendLine("using LightweightIocContainer.Registrations;"); + stringBuilder.AppendLine(); + + if (!string.IsNullOrEmpty(classNamespace)) + { + stringBuilder.AppendLine($"namespace {classNamespace};"); + stringBuilder.AppendLine(); + } + + stringBuilder.AppendLine($"public static class {className}");; + stringBuilder.AppendLine("{"); + + stringBuilder.AppendLine($"{INDENT}public static IRegistrationBase WithGeneratedFactory(this IRegistrationBase registration)"); + stringBuilder.AppendLine($"{INDENT}{{"); + stringBuilder.AppendLine($"{INDENT}{INDENT}TFactory factory = Builder.Create();"); + stringBuilder.AppendLine(); + stringBuilder.AppendLine($"{INDENT}{INDENT}if (registration is not RegistrationBase registrationBase)"); + stringBuilder.AppendLine($"{INDENT}{INDENT}{INDENT}throw new InvalidOperationException(\"The registration must be of type RegistrationBase to add a generated factory.\");"); + stringBuilder.AppendLine(); + stringBuilder.AppendLine($"{INDENT}{INDENT}registrationBase.AddGeneratedFactory(factory);"); + stringBuilder.AppendLine(); + stringBuilder.AppendLine($"{INDENT}{INDENT}return registrationBase;"); + stringBuilder.AppendLine($"{INDENT}}}"); + + stringBuilder.AppendLine("}"); + + return stringBuilder.ToString(); + } + + private bool IsCallToGenerateFactory(SyntaxNode node, CancellationToken cancellationToken) + { + if (!node.IsKind(SyntaxKind.GenericName) || node is not GenericNameSyntax genericNameSyntax) + return false; + + if (!genericNameSyntax.ToString().StartsWith("WithGeneratedFactory")) + return false; + + if (genericNameSyntax.TypeArgumentList.Arguments[0] is not IdentifierNameSyntax) + return false; + + return true; + } + + private ITypeSymbol? GetTypeArgument(GeneratorSyntaxContext syntaxContext, CancellationToken cancellationToken) + { + if (syntaxContext.Node is not GenericNameSyntax genericNameSyntax) + return null; + + if (genericNameSyntax.TypeArgumentList.Arguments[0] is not IdentifierNameSyntax identifierNameSyntax) + return null; + + if (syntaxContext.SemanticModel.GetSymbolInfo(identifierNameSyntax).Symbol is not ITypeSymbol typeSymbol) + return null; + + return typeSymbol; + } + + private void GenerateTypeDependentClasses(SourceProductionContext context, ImmutableArray types) + { + string? classNamespace = typeof(FactoryGenerator).Namespace; + context.AddSource($"{BUILDER_CLASS_NAME}.g.cs", GenerateBuilderClassSourceCode(classNamespace, types)); + } + + private string GenerateBuilderClassSourceCode(string? classNamespace, ImmutableArray types) + { + StringBuilder stringBuilder = new(); + + stringBuilder.AppendLine(GENERATED_FILE_HEADER); + stringBuilder.AppendLine(); + + foreach (string typeNamespace in GetNamespacesOfTypes(types)) + stringBuilder.AppendLine($"using {typeNamespace};"); + + stringBuilder.AppendLine(); + + if (classNamespace is not null) + { + stringBuilder.AppendLine($"namespace {classNamespace};"); + stringBuilder.AppendLine(); + } + + stringBuilder.AppendLine("public static class Builder"); + stringBuilder.AppendLine("{"); + stringBuilder.AppendLine($"{INDENT}public static TFactory Create()"); + stringBuilder.AppendLine($"{INDENT}{{"); + + foreach (ITypeSymbol? type in types) + { + if (type is null) + continue; + + stringBuilder.AppendLine($"{INDENT}{INDENT}if (typeof(TFactory) == typeof({type.Name}))"); + stringBuilder.AppendLine($"{INDENT}{INDENT}{{"); + stringBuilder.AppendLine($"{INDENT}{INDENT}{INDENT}return (TFactory) (object) new Generated{type.Name}();"); + stringBuilder.AppendLine($"{INDENT}{INDENT}}}"); + stringBuilder.AppendLine(); + } + + stringBuilder.AppendLine($"{INDENT}{INDENT}throw new Exception(\"Invalid type.\");"); + + stringBuilder.AppendLine($"{INDENT}}}"); + stringBuilder.AppendLine("}"); + + return stringBuilder.ToString(); + } + + private IEnumerable GetNamespacesOfTypes(ImmutableArray types) => + types.OfType() + .Select(s => s.ContainingNamespace.IsGlobalNamespace ? null : s.ContainingNamespace.ToString()) + .OfType() + .Distinct(); +} \ No newline at end of file From aa18aab7641e76d3a7be44031c9edf3a969e806e Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Tue, 2 Dec 2025 08:40:16 +0100 Subject: [PATCH 14/53] - update actions --- .github/workflows/ci.yml | 12 ++++++------ .github/workflows/deploy.yml | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5475bba..0983bf9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,10 +9,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v1 - - uses: actions/setup-dotnet@v1 + uses: actions/checkout@v5 + - uses: actions/setup-dotnet@v5 with: - dotnet-version: '9.0.x' + dotnet-version: '10.x' - name: Build run: dotnet build @@ -23,10 +23,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v1 - - uses: actions/setup-dotnet@v1 + uses: actions/checkout@v5 + - uses: actions/setup-dotnet@v5 with: - dotnet-version: '9.0.x' + dotnet-version: '10.x' - name: Run tests run: dotnet test diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 70f73a3..18b2e09 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -11,10 +11,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v1 - - uses: actions/setup-dotnet@v1 + uses: actions/checkout@v5 + - uses: actions/setup-dotnet@v5 with: - dotnet-version: '9.0.x' + dotnet-version: '10.x' - name: Build run: dotnet build -c Release -o output - name: Deploy to nuGet gallery From 8779c6883d35d04e9f8ee70098a7b5bbf00cd603 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Tue, 2 Dec 2025 09:26:30 +0100 Subject: [PATCH 15/53] - expose addGeneratedFactory in interface --- .../Interfaces/Registrations/Fluent/IWithFactory.cs | 7 +++++++ LightweightIocContainer/Registrations/RegistrationBase.cs | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/LightweightIocContainer/Interfaces/Registrations/Fluent/IWithFactory.cs b/LightweightIocContainer/Interfaces/Registrations/Fluent/IWithFactory.cs index b06d491..918a998 100644 --- a/LightweightIocContainer/Interfaces/Registrations/Fluent/IWithFactory.cs +++ b/LightweightIocContainer/Interfaces/Registrations/Fluent/IWithFactory.cs @@ -25,6 +25,13 @@ public interface IWithFactory /// The type of the implementation for the custom factory /// The current instance of this IRegistrationBase WithFactory() where TFactoryImplementation : TFactoryInterface; + + /// + /// Internal method used by the factory source generator to add the generated factory to the registration + /// + /// The actual generated factory + /// The type of the generated factory + void AddGeneratedFactory(TFactory generatedFactory); } internal interface IWithFactoryInternal : IWithFactory diff --git a/LightweightIocContainer/Registrations/RegistrationBase.cs b/LightweightIocContainer/Registrations/RegistrationBase.cs index ef89a0a..54639b5 100644 --- a/LightweightIocContainer/Registrations/RegistrationBase.cs +++ b/LightweightIocContainer/Registrations/RegistrationBase.cs @@ -121,7 +121,12 @@ internal abstract class RegistrationBase : IRegistrationBase, IWithFactoryIntern return this; } - internal void AddGeneratedFactory(TFactory generatedFactory) + /// + /// Internal method used by the factory source generator to add the generated factory to the registration + /// + /// The actual generated factory + /// The type of the generated factory + public void AddGeneratedFactory(TFactory generatedFactory) { TypedFactory factory = new(generatedFactory); Factory = factory; From 68baf341b43d4dd5939fa36194679fa6f5d6e523 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Tue, 2 Dec 2025 09:26:51 +0100 Subject: [PATCH 16/53] - call method from interface --- .../FactoryGenerator.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index f969dff..625c527 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -37,7 +37,6 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine("using LightweightIocContainer.Interfaces.Registrations;"); stringBuilder.AppendLine("using LightweightIocContainer.Interfaces.Registrations.Fluent;"); - stringBuilder.AppendLine("using LightweightIocContainer.Registrations;"); stringBuilder.AppendLine(); if (!string.IsNullOrEmpty(classNamespace)) @@ -52,13 +51,9 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine($"{INDENT}public static IRegistrationBase WithGeneratedFactory(this IRegistrationBase registration)"); stringBuilder.AppendLine($"{INDENT}{{"); stringBuilder.AppendLine($"{INDENT}{INDENT}TFactory factory = Builder.Create();"); + stringBuilder.AppendLine($"{INDENT}{INDENT}registration.AddGeneratedFactory(factory);"); stringBuilder.AppendLine(); - stringBuilder.AppendLine($"{INDENT}{INDENT}if (registration is not RegistrationBase registrationBase)"); - stringBuilder.AppendLine($"{INDENT}{INDENT}{INDENT}throw new InvalidOperationException(\"The registration must be of type RegistrationBase to add a generated factory.\");"); - stringBuilder.AppendLine(); - stringBuilder.AppendLine($"{INDENT}{INDENT}registrationBase.AddGeneratedFactory(factory);"); - stringBuilder.AppendLine(); - stringBuilder.AppendLine($"{INDENT}{INDENT}return registrationBase;"); + stringBuilder.AppendLine($"{INDENT}{INDENT}return registration;"); stringBuilder.AppendLine($"{INDENT}}}"); stringBuilder.AppendLine("}"); From be1cea11e00771ce6e3d0db8682886a04dc79a80 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Tue, 2 Dec 2025 13:07:30 +0100 Subject: [PATCH 17/53] - update xml comments --- .../LightweightIocContainer.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index 4fd07f4..6eb609a 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -670,6 +670,13 @@ The type of the implementation for the custom factory The current instance of this + + + Internal method used by the factory source generator to add the generated factory to the registration + + The actual generated factory + The type of the generated factory + The Factory added with the method @@ -1601,6 +1608,13 @@ The type of the abstract typed factory The current instance of this + + + Internal method used by the factory source generator to add the generated factory to the registration + + The actual generated factory + The type of the generated factory + Register a custom implemented factory for the From daad788b2e253681fc1020ffe78f78996c5ee4ce Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Tue, 2 Dec 2025 13:40:03 +0100 Subject: [PATCH 18/53] - introduce factory builder to create factory instances --- .../Interfaces/Factories/IFactoryBuilder.cs | 19 +++++++++++++++++ .../Registrations/Fluent/IWithFactory.cs | 4 ++-- .../LightweightIocContainer.xml | 21 +++++++++++++++---- .../Registrations/RegistrationBase.cs | 8 +++---- 4 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 LightweightIocContainer/Interfaces/Factories/IFactoryBuilder.cs diff --git a/LightweightIocContainer/Interfaces/Factories/IFactoryBuilder.cs b/LightweightIocContainer/Interfaces/Factories/IFactoryBuilder.cs new file mode 100644 index 0000000..f64ae43 --- /dev/null +++ b/LightweightIocContainer/Interfaces/Factories/IFactoryBuilder.cs @@ -0,0 +1,19 @@ +// Author: Simon.Gockner +// Created: 2025-12-02 +// Copyright(c) 2025 SimonG. All Rights Reserved. + +namespace LightweightIocContainer.Interfaces.Factories; + +/// +/// Internal class used by the factory source generator to create factory instances +/// +public interface IFactoryBuilder +{ + /// + /// Internal method used by the factory source generator to create factory instances + /// + /// The current instance of the + /// The type of the factory + /// The created factory instance + TFactory Create(IocContainer container); +} \ No newline at end of file diff --git a/LightweightIocContainer/Interfaces/Registrations/Fluent/IWithFactory.cs b/LightweightIocContainer/Interfaces/Registrations/Fluent/IWithFactory.cs index 918a998..978b045 100644 --- a/LightweightIocContainer/Interfaces/Registrations/Fluent/IWithFactory.cs +++ b/LightweightIocContainer/Interfaces/Registrations/Fluent/IWithFactory.cs @@ -29,9 +29,9 @@ public interface IWithFactory /// /// Internal method used by the factory source generator to add the generated factory to the registration /// - /// The actual generated factory + /// The factory creator /// The type of the generated factory - void AddGeneratedFactory(TFactory generatedFactory); + void AddGeneratedFactory(IFactoryBuilder factoryBuilder); } internal interface IWithFactoryInternal : IWithFactory diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index 6eb609a..8bbe8b4 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -486,6 +486,19 @@ The given A new with the given + + + Internal class used by the factory source generator to create factory instances + + + + + Internal method used by the factory source generator to create factory instances + + The current instance of the + The type of the factory + The created factory instance + Non-generic @@ -670,11 +683,11 @@ The type of the implementation for the custom factory The current instance of this - + Internal method used by the factory source generator to add the generated factory to the registration - The actual generated factory + The factory creator The type of the generated factory @@ -1608,11 +1621,11 @@ The type of the abstract typed factory The current instance of this - + Internal method used by the factory source generator to add the generated factory to the registration - The actual generated factory + The factory creator The type of the generated factory diff --git a/LightweightIocContainer/Registrations/RegistrationBase.cs b/LightweightIocContainer/Registrations/RegistrationBase.cs index 54639b5..048f8ec 100644 --- a/LightweightIocContainer/Registrations/RegistrationBase.cs +++ b/LightweightIocContainer/Registrations/RegistrationBase.cs @@ -120,15 +120,15 @@ internal abstract class RegistrationBase : IRegistrationBase, IWithFactoryIntern return this; } - + /// /// Internal method used by the factory source generator to add the generated factory to the registration /// - /// The actual generated factory + /// The factory creator /// The type of the generated factory - public void AddGeneratedFactory(TFactory generatedFactory) + public void AddGeneratedFactory(IFactoryBuilder factoryBuilder) { - TypedFactory factory = new(generatedFactory); + TypedFactory factory = new(factoryBuilder.Create(_container)); Factory = factory; _container.RegisterFactory(factory); From 0ac89506c122d5913b602f9c2858adef355c6f64 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Tue, 2 Dec 2025 13:42:22 +0100 Subject: [PATCH 19/53] - builder now implements iFactoryBuilder that is called from registration --- .../FactoryGenerator.cs | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index 625c527..3d63744 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -14,7 +14,7 @@ namespace LightweightIocContainer.FactoryGenerator; public class FactoryGenerator : IIncrementalGenerator { private const string EXTENSION_CLASS_NAME = "FactoryExtensions"; - private const string BUILDER_CLASS_NAME = "GeneratedFactoryBuilder"; + private const string BUILDER_CLASS_NAME = "FactoryBuilder"; private const string GENERATED_FILE_HEADER = "//---GENERATED by FactoryGenerator! DO NOT EDIT!---"; private const string INDENT = " "; @@ -25,6 +25,7 @@ public class FactoryGenerator : IIncrementalGenerator context.RegisterPostInitializationOutput(c => c.AddSource($"{EXTENSION_CLASS_NAME}.g.cs", GenerateFactoryExtensionsClass(classNamespace, EXTENSION_CLASS_NAME))); IncrementalValuesProvider syntaxProvider = context.SyntaxProvider.CreateSyntaxProvider(IsCallToGenerateFactory, GetTypeArgument); + context.RegisterSourceOutput(syntaxProvider.Collect(), GenerateTypeDependentClasses); } @@ -35,8 +36,8 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine(GENERATED_FILE_HEADER); stringBuilder.AppendLine(); + stringBuilder.AppendLine("using LightweightIocContainer.Interfaces.Factories;"); stringBuilder.AppendLine("using LightweightIocContainer.Interfaces.Registrations;"); - stringBuilder.AppendLine("using LightweightIocContainer.Interfaces.Registrations.Fluent;"); stringBuilder.AppendLine(); if (!string.IsNullOrEmpty(classNamespace)) @@ -50,8 +51,8 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine($"{INDENT}public static IRegistrationBase WithGeneratedFactory(this IRegistrationBase registration)"); stringBuilder.AppendLine($"{INDENT}{{"); - stringBuilder.AppendLine($"{INDENT}{INDENT}TFactory factory = Builder.Create();"); - stringBuilder.AppendLine($"{INDENT}{INDENT}registration.AddGeneratedFactory(factory);"); + stringBuilder.AppendLine($"{INDENT}{INDENT}FactoryBuilder factoryBuilder = new();"); + stringBuilder.AppendLine($"{INDENT}{INDENT}registration.AddGeneratedFactory(factoryBuilder);"); stringBuilder.AppendLine(); stringBuilder.AppendLine($"{INDENT}{INDENT}return registration;"); stringBuilder.AppendLine($"{INDENT}}}"); @@ -94,6 +95,14 @@ public class FactoryGenerator : IIncrementalGenerator string? classNamespace = typeof(FactoryGenerator).Namespace; context.AddSource($"{BUILDER_CLASS_NAME}.g.cs", GenerateBuilderClassSourceCode(classNamespace, types)); } + + private void GenerateFactory(SourceProductionContext context, ITypeSymbol? typeSymbol) + { + if (typeSymbol is null) + return; + + context.AddSource($"Generated{typeSymbol.Name}.g.cs", GenerateFactorySourceCode(typeSymbol)); + } private string GenerateBuilderClassSourceCode(string? classNamespace, ImmutableArray types) { @@ -102,6 +111,9 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine(GENERATED_FILE_HEADER); stringBuilder.AppendLine(); + stringBuilder.AppendLine("using LightweightIocContainer.Interfaces.Factories;"); + stringBuilder.AppendLine("using LightweightIocContainer.Factories;"); + foreach (string typeNamespace in GetNamespacesOfTypes(types)) stringBuilder.AppendLine($"using {typeNamespace};"); @@ -113,9 +125,9 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine(); } - stringBuilder.AppendLine("public static class Builder"); + stringBuilder.AppendLine("public class FactoryBuilder : IFactoryBuilder"); stringBuilder.AppendLine("{"); - stringBuilder.AppendLine($"{INDENT}public static TFactory Create()"); + stringBuilder.AppendLine($"{INDENT}public TFactory Create(IocContainer container)"); stringBuilder.AppendLine($"{INDENT}{{"); foreach (ITypeSymbol? type in types) @@ -125,7 +137,7 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine($"{INDENT}{INDENT}if (typeof(TFactory) == typeof({type.Name}))"); stringBuilder.AppendLine($"{INDENT}{INDENT}{{"); - stringBuilder.AppendLine($"{INDENT}{INDENT}{INDENT}return (TFactory) (object) new Generated{type.Name}();"); + stringBuilder.AppendLine($"{INDENT}{INDENT}{INDENT}return (TFactory) (object) new Generated{type.Name}(container, new FactoryHelper());"); stringBuilder.AppendLine($"{INDENT}{INDENT}}}"); stringBuilder.AppendLine(); } From aea48017a77cfe3af2c3ebfceff044beebbe9186 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Tue, 2 Dec 2025 15:02:14 +0100 Subject: [PATCH 20/53] - don't need to use factoryHelper anymore --- LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index 3d63744..c2f9b0d 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -112,7 +112,6 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine(); stringBuilder.AppendLine("using LightweightIocContainer.Interfaces.Factories;"); - stringBuilder.AppendLine("using LightweightIocContainer.Factories;"); foreach (string typeNamespace in GetNamespacesOfTypes(types)) stringBuilder.AppendLine($"using {typeNamespace};"); @@ -137,7 +136,7 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine($"{INDENT}{INDENT}if (typeof(TFactory) == typeof({type.Name}))"); stringBuilder.AppendLine($"{INDENT}{INDENT}{{"); - stringBuilder.AppendLine($"{INDENT}{INDENT}{INDENT}return (TFactory) (object) new Generated{type.Name}(container, new FactoryHelper());"); + stringBuilder.AppendLine($"{INDENT}{INDENT}{INDENT}return (TFactory) (object) new Generated{type.Name}(container);"); stringBuilder.AppendLine($"{INDENT}{INDENT}}}"); stringBuilder.AppendLine(); } From 88e242ed20815b7644b32100ea70286135eb98c2 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Tue, 2 Dec 2025 15:11:22 +0100 Subject: [PATCH 21/53] - first implementation generating factory source code --- .../FactoryGenerator.cs | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index c2f9b0d..7ce3184 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -19,6 +19,8 @@ public class FactoryGenerator : IIncrementalGenerator private const string GENERATED_FILE_HEADER = "//---GENERATED by FactoryGenerator! DO NOT EDIT!---"; private const string INDENT = " "; + private const string CLEAR_MULTITON_INSTANCE_METHOD_NAME = "ClearMultitonInstance"; + public void Initialize(IncrementalGeneratorInitializationContext context) { string? classNamespace = typeof(FactoryGenerator).Namespace; @@ -27,6 +29,7 @@ public class FactoryGenerator : IIncrementalGenerator IncrementalValuesProvider syntaxProvider = context.SyntaxProvider.CreateSyntaxProvider(IsCallToGenerateFactory, GetTypeArgument); context.RegisterSourceOutput(syntaxProvider.Collect(), GenerateTypeDependentClasses); + context.RegisterSourceOutput(syntaxProvider, GenerateFactory); } private string GenerateFactoryExtensionsClass(string? classNamespace, string className) @@ -148,10 +151,131 @@ public class FactoryGenerator : IIncrementalGenerator return stringBuilder.ToString(); } + + private string GenerateFactorySourceCode(ITypeSymbol typeSymbol) + { + string typeName = typeSymbol.Name; + string? typeNamespace = typeSymbol.ContainingNamespace.IsGlobalNamespace ? null : typeSymbol.ContainingNamespace.ToString(); + + StringBuilder stringBuilder = new(); + + stringBuilder.AppendLine(GENERATED_FILE_HEADER); + stringBuilder.AppendLine(); + + stringBuilder.AppendLine("using LightweightIocContainer;"); + stringBuilder.AppendLine(); + + if (typeNamespace is not null) + { + stringBuilder.AppendLine($"namespace {typeNamespace};"); + stringBuilder.AppendLine(); + } + + stringBuilder.AppendLine($"public class Generated{typeName}(IocContainer container) : {typeName}"); + stringBuilder.AppendLine("{"); + + foreach (ISymbol? member in typeSymbol.GetMembers()) + { + if (member is not IMethodSymbol method) + continue; + + stringBuilder.AppendLine(); + + if (!method.ReturnsVoid) //create method + { + stringBuilder.Append($"{INDENT}public {method.ReturnType.Name} {method.Name}"); + + if (method.IsGenericMethod) + stringBuilder.Append($"<{string.Join(", ", method.TypeParameters.Select(p => p.Name))}>"); + + stringBuilder.Append($"({string.Join(", ", method.Parameters.Select(GetParameterText))})"); + + if (method.IsGenericMethod) + { + foreach (ITypeParameterSymbol typeParameter in method.TypeParameters) + { + List parameterConstraints = GetParameterConstraints(typeParameter); + if (parameterConstraints.Count == 0) + continue; + + stringBuilder.Append($" where {typeParameter.Name} : {string.Join(", ", parameterConstraints)}"); + } + } + + stringBuilder.AppendLine(); + stringBuilder.AppendLine($"{INDENT}{{"); + + foreach (IParameterSymbol parameter in method.Parameters) + { + stringBuilder.AppendLine($"{INDENT}{INDENT}object? {parameter.Name}Value = {parameter.Name}"); + stringBuilder.AppendLine($"{INDENT}{INDENT}if ({parameter.Name}Value is null)"); + stringBuilder.AppendLine($"{INDENT}{INDENT}{INDENT}{parameter.Name}Value = new NullParameter(typeof({parameter.Type.Name}));"); + stringBuilder.AppendLine(); + } + + if (method.IsAsync) + stringBuilder.Append($"{INDENT}{INDENT}return await container.ResolveAsync<>("); //TODO: Get return type from Task<> + else + stringBuilder.Append($"{INDENT}{INDENT}return container.Resolve<{method.ReturnType.Name}>("); + + stringBuilder.Append(string.Join(", ", method.Parameters.Select(p => $"{p.Name}Value"))); + stringBuilder.AppendLine(");"); + + stringBuilder.AppendLine($"{INDENT}}}"); + } + else if (method.Name == CLEAR_MULTITON_INSTANCE_METHOD_NAME) + { + //TODO + } + } + + typeSymbol.GetMembers(); + + stringBuilder.AppendLine("}"); + + return stringBuilder.ToString(); + } private IEnumerable GetNamespacesOfTypes(ImmutableArray types) => types.OfType() .Select(s => s.ContainingNamespace.IsGlobalNamespace ? null : s.ContainingNamespace.ToString()) .OfType() .Distinct(); + + private string GetParameterText(IParameterSymbol parameter) + { + StringBuilder stringBuilder = new(); + stringBuilder.Append(parameter.Type.Name); + + if (parameter.NullableAnnotation == NullableAnnotation.Annotated) + stringBuilder.Append("?"); + + stringBuilder.Append($" {parameter.Name}"); + return stringBuilder.ToString(); + } + + private List GetParameterConstraints(ITypeParameterSymbol typeParameterSymbol) + { + List constraints = []; + + foreach (ITypeSymbol constraintType in typeParameterSymbol.ConstraintTypes) + constraints.Add(constraintType.Name); + + if (typeParameterSymbol.HasReferenceTypeConstraint) + constraints.Add("class"); + + if (typeParameterSymbol.HasValueTypeConstraint) + constraints.Add("struct"); + + if (typeParameterSymbol.HasConstructorConstraint) + constraints.Add("new()"); + + if (typeParameterSymbol.HasUnmanagedTypeConstraint) + constraints.Add("unmanaged"); + + if (typeParameterSymbol.HasNotNullConstraint) + constraints.Add("notnull"); + + return constraints; + } } \ No newline at end of file From ae1ed6dd4cf575b549bd44b04124e60aceccee04 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 09:03:53 +0100 Subject: [PATCH 22/53] - enable nullable explicitly --- LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index 7ce3184..2d0b59d 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -161,6 +161,9 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine(GENERATED_FILE_HEADER); stringBuilder.AppendLine(); + + stringBuilder.AppendLine("#nullable enable"); + stringBuilder.AppendLine(); stringBuilder.AppendLine("using LightweightIocContainer;"); stringBuilder.AppendLine(); From 94c244a7747ab06a6e8de6e81601d5cc61c8de99 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 09:04:28 +0100 Subject: [PATCH 23/53] - add missing semicolon --- LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index 2d0b59d..b4fcd05 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -210,7 +210,7 @@ public class FactoryGenerator : IIncrementalGenerator foreach (IParameterSymbol parameter in method.Parameters) { - stringBuilder.AppendLine($"{INDENT}{INDENT}object? {parameter.Name}Value = {parameter.Name}"); + stringBuilder.AppendLine($"{INDENT}{INDENT}object? {parameter.Name}Value = {parameter.Name};"); stringBuilder.AppendLine($"{INDENT}{INDENT}if ({parameter.Name}Value is null)"); stringBuilder.AppendLine($"{INDENT}{INDENT}{INDENT}{parameter.Name}Value = new NullParameter(typeof({parameter.Type.Name}));"); stringBuilder.AppendLine(); From 79642012e67893aa4a86e86de39f4eb741bf7d34 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 09:04:58 +0100 Subject: [PATCH 24/53] - add methods to test more factory types --- .../SampleClass.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs b/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs index 57ee8cf..7b4b4f7 100644 --- a/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs +++ b/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs @@ -10,4 +10,10 @@ public interface ISampleClass; public interface ISampleClassFactory { ISampleClass Create(); + T Create() where T : ISampleClass; + Task CreateAsync(); + + ISampleClass Create(string name); + T Create(U param1, V param2) where T : ISampleClass where U : class, new() where V : struct; + Task CreateAsync(U parameter) where T : ISampleClass where U : class; } \ No newline at end of file From 1a3cb30e82ea9b4c145f3127da8baff0750709bd Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 09:24:24 +0100 Subject: [PATCH 25/53] - remove duplicate semicolon --- LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index b4fcd05..bf460a5 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -49,7 +49,7 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine(); } - stringBuilder.AppendLine($"public static class {className}");; + stringBuilder.AppendLine($"public static class {className}"); stringBuilder.AppendLine("{"); stringBuilder.AppendLine($"{INDENT}public static IRegistrationBase WithGeneratedFactory(this IRegistrationBase registration)"); From f0b59b84b7a5351416156365b8dc2ac0e6efe257 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 10:11:19 +0100 Subject: [PATCH 26/53] - use correct types for async resolve --- .../FactoryGenerator.cs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index bf460a5..125cffd 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -186,7 +186,16 @@ public class FactoryGenerator : IIncrementalGenerator if (!method.ReturnsVoid) //create method { - stringBuilder.Append($"{INDENT}public {method.ReturnType.Name} {method.Name}"); + stringBuilder.Append($"{INDENT}public "); + stringBuilder.Append(method.ReturnType.Name); + + if (method.ReturnType.Name == "Task") + { + if (method.ReturnType is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) + stringBuilder.Append($"<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(a => a.Name))}>"); + } + + stringBuilder.Append($" {method.Name}"); if (method.IsGenericMethod) stringBuilder.Append($"<{string.Join(", ", method.TypeParameters.Select(p => p.Name))}>"); @@ -216,8 +225,15 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine(); } - if (method.IsAsync) - stringBuilder.Append($"{INDENT}{INDENT}return await container.ResolveAsync<>("); //TODO: Get return type from Task<> + if (method.ReturnType.Name == "Task") + { + stringBuilder.Append($"{INDENT}{INDENT}return container.ResolveAsync"); + + if (method.ReturnType is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) + stringBuilder.Append($"<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(a => a.Name))}>"); + + stringBuilder.Append("("); + } else stringBuilder.Append($"{INDENT}{INDENT}return container.Resolve<{method.ReturnType.Name}>("); From de0e6b236384036531f0b94e367953d504af6dde Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 10:33:10 +0100 Subject: [PATCH 27/53] - remove leftover line --- LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index 125cffd..9e8c941 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -248,8 +248,6 @@ public class FactoryGenerator : IIncrementalGenerator } } - typeSymbol.GetMembers(); - stringBuilder.AppendLine("}"); return stringBuilder.ToString(); From 2bfec513e8fab307a28ad8e3ef2a7e2b571f5579 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 10:47:45 +0100 Subject: [PATCH 28/53] - combine appends --- LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index 9e8c941..8731cdf 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -186,8 +186,7 @@ public class FactoryGenerator : IIncrementalGenerator if (!method.ReturnsVoid) //create method { - stringBuilder.Append($"{INDENT}public "); - stringBuilder.Append(method.ReturnType.Name); + stringBuilder.Append($"{INDENT}public {method.ReturnType.Name}"); if (method.ReturnType.Name == "Task") { From ec4d0bcb676158b5b1e91d266e2ecdf3c67564b1 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 10:48:06 +0100 Subject: [PATCH 29/53] - add clearMultitonInstance method to sample --- Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs b/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs index 7b4b4f7..3575aff 100644 --- a/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs +++ b/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs @@ -16,4 +16,6 @@ public interface ISampleClassFactory ISampleClass Create(string name); T Create(U param1, V param2) where T : ISampleClass where U : class, new() where V : struct; Task CreateAsync(U parameter) where T : ISampleClass where U : class; + + void ClearMultitonInstance() where T : ISampleClass; } \ No newline at end of file From dfe0b5713c2821a31b7b1245fbd1d4247a44ae29 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 11:02:24 +0100 Subject: [PATCH 30/53] - implement clear multiton instance method --- .../FactoryGenerator.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index 8731cdf..4bbab00 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -241,9 +241,20 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine($"{INDENT}}}"); } - else if (method.Name == CLEAR_MULTITON_INSTANCE_METHOD_NAME) + else if (method is { Name: CLEAR_MULTITON_INSTANCE_METHOD_NAME, IsGenericMethod: true }) { - //TODO + stringBuilder.Append($"{INDENT}public void {method.Name}<{string.Join(", ", method.TypeParameters.Select(p => p.Name))}>()"); + + foreach (ITypeParameterSymbol typeParameter in method.TypeParameters) + { + List parameterConstraints = GetParameterConstraints(typeParameter); + if (parameterConstraints.Count == 0) + continue; + + stringBuilder.Append($" where {typeParameter.Name} : {string.Join(", ", parameterConstraints)}"); + } + + stringBuilder.AppendLine($" => container.ClearMultitonInstances<{string.Join(", ", method.TypeArguments.Select(a => a.Name))}>();"); } } From 2b6abe5e1f1950cb34947b57adf55cd91ca74040 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 11:05:31 +0100 Subject: [PATCH 31/53] - only append empty line if not last member --- .../FactoryGenerator.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index 4bbab00..71fd88c 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -177,13 +177,12 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine($"public class Generated{typeName}(IocContainer container) : {typeName}"); stringBuilder.AppendLine("{"); - foreach (ISymbol? member in typeSymbol.GetMembers()) + ImmutableArray members = typeSymbol.GetMembers(); + foreach (ISymbol? member in members) { if (member is not IMethodSymbol method) continue; - stringBuilder.AppendLine(); - if (!method.ReturnsVoid) //create method { stringBuilder.Append($"{INDENT}public {method.ReturnType.Name}"); @@ -256,6 +255,9 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine($" => container.ClearMultitonInstances<{string.Join(", ", method.TypeArguments.Select(a => a.Name))}>();"); } + + if (members.IndexOf(member) < members.Length - 1) //only append empty line if not the last member + stringBuilder.AppendLine(); } stringBuilder.AppendLine("}"); From 0a006ae787398b371150efeaccbaac1e538d9706 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 11:49:03 +0100 Subject: [PATCH 32/53] - update readme --- LightweightIocContainer.FactoryGenerator/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/LightweightIocContainer.FactoryGenerator/README.md b/LightweightIocContainer.FactoryGenerator/README.md index e9617c7..c02edb9 100644 --- a/LightweightIocContainer.FactoryGenerator/README.md +++ b/LightweightIocContainer.FactoryGenerator/README.md @@ -7,3 +7,17 @@ The easiest way to [install](https://github.com/SimonG96/LghtweightIocContainer/ ```.net > dotnet add package LightweightIocContainer.FactoryGenerator --version 5.0.0 ``` + +### Example usage + +When registering an interface you can use the `WithGeneratedFactory<>()` method to generate a factory for the registered type: + +```c# +public class SampleInstaller : IIocInstaller +{ + public void Install(IRegistrationCollector registration) + { + registration.Add().WithGeneratedFactory(); + } +} +``` From b5f16126fea92ab65ddeb56c3eb3c0d0949bf2c9 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 13:41:56 +0100 Subject: [PATCH 33/53] - don't generate a file twice for the same factory --- .../FactoryGenerator.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index 71fd88c..a0101bc 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -29,7 +29,7 @@ public class FactoryGenerator : IIncrementalGenerator IncrementalValuesProvider syntaxProvider = context.SyntaxProvider.CreateSyntaxProvider(IsCallToGenerateFactory, GetTypeArgument); context.RegisterSourceOutput(syntaxProvider.Collect(), GenerateTypeDependentClasses); - context.RegisterSourceOutput(syntaxProvider, GenerateFactory); + context.RegisterSourceOutput(syntaxProvider.Collect(), GenerateFactory); } private string GenerateFactoryExtensionsClass(string? classNamespace, string className) @@ -99,12 +99,15 @@ public class FactoryGenerator : IIncrementalGenerator context.AddSource($"{BUILDER_CLASS_NAME}.g.cs", GenerateBuilderClassSourceCode(classNamespace, types)); } - private void GenerateFactory(SourceProductionContext context, ITypeSymbol? typeSymbol) + private void GenerateFactory(SourceProductionContext context, ImmutableArray types) { - if (typeSymbol is null) - return; + foreach (ISymbol? symbol in types.Distinct(SymbolEqualityComparer.IncludeNullability)) + { + if (symbol is not ITypeSymbol typeSymbol) + continue; - context.AddSource($"Generated{typeSymbol.Name}.g.cs", GenerateFactorySourceCode(typeSymbol)); + context.AddSource($"Generated{typeSymbol.Name}.g.cs", GenerateFactorySourceCode(typeSymbol)); + } } private string GenerateBuilderClassSourceCode(string? classNamespace, ImmutableArray types) @@ -132,9 +135,9 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine($"{INDENT}public TFactory Create(IocContainer container)"); stringBuilder.AppendLine($"{INDENT}{{"); - foreach (ITypeSymbol? type in types) + foreach (ISymbol? symbol in types.Distinct(SymbolEqualityComparer.IncludeNullability)) { - if (type is null) + if (symbol is not ITypeSymbol type) continue; stringBuilder.AppendLine($"{INDENT}{INDENT}if (typeof(TFactory) == typeof({type.Name}))"); From cf953729263d51f2154fe4fcad1530ceae2e14ae Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 13:53:27 +0100 Subject: [PATCH 34/53] - update nuget packages --- .../Test.LightweightIocContainer.Validation.csproj | 2 +- .../Test.LightweightIocContainer.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Test.LightweightIocContainer.Validation/Test.LightweightIocContainer.Validation.csproj b/Test.LightweightIocContainer.Validation/Test.LightweightIocContainer.Validation.csproj index 9af3782..684e40c 100644 --- a/Test.LightweightIocContainer.Validation/Test.LightweightIocContainer.Validation.csproj +++ b/Test.LightweightIocContainer.Validation/Test.LightweightIocContainer.Validation.csproj @@ -13,7 +13,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Test.LightweightIocContainer/Test.LightweightIocContainer.csproj b/Test.LightweightIocContainer/Test.LightweightIocContainer.csproj index ddbde95..da69d46 100644 --- a/Test.LightweightIocContainer/Test.LightweightIocContainer.csproj +++ b/Test.LightweightIocContainer/Test.LightweightIocContainer.csproj @@ -13,7 +13,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From ba922066a1d9d56b36511e76ae688306c209a880 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 13:53:43 +0100 Subject: [PATCH 35/53] - add test lib for generator --- LightweightIocContainer.sln | 6 ++++ ...weightIocContainer.FactoryGenerator.csproj | 33 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 Test.LightweightIocContainer.FactoryGenerator/Test.LightweightIocContainer.FactoryGenerator.csproj diff --git a/LightweightIocContainer.sln b/LightweightIocContainer.sln index e44123e..df30de0 100644 --- a/LightweightIocContainer.sln +++ b/LightweightIocContainer.sln @@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightweightIocContainer.Fac EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debug.LightweightIocContainer.FactoryGenerator", "Debug.LightweightIocContainer.FactoryGenerator\Debug.LightweightIocContainer.FactoryGenerator.csproj", "{578ED8B2-A2BE-4E10-9422-4D5AF2E605E0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.LightweightIocContainer.FactoryGenerator", "Test.LightweightIocContainer.FactoryGenerator\Test.LightweightIocContainer.FactoryGenerator.csproj", "{0CB46F2E-5429-40BB-9F9F-31606B212E81}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,10 @@ Global {578ED8B2-A2BE-4E10-9422-4D5AF2E605E0}.Debug|Any CPU.Build.0 = Debug|Any CPU {578ED8B2-A2BE-4E10-9422-4D5AF2E605E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {578ED8B2-A2BE-4E10-9422-4D5AF2E605E0}.Release|Any CPU.Build.0 = Release|Any CPU + {0CB46F2E-5429-40BB-9F9F-31606B212E81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0CB46F2E-5429-40BB-9F9F-31606B212E81}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CB46F2E-5429-40BB-9F9F-31606B212E81}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0CB46F2E-5429-40BB-9F9F-31606B212E81}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Test.LightweightIocContainer.FactoryGenerator/Test.LightweightIocContainer.FactoryGenerator.csproj b/Test.LightweightIocContainer.FactoryGenerator/Test.LightweightIocContainer.FactoryGenerator.csproj new file mode 100644 index 0000000..a5be96c --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/Test.LightweightIocContainer.FactoryGenerator.csproj @@ -0,0 +1,33 @@ + + + + net10.0 + latest + enable + enable + false + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + \ No newline at end of file From 40e026042800ade7c05bba79d89512308f502612 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 14:06:51 +0100 Subject: [PATCH 36/53] - move files to folders to test correct generation of usings --- .../SampleInstaller.cs | 3 +++ .../Factories/ISampleClassFactory.cs} | 9 ++------- .../Samples/Interfaces/ISampleClass.cs | 3 +++ .../Samples/SampleClass.cs | 9 +++++++++ 4 files changed, 17 insertions(+), 7 deletions(-) rename Debug.LightweightIocContainer.FactoryGenerator/{SampleClass.cs => Samples/Factories/ISampleClassFactory.cs} (65%) create mode 100644 Debug.LightweightIocContainer.FactoryGenerator/Samples/Interfaces/ISampleClass.cs create mode 100644 Debug.LightweightIocContainer.FactoryGenerator/Samples/SampleClass.cs diff --git a/Debug.LightweightIocContainer.FactoryGenerator/SampleInstaller.cs b/Debug.LightweightIocContainer.FactoryGenerator/SampleInstaller.cs index 3f63134..dd14b13 100644 --- a/Debug.LightweightIocContainer.FactoryGenerator/SampleInstaller.cs +++ b/Debug.LightweightIocContainer.FactoryGenerator/SampleInstaller.cs @@ -2,6 +2,9 @@ // Created: 2025-12-01 // Copyright(c) 2025 SimonG. All Rights Reserved. +using Debug.LightweightIocContainer.FactoryGenerator.Samples; +using Debug.LightweightIocContainer.FactoryGenerator.Samples.Factories; +using Debug.LightweightIocContainer.FactoryGenerator.Samples.Interfaces; using LightweightIocContainer.FactoryGenerator; using LightweightIocContainer.Interfaces.Installers; using LightweightIocContainer.Interfaces.Registrations; diff --git a/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs b/Debug.LightweightIocContainer.FactoryGenerator/Samples/Factories/ISampleClassFactory.cs similarity index 65% rename from Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs rename to Debug.LightweightIocContainer.FactoryGenerator/Samples/Factories/ISampleClassFactory.cs index 3575aff..37356a4 100644 --- a/Debug.LightweightIocContainer.FactoryGenerator/SampleClass.cs +++ b/Debug.LightweightIocContainer.FactoryGenerator/Samples/Factories/ISampleClassFactory.cs @@ -1,11 +1,6 @@ -// Author: Simon.Gockner -// Created: 2025-12-01 -// Copyright(c) 2025 SimonG. All Rights Reserved. +using Debug.LightweightIocContainer.FactoryGenerator.Samples.Interfaces; -namespace Debug.LightweightIocContainer.FactoryGenerator; - -public class SampleClass : ISampleClass; -public interface ISampleClass; +namespace Debug.LightweightIocContainer.FactoryGenerator.Samples.Factories; public interface ISampleClassFactory { diff --git a/Debug.LightweightIocContainer.FactoryGenerator/Samples/Interfaces/ISampleClass.cs b/Debug.LightweightIocContainer.FactoryGenerator/Samples/Interfaces/ISampleClass.cs new file mode 100644 index 0000000..c9c7c62 --- /dev/null +++ b/Debug.LightweightIocContainer.FactoryGenerator/Samples/Interfaces/ISampleClass.cs @@ -0,0 +1,3 @@ +namespace Debug.LightweightIocContainer.FactoryGenerator.Samples.Interfaces; + +public interface ISampleClass; \ No newline at end of file diff --git a/Debug.LightweightIocContainer.FactoryGenerator/Samples/SampleClass.cs b/Debug.LightweightIocContainer.FactoryGenerator/Samples/SampleClass.cs new file mode 100644 index 0000000..c2b1429 --- /dev/null +++ b/Debug.LightweightIocContainer.FactoryGenerator/Samples/SampleClass.cs @@ -0,0 +1,9 @@ +// Author: Simon.Gockner +// Created: 2025-12-01 +// Copyright(c) 2025 SimonG. All Rights Reserved. + +using Debug.LightweightIocContainer.FactoryGenerator.Samples.Interfaces; + +namespace Debug.LightweightIocContainer.FactoryGenerator.Samples; + +public class SampleClass : ISampleClass; \ No newline at end of file From 07538852f2bc7c57311a008f369e5edfc8f24bc6 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 14:32:09 +0100 Subject: [PATCH 37/53] - add usings for returnValues and parameters --- .../FactoryGenerator.cs | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index a0101bc..85c3214 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -169,6 +169,32 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine(); stringBuilder.AppendLine("using LightweightIocContainer;"); + + ImmutableArray members = typeSymbol.GetMembers(); + + List namespaces = []; + foreach (ISymbol? member in members) + { + if (member is not IMethodSymbol method) + continue; + + if (!method.ReturnsVoid) + { + if (method.ReturnType.Name == "Task") + { + if (method.ReturnType is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) + namespaces.AddRange(namedTypeSymbol.TypeArguments.Select(GetNamespaceOfType)); + } + else + namespaces.Add(GetNamespaceOfType(method.ReturnType)); + } + + namespaces.AddRange(method.Parameters.Select(p => GetNamespaceOfType(p.Type))); + } + + foreach (string @namespace in namespaces.Distinct().OfType().OrderBy(n => n)) + stringBuilder.AppendLine($"using {@namespace};"); + stringBuilder.AppendLine(); if (typeNamespace is not null) @@ -180,7 +206,6 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine($"public class Generated{typeName}(IocContainer container) : {typeName}"); stringBuilder.AppendLine("{"); - ImmutableArray members = typeSymbol.GetMembers(); foreach (ISymbol? member in members) { if (member is not IMethodSymbol method) @@ -268,9 +293,10 @@ public class FactoryGenerator : IIncrementalGenerator return stringBuilder.ToString(); } + private string? GetNamespaceOfType(ITypeSymbol s) => s.ContainingNamespace.IsGlobalNamespace ? null : s.ContainingNamespace.ToString(); private IEnumerable GetNamespacesOfTypes(ImmutableArray types) => types.OfType() - .Select(s => s.ContainingNamespace.IsGlobalNamespace ? null : s.ContainingNamespace.ToString()) + .Select(GetNamespaceOfType) .OfType() .Distinct(); From 01ee6324def709f646e0f63a78cb98d0b9c2d4a9 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 14:34:57 +0100 Subject: [PATCH 38/53] - make internals visible to new test lib --- LightweightIocContainer/Properties/AssemblyInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/LightweightIocContainer/Properties/AssemblyInfo.cs b/LightweightIocContainer/Properties/AssemblyInfo.cs index 79de6d3..b94a037 100644 --- a/LightweightIocContainer/Properties/AssemblyInfo.cs +++ b/LightweightIocContainer/Properties/AssemblyInfo.cs @@ -7,4 +7,5 @@ using System.Runtime.CompilerServices; [assembly:InternalsVisibleTo("LightweightIocContainer.Validation")] [assembly:InternalsVisibleTo("Test.LightweightIocContainer")] +[assembly:InternalsVisibleTo("Test.LightweightIocContainer.FactoryGenerator")] [assembly:InternalsVisibleTo("Test.LightweightIocContainer.Validation")] From 8d9777a9547a3d014297acb52d607402cefb4013 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 15:01:18 +0100 Subject: [PATCH 39/53] - add all existing test cases that use current factory implementation and substitute it with generated factories --- .../AsyncFactoryTest.cs | 60 +++++ .../FluentFactoryRegistrationTest.cs | 212 ++++++++++++++++++ .../MultiLayerResolveTest.cs | 40 ++++ .../OpenGenericRegistrationTest.cs | 41 ++++ .../TestClasses/A.cs | 10 + .../TestClasses/AsyncMultitonTest.cs | 13 ++ .../TestClasses/AsyncTest.cs | 14 ++ .../TestClasses/B.cs | 10 + .../TestClasses/C.cs | 9 + .../TestClasses/Constraint.cs | 9 + .../TestClasses/CtorGenericTest.cs | 11 + .../TestClasses/DefaultTest.cs | 5 + .../TestClasses/Factories/IAFactory.cs | 10 + .../Factories/IAsyncMultitonTestFactory.cs | 8 + .../Factories/IAsyncTestFactory.cs | 8 + .../TestClasses/Factories/IBFactory.cs | 9 + .../Factories/ICtorGenericTestFactory.cs | 8 + .../Factories/IDefaultTestFactory.cs | 11 + .../Factories/IGenericTestFactory.cs | 8 + .../Factories/IInvalidMultitonTestFactory.cs | 9 + .../Factories/IMultitonTestFactory.cs | 9 + .../Factories/ITestDefaultFactory.cs | 8 + .../Factories/ITestFactoryNoCreate.cs | 3 + .../Factories/ITestFactoryWrongReturn.cs | 6 + .../TestClasses/Factories/ITestNullFactory.cs | 8 + .../TestClasses/GenericTest.cs | 5 + .../TestClasses/Interfaces/IA.cs | 6 + .../TestClasses/Interfaces/IAsyncTest.cs | 7 + .../TestClasses/Interfaces/IB.cs | 6 + .../TestClasses/Interfaces/IConstraint.cs | 3 + .../TestClasses/Interfaces/IDefaultTest.cs | 3 + .../TestClasses/Interfaces/IGenericTest.cs | 3 + .../TestClasses/MultitonScope.cs | 3 + .../TestClasses/TestByte.cs | 12 + .../TestClasses/TestConstructor.cs | 16 ++ .../TestClasses/TestMultiton.cs | 11 + .../TestClasses/TestNull.cs | 23 ++ 37 files changed, 637 insertions(+) create mode 100644 Test.LightweightIocContainer.FactoryGenerator/AsyncFactoryTest.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/FluentFactoryRegistrationTest.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/MultiLayerResolveTest.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/OpenGenericRegistrationTest.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/A.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/AsyncMultitonTest.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/AsyncTest.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/B.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/C.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Constraint.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/CtorGenericTest.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/DefaultTest.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAFactory.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAsyncMultitonTestFactory.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAsyncTestFactory.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IBFactory.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ICtorGenericTestFactory.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IDefaultTestFactory.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IGenericTestFactory.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IInvalidMultitonTestFactory.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IMultitonTestFactory.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestDefaultFactory.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestFactoryNoCreate.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestFactoryWrongReturn.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestNullFactory.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/GenericTest.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IA.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IAsyncTest.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IB.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IConstraint.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IDefaultTest.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IGenericTest.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/MultitonScope.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestByte.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestConstructor.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestMultiton.cs create mode 100644 Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestNull.cs diff --git a/Test.LightweightIocContainer.FactoryGenerator/AsyncFactoryTest.cs b/Test.LightweightIocContainer.FactoryGenerator/AsyncFactoryTest.cs new file mode 100644 index 0000000..41347dd --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/AsyncFactoryTest.cs @@ -0,0 +1,60 @@ +// Author: Simon.Gockner +// Created: 2025-12-03 +// Copyright(c) 2025 SimonG. All Rights Reserved. + +using LightweightIocContainer; +using LightweightIocContainer.FactoryGenerator; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator; + +[TestFixture] +public class AsyncFactoryTest +{ + [Test] + public async Task TestAsyncFactoryResolve() + { + IocContainer container = new(); + container.Register(r => r.Add().WithGeneratedFactory()); + + IAsyncTestFactory testFactory = container.Resolve(); + IAsyncTest test = await testFactory.Create(); + + Assert.That(test, Is.InstanceOf()); + } + + [Test] + public async Task TestAsyncFactoryResolveOnCreateCalled() + { + IocContainer container = new(); + container.Register(r => r.Add().OnCreateAsync(t => t.Initialize()).WithGeneratedFactory()); + + IAsyncTestFactory testFactory = container.Resolve(); + IAsyncTest test = await testFactory.Create(); + + Assert.That(test, Is.InstanceOf()); + Assert.That(test.IsInitialized, Is.True); + } + + [Test] + public async Task TestAsyncMultitonFactoryResolveOnCreateCalledCorrectly() + { + IocContainer container = new(); + container.Register(r => r.AddMultiton().OnCreateAsync(t => t.Initialize()).WithGeneratedFactory()); + + IAsyncMultitonTestFactory testFactory = container.Resolve(); + IAsyncTest test1 = await testFactory.Create(1); + IAsyncTest test2 = await testFactory.Create(2); + IAsyncTest anotherTest1 = await testFactory.Create(1); + + Assert.That(test1, Is.InstanceOf()); + Assert.That(test1.IsInitialized, Is.True); + + Assert.That(test2, Is.InstanceOf()); + Assert.That(test2.IsInitialized, Is.True); + + Assert.That(test1, Is.SameAs(anotherTest1)); + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/FluentFactoryRegistrationTest.cs b/Test.LightweightIocContainer.FactoryGenerator/FluentFactoryRegistrationTest.cs new file mode 100644 index 0000000..dc702a1 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/FluentFactoryRegistrationTest.cs @@ -0,0 +1,212 @@ +// Author: Simon.Gockner +// Created: 2025-12-03 +// Copyright(c) 2025 SimonG. All Rights Reserved. + +using LightweightIocContainer; +using LightweightIocContainer.Exceptions; +using LightweightIocContainer.FactoryGenerator; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator; + +[TestFixture] +public class FluentFactoryRegistrationTest +{ + private IocContainer _iocContainer; + + [SetUp] + public void SetUp() => _iocContainer = new IocContainer(); + + [TearDown] + public void TearDown() => _iocContainer.Dispose(); + + [Test] + public void TestFluentFactoryRegistration() + { + _iocContainer.Register(r => r.Add().WithGeneratedFactory()); + + IDefaultTestFactory factory = _iocContainer.Resolve(); + IDefaultTest test = factory.Create(); + + Assert.That(factory, Is.InstanceOf()); + Assert.That(test, Is.InstanceOf()); + } + + [Test] + public void TestFluentFactoryRegistrationResolveWithoutFactoryFails() + { + _iocContainer.Register(r => r.Add().WithGeneratedFactory()); + Assert.Throws(()=>_iocContainer.Resolve()); + } + + [Test] + public void TestRegisterFactoryWithoutCreate() => Assert.Throws(() => _iocContainer.Register(r => r.Add().WithGeneratedFactory())); + + [Test] + public void TestResolveFromFactory() + { + _iocContainer.Register(r => r.Add().WithGeneratedFactory()); + + IDefaultTestFactory testFactory = _iocContainer.Resolve(); + IDefaultTest createdTest = testFactory.Create(); + + Assert.That(createdTest, Is.InstanceOf()); + } + + [Test] + public void TestResolveFromFactoryWithParams() + { + _iocContainer.Register(r => r.Add().WithGeneratedFactory()); + _iocContainer.Register(r => r.Add()); //this registration is abnormal and should only be used in unit tests + + IDefaultTestFactory testFactory = _iocContainer.Resolve(); + IDefaultTest createdTest = testFactory.Create("Test"); + + Assert.That(createdTest, Is.InstanceOf()); + } + + [Test] + public void TestResolveFromFactoryWithDefaultParamCreate() + { + _iocContainer.Register(r => r.Add().WithGeneratedFactory()); + _iocContainer.Register(r => r.Add()); //this registration is abnormal and should only be used in unit tests + + IDefaultTestFactory testFactory = _iocContainer.Resolve(); + IDefaultTest createdTest = testFactory.CreateTest(); + + Assert.That(createdTest, Is.InstanceOf()); + } + + [Test] + public void TestResolveFromFactoryWithDefaultParamCtor() + { + _iocContainer.Register(r => r.Add().WithGeneratedFactory()); + _iocContainer.Register(r => r.Add()); //this registration is abnormal and should only be used in unit tests + + IDefaultTestFactory testFactory = _iocContainer.Resolve(); + IDefaultTest createdTest = testFactory.Create(); + + Assert.That(createdTest, Is.InstanceOf()); + } + + [Test] + public void TestResolveFromFactoryWithByte() + { + _iocContainer.Register(r => r.Add().WithGeneratedFactory()); + + IDefaultTestFactory testFactory = _iocContainer.Resolve(); + IDefaultTest createdTest = testFactory.Create(1); + + Assert.That(createdTest, Is.InstanceOf()); + } + + [Test] + public void TestPassingNullAsMiddleParameter() + { + _iocContainer.Register(r => r.Add().WithGeneratedFactory()); + + ITestNullFactory testNullFactory = _iocContainer.Resolve(); + + object obj = new(); + string content = "TestContent"; + string optional2 = "optionalParameter2"; + + IDefaultTest createdTest = testNullFactory.Create(obj, content, null!, optional2); + if (createdTest is not TestNull testNull) + { + Assert.Fail(); + return; + } + + Assert.That(testNull.Obj, Is.SameAs(obj)); + Assert.That(testNull.Content, Is.EqualTo(content)); + Assert.That(testNull.Optional1, Is.Null); + Assert.That(testNull.Optional2, Is.EqualTo(optional2)); + } + + [Test] + public void TestPassingNullAsDefaultParameter() + { + _iocContainer.Register(r => r.Add().WithGeneratedFactory()); + + ITestDefaultFactory testDefaultFactory = _iocContainer.Resolve(); + + object obj = new(); + string content = "TestContent"; + string optional2 = "optionalParameter2"; + + IDefaultTest createdTest = testDefaultFactory.Create(obj, content, null!, optional2); + if (createdTest is not TestNull testNull) + { + Assert.Fail(); + return; + } + + Assert.That(testNull.Obj, Is.SameAs(obj)); + Assert.That(testNull.Content, Is.EqualTo(content)); + Assert.That(testNull.Optional1, Is.Null); + Assert.That(testNull.Optional2, Is.EqualTo(optional2)); + Assert.That(testNull.TestNullFactory, Is.Null); + } + + [Test] + public void TestResolveMultitonFromFactory() + { + _iocContainer.Register(r => r.AddMultiton().WithGeneratedFactory()); + + MultitonScope scope1 = new(); + MultitonScope scope2 = new(); + + IMultitonTestFactory testFactory = _iocContainer.Resolve(); + + IDefaultTest resolvedTest1 = testFactory.Create(scope1); + IDefaultTest resolvedTest2 = testFactory.Create(scope1); + IDefaultTest resolvedTest3 = testFactory.Create(scope2); + + Assert.That(resolvedTest1, Is.SameAs(resolvedTest2)); + Assert.That(resolvedTest1, Is.Not.SameAs(resolvedTest3)); + Assert.That(resolvedTest2, Is.Not.SameAs(resolvedTest3)); + } + + [Test] + public void TestResolveMultitonFromFactoryClearInstances() + { + _iocContainer.Register(r => r.AddMultiton().WithGeneratedFactory()); + + MultitonScope scope1 = new(); + MultitonScope scope2 = new(); + + IMultitonTestFactory testFactory = _iocContainer.Resolve(); + + IDefaultTest resolvedTest1 = testFactory.Create(scope1); + IDefaultTest resolvedTest2 = testFactory.Create(scope1); + IDefaultTest resolvedTest3 = testFactory.Create(scope2); + + Assert.That(resolvedTest1, Is.SameAs(resolvedTest2)); + Assert.That(resolvedTest1, Is.Not.SameAs(resolvedTest3)); + Assert.That(resolvedTest2, Is.Not.SameAs(resolvedTest3)); + + testFactory.ClearMultitonInstance(); + + IDefaultTest resolvedTest4 = testFactory.Create(scope1); + IDefaultTest resolvedTest5 = testFactory.Create(scope2); + + Assert.That(resolvedTest1, Is.Not.SameAs(resolvedTest4)); + Assert.That(resolvedTest2, Is.Not.SameAs(resolvedTest4)); + Assert.That(resolvedTest3, Is.Not.SameAs(resolvedTest5)); + } + + [Test] + public void InvalidMultitonFactoryRegistrationFactoryWithoutParameter() => + Assert.Throws(() => _iocContainer.Register(r => r.AddMultiton().WithGeneratedFactory())); + + [Test] + public void InvalidMultitonFactoryRegistrationFactoryWithoutScopeAsFirstParameter() => + Assert.Throws(() => _iocContainer.Register(r => r.AddMultiton().WithGeneratedFactory())); + + [Test] + public void TestInvalidCreateMethodReturnType() => + Assert.Throws(() => _iocContainer.Register(r => r.Add().WithGeneratedFactory())); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/MultiLayerResolveTest.cs b/Test.LightweightIocContainer.FactoryGenerator/MultiLayerResolveTest.cs new file mode 100644 index 0000000..ad22459 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/MultiLayerResolveTest.cs @@ -0,0 +1,40 @@ +// Author: Simon.Gockner +// Created: 2025-12-03 +// Copyright(c) 2025 SimonG. All Rights Reserved. + +using LightweightIocContainer; +using LightweightIocContainer.FactoryGenerator; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator; + +[TestFixture] +public class MultiLayerResolveTest +{ + [Test] + public void TestResolveFactoryAsCtorParameter() + { + IocContainer container = new(); + container.Register(r => r.Add().WithGeneratedFactory()); + container.Register(r => r.Add().WithGeneratedFactory()); + + IAFactory aFactory = container.Resolve(); + IA a = aFactory.Create(); + Assert.That(a, Is.InstanceOf()); + } + + [Test] + public void TestResolveSingleTypeRegistrationAsCtorParameter() + { + IocContainer container = new(); + container.Register(r => r.Add()); + container.Register(r => r.Add().WithGeneratedFactory()); + container.Register(r => r.Add().WithFactoryMethod(_ => new C("test"))); + + IBFactory bFactory = container.Resolve(); + IB b = bFactory.Create(); + Assert.That(b, Is.InstanceOf()); + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/OpenGenericRegistrationTest.cs b/Test.LightweightIocContainer.FactoryGenerator/OpenGenericRegistrationTest.cs new file mode 100644 index 0000000..0bbc68c --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/OpenGenericRegistrationTest.cs @@ -0,0 +1,41 @@ +// Author: Simon.Gockner +// Created: 2025-12-03 +// Copyright(c) 2025 SimonG. All Rights Reserved. + +using LightweightIocContainer; +using LightweightIocContainer.FactoryGenerator; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator; + +[TestFixture] +public class OpenGenericRegistrationTest +{ + private IocContainer _iocContainer; + + [SetUp] + public void SetUp() => _iocContainer = new IocContainer(); + + [TearDown] + public void TearDown() => _iocContainer.Dispose(); + + [Test] + public void TestRegisterFactoryOfOpenGenericType() + { + _iocContainer.Register(r => r.AddOpenGenerics(typeof(IGenericTest<>), typeof(GenericTest<>)).WithGeneratedFactory()); + IGenericTestFactory testFactory = _iocContainer.Resolve(); + IGenericTest test = testFactory.Create(); + Assert.That(test, Is.InstanceOf>()); + } + + [Test] + public void TestRegisterFactoryOfOpenGenericTypeWithCtorParameter() + { + _iocContainer.Register(r => r.AddOpenGenerics(typeof(IGenericTest<>), typeof(CtorGenericTest<>)).WithGeneratedFactory()); + ICtorGenericTestFactory testFactory = _iocContainer.Resolve(); + IGenericTest test = testFactory.Create(new Constraint()); + Assert.That(test, Is.InstanceOf>()); + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/A.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/A.cs new file mode 100644 index 0000000..257c818 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/A.cs @@ -0,0 +1,10 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class A : IA +{ + public A(IBFactory bFactory) => BProperty = bFactory.Create(new C("from A")); + public IB BProperty { get; } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/AsyncMultitonTest.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/AsyncMultitonTest.cs new file mode 100644 index 0000000..f348057 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/AsyncMultitonTest.cs @@ -0,0 +1,13 @@ +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class AsyncMultitonTest(int id) : AsyncTest +{ + public int Id { get; } = id; + public override async Task Initialize() + { + if (IsInitialized) + throw new Exception(); + + await base.Initialize(); + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/AsyncTest.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/AsyncTest.cs new file mode 100644 index 0000000..db49384 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/AsyncTest.cs @@ -0,0 +1,14 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class AsyncTest : IAsyncTest +{ + public bool IsInitialized { get; private set; } + + public virtual async Task Initialize() + { + await Task.Delay(200); + IsInitialized = true; + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/B.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/B.cs new file mode 100644 index 0000000..ea3d980 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/B.cs @@ -0,0 +1,10 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class B : IB +{ + public B(C c) => C = c; + + public C C { get; } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/C.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/C.cs new file mode 100644 index 0000000..0c30db7 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/C.cs @@ -0,0 +1,9 @@ +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class C +{ + public C(string test) + { + + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Constraint.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Constraint.cs new file mode 100644 index 0000000..de12f29 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Constraint.cs @@ -0,0 +1,9 @@ +// Author: Simon.Gockner +// Created: 2025-12-03 +// Copyright(c) 2025 SimonG. All Rights Reserved. + +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class Constraint : IConstraint; \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/CtorGenericTest.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/CtorGenericTest.cs new file mode 100644 index 0000000..53447b0 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/CtorGenericTest.cs @@ -0,0 +1,11 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class CtorGenericTest : IGenericTest where T : IConstraint, new() +{ + public CtorGenericTest(T item) + { + + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/DefaultTest.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/DefaultTest.cs new file mode 100644 index 0000000..8ff1e67 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/DefaultTest.cs @@ -0,0 +1,5 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class DefaultTest : IDefaultTest; \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAFactory.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAFactory.cs new file mode 100644 index 0000000..4db3a75 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAFactory.cs @@ -0,0 +1,10 @@ +using JetBrains.Annotations; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +[UsedImplicitly] +public interface IAFactory +{ + IA Create(); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAsyncMultitonTestFactory.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAsyncMultitonTestFactory.cs new file mode 100644 index 0000000..e868964 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAsyncMultitonTestFactory.cs @@ -0,0 +1,8 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +public interface IAsyncMultitonTestFactory +{ + Task Create(int id); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAsyncTestFactory.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAsyncTestFactory.cs new file mode 100644 index 0000000..4da09ba --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAsyncTestFactory.cs @@ -0,0 +1,8 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +public interface IAsyncTestFactory +{ + Task Create(); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IBFactory.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IBFactory.cs new file mode 100644 index 0000000..893af32 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IBFactory.cs @@ -0,0 +1,9 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +public interface IBFactory +{ + IB Create(); + IB Create(C c); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ICtorGenericTestFactory.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ICtorGenericTestFactory.cs new file mode 100644 index 0000000..bda8c3b --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ICtorGenericTestFactory.cs @@ -0,0 +1,8 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +public interface ICtorGenericTestFactory +{ + IGenericTest Create(T item) where T : IConstraint, new(); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IDefaultTestFactory.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IDefaultTestFactory.cs new file mode 100644 index 0000000..9c4ccda --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IDefaultTestFactory.cs @@ -0,0 +1,11 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +public interface IDefaultTestFactory +{ + IDefaultTest Create(); + IDefaultTest Create(string name); + IDefaultTest CreateTest(string name = null!); + IDefaultTest Create(byte id); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IGenericTestFactory.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IGenericTestFactory.cs new file mode 100644 index 0000000..83a9bdf --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IGenericTestFactory.cs @@ -0,0 +1,8 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +public interface IGenericTestFactory +{ + IGenericTest Create() where T : IConstraint, new(); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IInvalidMultitonTestFactory.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IInvalidMultitonTestFactory.cs new file mode 100644 index 0000000..8250ca9 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IInvalidMultitonTestFactory.cs @@ -0,0 +1,9 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +public interface IInvalidMultitonTestFactory +{ + IDefaultTest Create(MultitonScope scope); + IDefaultTest Create(int number); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IMultitonTestFactory.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IMultitonTestFactory.cs new file mode 100644 index 0000000..9e5effa --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IMultitonTestFactory.cs @@ -0,0 +1,9 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +public interface IMultitonTestFactory +{ + IDefaultTest Create(MultitonScope scope); + void ClearMultitonInstance(); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestDefaultFactory.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestDefaultFactory.cs new file mode 100644 index 0000000..e88a284 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestDefaultFactory.cs @@ -0,0 +1,8 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +public interface ITestDefaultFactory +{ + IDefaultTest Create(object obj, string content, string optional1, string optional2, ITestNullFactory testNullFactory = null!); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestFactoryNoCreate.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestFactoryNoCreate.cs new file mode 100644 index 0000000..3409b08 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestFactoryNoCreate.cs @@ -0,0 +1,3 @@ +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +public interface ITestFactoryNoCreate; \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestFactoryWrongReturn.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestFactoryWrongReturn.cs new file mode 100644 index 0000000..83b5315 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestFactoryWrongReturn.cs @@ -0,0 +1,6 @@ +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +public interface ITestFactoryWrongReturn +{ + public MultitonScope Create(); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestNullFactory.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestNullFactory.cs new file mode 100644 index 0000000..b3a4f80 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/ITestNullFactory.cs @@ -0,0 +1,8 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; + +public interface ITestNullFactory +{ + IDefaultTest Create(object obj, string content, string optional1, string optional2); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/GenericTest.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/GenericTest.cs new file mode 100644 index 0000000..9265eab --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/GenericTest.cs @@ -0,0 +1,5 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class GenericTest : IGenericTest where T : IConstraint, new(); \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IA.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IA.cs new file mode 100644 index 0000000..276ecef --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IA.cs @@ -0,0 +1,6 @@ +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +public interface IA +{ + IB BProperty { get; } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IAsyncTest.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IAsyncTest.cs new file mode 100644 index 0000000..42af1dd --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IAsyncTest.cs @@ -0,0 +1,7 @@ +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +public interface IAsyncTest +{ + bool IsInitialized { get; } + Task Initialize(); +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IB.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IB.cs new file mode 100644 index 0000000..831f456 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IB.cs @@ -0,0 +1,6 @@ +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +public interface IB +{ + C C { get; } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IConstraint.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IConstraint.cs new file mode 100644 index 0000000..3956cae --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IConstraint.cs @@ -0,0 +1,3 @@ +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +public interface IConstraint; \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IDefaultTest.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IDefaultTest.cs new file mode 100644 index 0000000..b65741e --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IDefaultTest.cs @@ -0,0 +1,3 @@ +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +public interface IDefaultTest; \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IGenericTest.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IGenericTest.cs new file mode 100644 index 0000000..14a4006 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Interfaces/IGenericTest.cs @@ -0,0 +1,3 @@ +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +public interface IGenericTest where T : IConstraint, new(); \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/MultitonScope.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/MultitonScope.cs new file mode 100644 index 0000000..7c3fdfe --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/MultitonScope.cs @@ -0,0 +1,3 @@ +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class MultitonScope; \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestByte.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestByte.cs new file mode 100644 index 0000000..e81e8ff --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestByte.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class TestByte : IDefaultTest +{ + [UsedImplicitly] + private readonly byte _id; + + public TestByte(byte id) => _id = id; +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestConstructor.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestConstructor.cs new file mode 100644 index 0000000..db2c0e2 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestConstructor.cs @@ -0,0 +1,16 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class TestConstructor : IDefaultTest +{ + public TestConstructor(string name, DefaultTest test) + { + + } + + public TestConstructor(DefaultTest test, string name = null!) + { + + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestMultiton.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestMultiton.cs new file mode 100644 index 0000000..88ea27d --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestMultiton.cs @@ -0,0 +1,11 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class TestMultiton : IDefaultTest +{ + public TestMultiton(MultitonScope scope) + { + + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestNull.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestNull.cs new file mode 100644 index 0000000..cf99071 --- /dev/null +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestNull.cs @@ -0,0 +1,23 @@ +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; + +namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; + +public class TestNull : IDefaultTest +{ + + public TestNull(object obj, string content, string optional1, string optional2, ITestNullFactory testNullFactory) + { + Obj = obj; + Content = content; + Optional1 = optional1; + Optional2 = optional2; + TestNullFactory = testNullFactory; + } + + public object Obj { get; } + public string Content { get; } + public string Optional1 { get; } + public string Optional2 { get; } + public ITestNullFactory TestNullFactory { get; } +} \ No newline at end of file From b16721164b9aa8a58bbcf79b9afda964616612a4 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 15:02:53 +0100 Subject: [PATCH 40/53] - remove unneeded annotations --- .../TestClasses/Factories/IAFactory.cs | 4 +--- .../TestClasses/TestByte.cs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAFactory.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAFactory.cs index 4db3a75..f09d0f8 100644 --- a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAFactory.cs +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/Factories/IAFactory.cs @@ -1,9 +1,7 @@ -using JetBrains.Annotations; -using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses.Factories; -[UsedImplicitly] public interface IAFactory { IA Create(); diff --git a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestByte.cs b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestByte.cs index e81e8ff..81fbacb 100644 --- a/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestByte.cs +++ b/Test.LightweightIocContainer.FactoryGenerator/TestClasses/TestByte.cs @@ -1,11 +1,9 @@ -using JetBrains.Annotations; -using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; +using Test.LightweightIocContainer.FactoryGenerator.TestClasses.Interfaces; namespace Test.LightweightIocContainer.FactoryGenerator.TestClasses; public class TestByte : IDefaultTest { - [UsedImplicitly] private readonly byte _id; public TestByte(byte id) => _id = id; From 610c47bc15011d3c886d19a15867153b5cb4545e Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 16:05:01 +0100 Subject: [PATCH 41/53] - add more test cases with nullable and generic parameters --- .../Samples/Factories/ISampleClassFactory.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Debug.LightweightIocContainer.FactoryGenerator/Samples/Factories/ISampleClassFactory.cs b/Debug.LightweightIocContainer.FactoryGenerator/Samples/Factories/ISampleClassFactory.cs index 37356a4..fbf29b7 100644 --- a/Debug.LightweightIocContainer.FactoryGenerator/Samples/Factories/ISampleClassFactory.cs +++ b/Debug.LightweightIocContainer.FactoryGenerator/Samples/Factories/ISampleClassFactory.cs @@ -12,5 +12,10 @@ public interface ISampleClassFactory T Create(U param1, V param2) where T : ISampleClass where U : class, new() where V : struct; Task CreateAsync(U parameter) where T : ISampleClass where U : class; + ISampleClass Create(Task task) where T : Task; + + Task Create(ISampleClass? sampleClass) where T : class, ISampleClass; + ISampleClass? Create(int id); + void ClearMultitonInstance() where T : ISampleClass; } \ No newline at end of file From ab810a5430e0e456dfe93b73e5c642b7cc4743a7 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 16:26:34 +0100 Subject: [PATCH 42/53] - fix generic arguments and nullability --- .../Samples/Factories/ISampleClassFactory.cs | 2 +- .../FactoryGenerator.cs | 59 ++++++++++++------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/Debug.LightweightIocContainer.FactoryGenerator/Samples/Factories/ISampleClassFactory.cs b/Debug.LightweightIocContainer.FactoryGenerator/Samples/Factories/ISampleClassFactory.cs index fbf29b7..9ed43aa 100644 --- a/Debug.LightweightIocContainer.FactoryGenerator/Samples/Factories/ISampleClassFactory.cs +++ b/Debug.LightweightIocContainer.FactoryGenerator/Samples/Factories/ISampleClassFactory.cs @@ -12,7 +12,7 @@ public interface ISampleClassFactory T Create(U param1, V param2) where T : ISampleClass where U : class, new() where V : struct; Task CreateAsync(U parameter) where T : ISampleClass where U : class; - ISampleClass Create(Task task) where T : Task; + ISampleClass Create(Task task) where T : Task; Task Create(ISampleClass? sampleClass) where T : class, ISampleClass; ISampleClass? Create(int id); diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index 85c3214..f20a248 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -180,10 +180,12 @@ public class FactoryGenerator : IIncrementalGenerator if (!method.ReturnsVoid) { - if (method.ReturnType.Name == "Task") + if (method.ReturnType is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) { - if (method.ReturnType is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) - namespaces.AddRange(namedTypeSymbol.TypeArguments.Select(GetNamespaceOfType)); + namespaces.AddRange(namedTypeSymbol.TypeArguments.Select(GetNamespaceOfType)); + + if (method.ReturnType.Name != "Task") + namespaces.Add(GetNamespaceOfType(method.ReturnType)); } else namespaces.Add(GetNamespaceOfType(method.ReturnType)); @@ -215,11 +217,8 @@ public class FactoryGenerator : IIncrementalGenerator { stringBuilder.Append($"{INDENT}public {method.ReturnType.Name}"); - if (method.ReturnType.Name == "Task") - { - if (method.ReturnType is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) - stringBuilder.Append($"<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(a => a.Name))}>"); - } + if (method.ReturnType is INamedTypeSymbol { IsGenericType: true } namedReturnType) + stringBuilder.Append(GetGenericArguments(namedReturnType)); stringBuilder.Append($" {method.Name}"); @@ -251,14 +250,12 @@ public class FactoryGenerator : IIncrementalGenerator stringBuilder.AppendLine(); } - if (method.ReturnType.Name == "Task") + if (method.ReturnType is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) { - stringBuilder.Append($"{INDENT}{INDENT}return container.ResolveAsync"); - - if (method.ReturnType is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) - stringBuilder.Append($"<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(a => a.Name))}>"); - - stringBuilder.Append("("); + if (method.ReturnType.Name == "Task") + stringBuilder.Append($"{INDENT}{INDENT}return container.ResolveAsync{GetGenericArguments(namedTypeSymbol)}("); + else + stringBuilder.Append($"{INDENT}{INDENT}return container.Resolve<{method.ReturnType.Name}{GetGenericArguments(namedTypeSymbol)}>("); } else stringBuilder.Append($"{INDENT}{INDENT}return container.Resolve<{method.ReturnType.Name}>("); @@ -305,20 +302,34 @@ public class FactoryGenerator : IIncrementalGenerator StringBuilder stringBuilder = new(); stringBuilder.Append(parameter.Type.Name); + if (parameter.Type is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) + stringBuilder.Append(GetGenericArguments(namedTypeSymbol)); + if (parameter.NullableAnnotation == NullableAnnotation.Annotated) stringBuilder.Append("?"); stringBuilder.Append($" {parameter.Name}"); return stringBuilder.ToString(); } - + + private string GetGenericArguments(INamedTypeSymbol namedTypeSymbol) => $"<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(GetGenericArgument))}>"; + private string GetGenericArgument(ITypeSymbol argument) + { + StringBuilder stringBuilder = new(); + stringBuilder.Append(argument.Name); + + if (argument is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) + stringBuilder.Append(GetGenericArguments(namedTypeSymbol)); + + if (argument.NullableAnnotation == NullableAnnotation.Annotated) + stringBuilder.Append("?"); + + return stringBuilder.ToString(); + } + private List GetParameterConstraints(ITypeParameterSymbol typeParameterSymbol) { List constraints = []; - - foreach (ITypeSymbol constraintType in typeParameterSymbol.ConstraintTypes) - constraints.Add(constraintType.Name); - if (typeParameterSymbol.HasReferenceTypeConstraint) constraints.Add("class"); @@ -334,6 +345,14 @@ public class FactoryGenerator : IIncrementalGenerator if (typeParameterSymbol.HasNotNullConstraint) constraints.Add("notnull"); + foreach (ITypeSymbol constraintType in typeParameterSymbol.ConstraintTypes) + { + if (constraintType is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) + constraints.Add($"{constraintType.Name}{GetGenericArguments(namedTypeSymbol)}"); + else + constraints.Add(constraintType.Name); + } + return constraints; } } \ No newline at end of file From b3f9a963261234d15e92ae61f7e3f310f9af9c7f Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Wed, 3 Dec 2025 16:34:08 +0100 Subject: [PATCH 43/53] - use factory resolve instead of normal resolve --- .../FactoryGenerator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index f20a248..a09c06e 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -253,12 +253,12 @@ public class FactoryGenerator : IIncrementalGenerator if (method.ReturnType is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) { if (method.ReturnType.Name == "Task") - stringBuilder.Append($"{INDENT}{INDENT}return container.ResolveAsync{GetGenericArguments(namedTypeSymbol)}("); + stringBuilder.Append($"{INDENT}{INDENT}return container.FactoryResolveAsync{GetGenericArguments(namedTypeSymbol)}("); else - stringBuilder.Append($"{INDENT}{INDENT}return container.Resolve<{method.ReturnType.Name}{GetGenericArguments(namedTypeSymbol)}>("); + stringBuilder.Append($"{INDENT}{INDENT}return container.FactoryResolve<{method.ReturnType.Name}{GetGenericArguments(namedTypeSymbol)}>("); } else - stringBuilder.Append($"{INDENT}{INDENT}return container.Resolve<{method.ReturnType.Name}>("); + stringBuilder.Append($"{INDENT}{INDENT}return container.FactoryResolve<{method.ReturnType.Name}>("); stringBuilder.Append(string.Join(", ", method.Parameters.Select(p => $"{p.Name}Value"))); stringBuilder.AppendLine(");"); From cdc96ccb4cb84343c56e92befb259640619edc5a Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Thu, 4 Dec 2025 07:59:53 +0100 Subject: [PATCH 44/53] - new constraint must be last --- .../FactoryGenerator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index a09c06e..d6b036b 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -336,9 +336,6 @@ public class FactoryGenerator : IIncrementalGenerator if (typeParameterSymbol.HasValueTypeConstraint) constraints.Add("struct"); - if (typeParameterSymbol.HasConstructorConstraint) - constraints.Add("new()"); - if (typeParameterSymbol.HasUnmanagedTypeConstraint) constraints.Add("unmanaged"); @@ -353,6 +350,9 @@ public class FactoryGenerator : IIncrementalGenerator constraints.Add(constraintType.Name); } + if (typeParameterSymbol.HasConstructorConstraint) + constraints.Add("new()"); + return constraints; } } \ No newline at end of file From d7503ba68d80262a7a1d013dcb57816ca8851563 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Thu, 4 Dec 2025 08:35:48 +0100 Subject: [PATCH 45/53] - add factoryGenerator nuget badges to readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 157cb99..f77145e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ A lightweight IOC Container that is powerful enough to do all the things you nee [![Nuget](https://img.shields.io/nuget/v/LightweightIocContainer.Validation.svg?label=Validation%20NuGet%20Version&logo=NuGet)](https://www.nuget.org/packages/LightweightIocContainer.Validation/) [![Nuget (with prereleases)](https://img.shields.io/nuget/vpre/LightweightIocContainer.Validation.svg?label=Validation%20NuGet%20Pre-Release&logo=NuGet)](https://www.nuget.org/packages/LightweightIocContainer.Validation/) +[![Nuget](https://img.shields.io/nuget/dt/LightweightIocContainer.FactoryGenerator.svg?label=FactoryGenerator%20NuGet%20Downloads&logo=NuGet)](https://www.nuget.org/packages/LightweightIocContainer.FactoryGenerator/) +[![Nuget](https://img.shields.io/nuget/v/LightweightIocContainer.FactoryGenerator.svg?label=FactoryGenerator%20NuGet%20Version&logo=NuGet)](https://www.nuget.org/packages/LightweightIocContainer.FactoryGenerator/) +[![Nuget (with prereleases)](https://img.shields.io/nuget/vpre/LightweightIocContainer.FactoryGenerator.svg?label=FactoryGenerator%20NuGet%20Pre-Release&logo=NuGet)](https://www.nuget.org/packages/LightweightIocContainer.FactoryGenerator/) + ## Get started with the Lightweight IOC Container ### How to install From d7ae46bb17f9349e5034b86eaf2b7372d23b66fa Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Thu, 4 Dec 2025 08:45:50 +0100 Subject: [PATCH 46/53] - cleanup csproj --- .../LightweightIocContainer.Validation.csproj | 76 +++++++++---------- .../LightweightIocContainer.csproj | 64 ++++++++-------- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj b/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj index b2e11a4..d867ce2 100644 --- a/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj +++ b/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj @@ -1,45 +1,45 @@ - - net10.0 - SimonG - Copyright(c) 2025 SimonG. All Rights Reserved. - A lightweight IOC Container Validator. - https://github.com/SimonG96/LightweightIocContainer - latest - enable - enable - LightweightIocContainer.Validation.xml - 5.0.0 - + + net10.0 + SimonG + Copyright(c) 2025 SimonG. All Rights Reserved. + A lightweight IOC Container Validator. + https://github.com/SimonG96/LightweightIocContainer + latest + enable + enable + LightweightIocContainer.Validation.xml + 5.0.0 + - - true - https://github.com/SimonG96/LightweightIocContainer - MIT - README.md - true - snupkg - + + true + https://github.com/SimonG96/LightweightIocContainer + MIT + README.md + true + snupkg + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + - - - + + + - - - - - - + + + + + + - + \ No newline at end of file diff --git a/LightweightIocContainer/LightweightIocContainer.csproj b/LightweightIocContainer/LightweightIocContainer.csproj index 5216bc4..26f6fe2 100644 --- a/LightweightIocContainer/LightweightIocContainer.csproj +++ b/LightweightIocContainer/LightweightIocContainer.csproj @@ -1,38 +1,38 @@ - - net10.0 - SimonG - Copyright(c) 2025 SimonG. All Rights Reserved. - A lightweight IOC Container that is powerful enough to do all the things you need it to do. - https://github.com/SimonG96/LightweightIocContainer - latest - enable - enable - LightweightIocContainer.xml - 5.0.0 - + + net10.0 + SimonG + Copyright(c) 2025 SimonG. All Rights Reserved. + A lightweight IOC Container that is powerful enough to do all the things you need it to do. + https://github.com/SimonG96/LightweightIocContainer + latest + enable + enable + LightweightIocContainer.xml + 5.0.0 + - - true - https://github.com/SimonG96/LightweightIocContainer - LICENSE.md - README.md - true - snupkg - + + true + https://github.com/SimonG96/LightweightIocContainer + LICENSE.md + README.md + true + snupkg + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + - - - - + + + + - + \ No newline at end of file From fa20b72e0100612b8135e0b2510e1de24edeb6f4 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Thu, 4 Dec 2025 08:49:27 +0100 Subject: [PATCH 47/53] - add beta version suffix --- .../LightweightIocContainer.FactoryGenerator.csproj | 1 + .../LightweightIocContainer.Validation.csproj | 1 + LightweightIocContainer/LightweightIocContainer.csproj | 1 + 3 files changed, 3 insertions(+) diff --git a/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj b/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj index e2efa5d..671da42 100644 --- a/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj +++ b/LightweightIocContainer.FactoryGenerator/LightweightIocContainer.FactoryGenerator.csproj @@ -12,6 +12,7 @@ true true 5.0.0 + beta diff --git a/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj b/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj index d867ce2..859d9b0 100644 --- a/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj +++ b/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj @@ -11,6 +11,7 @@ enable LightweightIocContainer.Validation.xml 5.0.0 + beta diff --git a/LightweightIocContainer/LightweightIocContainer.csproj b/LightweightIocContainer/LightweightIocContainer.csproj index 26f6fe2..5ff1b32 100644 --- a/LightweightIocContainer/LightweightIocContainer.csproj +++ b/LightweightIocContainer/LightweightIocContainer.csproj @@ -11,6 +11,7 @@ enable LightweightIocContainer.xml 5.0.0 + beta From f16a6e4b2d165bb36aa3ba1b781ea780c12c5503 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Thu, 4 Dec 2025 08:52:50 +0100 Subject: [PATCH 48/53] - update copyright --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index 43a5e86..fe4cd6f 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Simon G. +Copyright (c) 2025 Simon G. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From c67868db74575b25a5087fae30c44f852830b12e Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Thu, 4 Dec 2025 09:57:25 +0100 Subject: [PATCH 49/53] - remove unneeded references --- .../LightweightIocContainer.Validation.csproj | 1 - LightweightIocContainer/LightweightIocContainer.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj b/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj index 859d9b0..596598d 100644 --- a/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj +++ b/LightweightIocContainer.Validation/LightweightIocContainer.Validation.csproj @@ -29,7 +29,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/LightweightIocContainer/LightweightIocContainer.csproj b/LightweightIocContainer/LightweightIocContainer.csproj index 5ff1b32..e7b9283 100644 --- a/LightweightIocContainer/LightweightIocContainer.csproj +++ b/LightweightIocContainer/LightweightIocContainer.csproj @@ -28,7 +28,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - From a7c4e5ed12ad8d04da605f54b089de6bc8faf249 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Thu, 4 Dec 2025 10:54:19 +0100 Subject: [PATCH 50/53] #62: fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f77145e..bea9159 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ The easiest way to [install](https://github.com/SimonG96/LightweightIocContainer There is the option to install the [LightweightIocContainer.Validation](https://www.nuget.org/packages/LightweightIocContainer.Validation/) package: ```.net -> dotnet add package LightweightIocContainer.Validaton --version 5.0.0 +> dotnet add package LightweightIocContainer.Validation --version 5.0.0 ``` With this you can validate your `IocContainer` setup by using the `IocValidator` in a unit test: From 35250d4fa04e2d39befbea6759ecccc6290232d0 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Thu, 4 Dec 2025 10:55:38 +0100 Subject: [PATCH 51/53] #62: add missing description --- LightweightIocContainer/Factories/TypedFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LightweightIocContainer/Factories/TypedFactory.cs b/LightweightIocContainer/Factories/TypedFactory.cs index f0dad10..7dc717d 100644 --- a/LightweightIocContainer/Factories/TypedFactory.cs +++ b/LightweightIocContainer/Factories/TypedFactory.cs @@ -27,7 +27,7 @@ public class TypedFactory : TypedFactoryBase, ITypedFactory< /// /// Constructor for generated factories /// - /// + /// The generated factory instance public TypedFactory(TFactory factory) => Factory = factory; /// From f0082da9e456578f0135a3b5d34c9de4f58bd582 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Thu, 4 Dec 2025 11:31:43 +0100 Subject: [PATCH 52/53] #62: add missing description --- LightweightIocContainer/LightweightIocContainer.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index 8bbe8b4..63c2fb5 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -392,7 +392,7 @@ Constructor for generated factories - + The generated factory instance From a3c244e8da5753545066f81f89fd45a0fb754123 Mon Sep 17 00:00:00 2001 From: "Simon G." Date: Thu, 4 Dec 2025 11:40:26 +0100 Subject: [PATCH 53/53] #62: extract and use more methods to keep type handling the same --- .../FactoryGenerator.cs | 74 ++++++++----------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index d6b036b..4598053 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -215,29 +215,15 @@ public class FactoryGenerator : IIncrementalGenerator if (!method.ReturnsVoid) //create method { - stringBuilder.Append($"{INDENT}public {method.ReturnType.Name}"); - - if (method.ReturnType is INamedTypeSymbol { IsGenericType: true } namedReturnType) - stringBuilder.Append(GetGenericArguments(namedReturnType)); - - stringBuilder.Append($" {method.Name}"); + stringBuilder.Append($"{INDENT}public {GetTypeText(method.ReturnType)} {method.Name}"); if (method.IsGenericMethod) - stringBuilder.Append($"<{string.Join(", ", method.TypeParameters.Select(p => p.Name))}>"); + stringBuilder.Append(GetGenericParameters(method)); stringBuilder.Append($"({string.Join(", ", method.Parameters.Select(GetParameterText))})"); - if (method.IsGenericMethod) - { - foreach (ITypeParameterSymbol typeParameter in method.TypeParameters) - { - List parameterConstraints = GetParameterConstraints(typeParameter); - if (parameterConstraints.Count == 0) - continue; - - stringBuilder.Append($" where {typeParameter.Name} : {string.Join(", ", parameterConstraints)}"); - } - } + if (method.IsGenericMethod) + stringBuilder.Append(GetParameterConstraintsText(method)); stringBuilder.AppendLine(); stringBuilder.AppendLine($"{INDENT}{{"); @@ -246,10 +232,11 @@ public class FactoryGenerator : IIncrementalGenerator { stringBuilder.AppendLine($"{INDENT}{INDENT}object? {parameter.Name}Value = {parameter.Name};"); stringBuilder.AppendLine($"{INDENT}{INDENT}if ({parameter.Name}Value is null)"); - stringBuilder.AppendLine($"{INDENT}{INDENT}{INDENT}{parameter.Name}Value = new NullParameter(typeof({parameter.Type.Name}));"); + stringBuilder.AppendLine($"{INDENT}{INDENT}{INDENT}{parameter.Name}Value = new NullParameter(typeof({GetTypeText(parameter.Type, false)}));"); stringBuilder.AppendLine(); } + //don't use getTypeText here, because we need the raw type name for Task<> if (method.ReturnType is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) { if (method.ReturnType.Name == "Task") @@ -267,18 +254,10 @@ public class FactoryGenerator : IIncrementalGenerator } else if (method is { Name: CLEAR_MULTITON_INSTANCE_METHOD_NAME, IsGenericMethod: true }) { - stringBuilder.Append($"{INDENT}public void {method.Name}<{string.Join(", ", method.TypeParameters.Select(p => p.Name))}>()"); - - foreach (ITypeParameterSymbol typeParameter in method.TypeParameters) - { - List parameterConstraints = GetParameterConstraints(typeParameter); - if (parameterConstraints.Count == 0) - continue; - - stringBuilder.Append($" where {typeParameter.Name} : {string.Join(", ", parameterConstraints)}"); - } + stringBuilder.Append($"{INDENT}public void {method.Name}{GetGenericParameters(method)}()"); + stringBuilder.Append(GetParameterConstraintsText(method)); - stringBuilder.AppendLine($" => container.ClearMultitonInstances<{string.Join(", ", method.TypeArguments.Select(a => a.Name))}>();"); + stringBuilder.AppendLine($" => container.ClearMultitonInstances{GetGenericArguments(method)}();"); } if (members.IndexOf(member) < members.Length - 1) //only append empty line if not the last member @@ -297,33 +276,42 @@ public class FactoryGenerator : IIncrementalGenerator .OfType() .Distinct(); - private string GetParameterText(IParameterSymbol parameter) + private string GetTypeText(ITypeSymbol typeSymbol) => GetTypeText(typeSymbol, true); + private string GetTypeText(ITypeSymbol typeSymbol, bool allowNullable) { StringBuilder stringBuilder = new(); - stringBuilder.Append(parameter.Type.Name); + stringBuilder.Append(typeSymbol.Name); - if (parameter.Type is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) + if (typeSymbol is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) stringBuilder.Append(GetGenericArguments(namedTypeSymbol)); - if (parameter.NullableAnnotation == NullableAnnotation.Annotated) + if (allowNullable && typeSymbol.NullableAnnotation == NullableAnnotation.Annotated) stringBuilder.Append("?"); - stringBuilder.Append($" {parameter.Name}"); return stringBuilder.ToString(); } - private string GetGenericArguments(INamedTypeSymbol namedTypeSymbol) => $"<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(GetGenericArgument))}>"; - private string GetGenericArgument(ITypeSymbol argument) + private string GetParameterText(IParameterSymbol parameter) => $"{GetTypeText(parameter.Type)} {parameter.Name}"; + private string GetGenericArguments(INamedTypeSymbol namedTypeSymbol) => GetGenericArguments(namedTypeSymbol.TypeArguments); + private string GetGenericArguments(IMethodSymbol methodSymbol) => GetGenericArguments(methodSymbol.TypeArguments); + private string GetGenericArguments(ImmutableArray typeArguments) => $"<{string.Join(", ", typeArguments.Select(GetTypeText))}>"; + + private string GetGenericParameters(IMethodSymbol methodSymbol) => GetGenericParameters(methodSymbol.TypeParameters); + private string GetGenericParameters(ImmutableArray typeParameters) => $"<{string.Join(", ", typeParameters.Select(GetTypeText))}>"; + + private string GetParameterConstraintsText(IMethodSymbol method) { StringBuilder stringBuilder = new(); - stringBuilder.Append(argument.Name); - if (argument is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol) - stringBuilder.Append(GetGenericArguments(namedTypeSymbol)); + foreach (ITypeParameterSymbol typeParameter in method.TypeParameters) + { + List parameterConstraints = GetParameterConstraints(typeParameter); + if (parameterConstraints.Count == 0) + continue; + + stringBuilder.Append($" where {typeParameter.Name} : {string.Join(", ", parameterConstraints)}"); + } - if (argument.NullableAnnotation == NullableAnnotation.Annotated) - stringBuilder.Append("?"); - return stringBuilder.ToString(); }