diff --git a/Cortex.sln b/Cortex.sln
index 6665027..9640f38 100644
--- a/Cortex.sln
+++ b/Cortex.sln
@@ -1,3 +1,4 @@
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.0.11111.16
@@ -76,144 +77,426 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cortex.States.DuckDb", "src
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cortex.Mediator.Behaviors.Transactional", "src\Cortex.Mediator.Behaviors.Transactional\Cortex.Mediator.Behaviors.Transactional.csproj", "{F7C9F778-EFDB-4F02-8F19-43A9F4A86003}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cortex.Mediator.SourceGenerator", "src\Cortex.Mediator.SourceGenerator\Cortex.Mediator.SourceGenerator.csproj", "{67B76C68-F28F-4A01-AB02-DB661785C690}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F1CC775A-95DA-4A5A-879F-66BFCB0FDCC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1CC775A-95DA-4A5A-879F-66BFCB0FDCC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F1CC775A-95DA-4A5A-879F-66BFCB0FDCC9}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F1CC775A-95DA-4A5A-879F-66BFCB0FDCC9}.Debug|x64.Build.0 = Debug|Any CPU
+ {F1CC775A-95DA-4A5A-879F-66BFCB0FDCC9}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F1CC775A-95DA-4A5A-879F-66BFCB0FDCC9}.Debug|x86.Build.0 = Debug|Any CPU
{F1CC775A-95DA-4A5A-879F-66BFCB0FDCC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F1CC775A-95DA-4A5A-879F-66BFCB0FDCC9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F1CC775A-95DA-4A5A-879F-66BFCB0FDCC9}.Release|x64.ActiveCfg = Release|Any CPU
+ {F1CC775A-95DA-4A5A-879F-66BFCB0FDCC9}.Release|x64.Build.0 = Release|Any CPU
+ {F1CC775A-95DA-4A5A-879F-66BFCB0FDCC9}.Release|x86.ActiveCfg = Release|Any CPU
+ {F1CC775A-95DA-4A5A-879F-66BFCB0FDCC9}.Release|x86.Build.0 = Release|Any CPU
{1C8605F4-91CB-49A4-A080-0A6DFE1FB010}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1C8605F4-91CB-49A4-A080-0A6DFE1FB010}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1C8605F4-91CB-49A4-A080-0A6DFE1FB010}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1C8605F4-91CB-49A4-A080-0A6DFE1FB010}.Debug|x64.Build.0 = Debug|Any CPU
+ {1C8605F4-91CB-49A4-A080-0A6DFE1FB010}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1C8605F4-91CB-49A4-A080-0A6DFE1FB010}.Debug|x86.Build.0 = Debug|Any CPU
{1C8605F4-91CB-49A4-A080-0A6DFE1FB010}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C8605F4-91CB-49A4-A080-0A6DFE1FB010}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1C8605F4-91CB-49A4-A080-0A6DFE1FB010}.Release|x64.ActiveCfg = Release|Any CPU
+ {1C8605F4-91CB-49A4-A080-0A6DFE1FB010}.Release|x64.Build.0 = Release|Any CPU
+ {1C8605F4-91CB-49A4-A080-0A6DFE1FB010}.Release|x86.ActiveCfg = Release|Any CPU
+ {1C8605F4-91CB-49A4-A080-0A6DFE1FB010}.Release|x86.Build.0 = Release|Any CPU
{96701658-663E-41C5-9EF9-843C79CE727A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96701658-663E-41C5-9EF9-843C79CE727A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {96701658-663E-41C5-9EF9-843C79CE727A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {96701658-663E-41C5-9EF9-843C79CE727A}.Debug|x64.Build.0 = Debug|Any CPU
+ {96701658-663E-41C5-9EF9-843C79CE727A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {96701658-663E-41C5-9EF9-843C79CE727A}.Debug|x86.Build.0 = Debug|Any CPU
{96701658-663E-41C5-9EF9-843C79CE727A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{96701658-663E-41C5-9EF9-843C79CE727A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {96701658-663E-41C5-9EF9-843C79CE727A}.Release|x64.ActiveCfg = Release|Any CPU
+ {96701658-663E-41C5-9EF9-843C79CE727A}.Release|x64.Build.0 = Release|Any CPU
+ {96701658-663E-41C5-9EF9-843C79CE727A}.Release|x86.ActiveCfg = Release|Any CPU
+ {96701658-663E-41C5-9EF9-843C79CE727A}.Release|x86.Build.0 = Release|Any CPU
{EE799E10-7469-428E-AF8C-F2807F6CE7E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE799E10-7469-428E-AF8C-F2807F6CE7E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EE799E10-7469-428E-AF8C-F2807F6CE7E5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {EE799E10-7469-428E-AF8C-F2807F6CE7E5}.Debug|x64.Build.0 = Debug|Any CPU
+ {EE799E10-7469-428E-AF8C-F2807F6CE7E5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {EE799E10-7469-428E-AF8C-F2807F6CE7E5}.Debug|x86.Build.0 = Debug|Any CPU
{EE799E10-7469-428E-AF8C-F2807F6CE7E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE799E10-7469-428E-AF8C-F2807F6CE7E5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EE799E10-7469-428E-AF8C-F2807F6CE7E5}.Release|x64.ActiveCfg = Release|Any CPU
+ {EE799E10-7469-428E-AF8C-F2807F6CE7E5}.Release|x64.Build.0 = Release|Any CPU
+ {EE799E10-7469-428E-AF8C-F2807F6CE7E5}.Release|x86.ActiveCfg = Release|Any CPU
+ {EE799E10-7469-428E-AF8C-F2807F6CE7E5}.Release|x86.Build.0 = Release|Any CPU
{34CA231A-1E3A-4CA4-820C-946DC8E2737F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34CA231A-1E3A-4CA4-820C-946DC8E2737F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {34CA231A-1E3A-4CA4-820C-946DC8E2737F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {34CA231A-1E3A-4CA4-820C-946DC8E2737F}.Debug|x64.Build.0 = Debug|Any CPU
+ {34CA231A-1E3A-4CA4-820C-946DC8E2737F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {34CA231A-1E3A-4CA4-820C-946DC8E2737F}.Debug|x86.Build.0 = Debug|Any CPU
{34CA231A-1E3A-4CA4-820C-946DC8E2737F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34CA231A-1E3A-4CA4-820C-946DC8E2737F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {34CA231A-1E3A-4CA4-820C-946DC8E2737F}.Release|x64.ActiveCfg = Release|Any CPU
+ {34CA231A-1E3A-4CA4-820C-946DC8E2737F}.Release|x64.Build.0 = Release|Any CPU
+ {34CA231A-1E3A-4CA4-820C-946DC8E2737F}.Release|x86.ActiveCfg = Release|Any CPU
+ {34CA231A-1E3A-4CA4-820C-946DC8E2737F}.Release|x86.Build.0 = Release|Any CPU
{16FA00B1-973B-443C-BF84-9B76DCAF341B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{16FA00B1-973B-443C-BF84-9B76DCAF341B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {16FA00B1-973B-443C-BF84-9B76DCAF341B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {16FA00B1-973B-443C-BF84-9B76DCAF341B}.Debug|x64.Build.0 = Debug|Any CPU
+ {16FA00B1-973B-443C-BF84-9B76DCAF341B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {16FA00B1-973B-443C-BF84-9B76DCAF341B}.Debug|x86.Build.0 = Debug|Any CPU
{16FA00B1-973B-443C-BF84-9B76DCAF341B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16FA00B1-973B-443C-BF84-9B76DCAF341B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {16FA00B1-973B-443C-BF84-9B76DCAF341B}.Release|x64.ActiveCfg = Release|Any CPU
+ {16FA00B1-973B-443C-BF84-9B76DCAF341B}.Release|x64.Build.0 = Release|Any CPU
+ {16FA00B1-973B-443C-BF84-9B76DCAF341B}.Release|x86.ActiveCfg = Release|Any CPU
+ {16FA00B1-973B-443C-BF84-9B76DCAF341B}.Release|x86.Build.0 = Release|Any CPU
{1F60F66A-5DC0-4C1D-A7B7-66F568A26911}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F60F66A-5DC0-4C1D-A7B7-66F568A26911}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1F60F66A-5DC0-4C1D-A7B7-66F568A26911}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1F60F66A-5DC0-4C1D-A7B7-66F568A26911}.Debug|x64.Build.0 = Debug|Any CPU
+ {1F60F66A-5DC0-4C1D-A7B7-66F568A26911}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1F60F66A-5DC0-4C1D-A7B7-66F568A26911}.Debug|x86.Build.0 = Debug|Any CPU
{1F60F66A-5DC0-4C1D-A7B7-66F568A26911}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F60F66A-5DC0-4C1D-A7B7-66F568A26911}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1F60F66A-5DC0-4C1D-A7B7-66F568A26911}.Release|x64.ActiveCfg = Release|Any CPU
+ {1F60F66A-5DC0-4C1D-A7B7-66F568A26911}.Release|x64.Build.0 = Release|Any CPU
+ {1F60F66A-5DC0-4C1D-A7B7-66F568A26911}.Release|x86.ActiveCfg = Release|Any CPU
+ {1F60F66A-5DC0-4C1D-A7B7-66F568A26911}.Release|x86.Build.0 = Release|Any CPU
{2B949637-FC31-4F51-A391-19F76B57CC28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B949637-FC31-4F51-A391-19F76B57CC28}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2B949637-FC31-4F51-A391-19F76B57CC28}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2B949637-FC31-4F51-A391-19F76B57CC28}.Debug|x64.Build.0 = Debug|Any CPU
+ {2B949637-FC31-4F51-A391-19F76B57CC28}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2B949637-FC31-4F51-A391-19F76B57CC28}.Debug|x86.Build.0 = Debug|Any CPU
{2B949637-FC31-4F51-A391-19F76B57CC28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B949637-FC31-4F51-A391-19F76B57CC28}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2B949637-FC31-4F51-A391-19F76B57CC28}.Release|x64.ActiveCfg = Release|Any CPU
+ {2B949637-FC31-4F51-A391-19F76B57CC28}.Release|x64.Build.0 = Release|Any CPU
+ {2B949637-FC31-4F51-A391-19F76B57CC28}.Release|x86.ActiveCfg = Release|Any CPU
+ {2B949637-FC31-4F51-A391-19F76B57CC28}.Release|x86.Build.0 = Release|Any CPU
{6E5AC0AC-A364-4DB3-9E9A-C2FB54BD6D1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6E5AC0AC-A364-4DB3-9E9A-C2FB54BD6D1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6E5AC0AC-A364-4DB3-9E9A-C2FB54BD6D1E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6E5AC0AC-A364-4DB3-9E9A-C2FB54BD6D1E}.Debug|x64.Build.0 = Debug|Any CPU
+ {6E5AC0AC-A364-4DB3-9E9A-C2FB54BD6D1E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6E5AC0AC-A364-4DB3-9E9A-C2FB54BD6D1E}.Debug|x86.Build.0 = Debug|Any CPU
{6E5AC0AC-A364-4DB3-9E9A-C2FB54BD6D1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E5AC0AC-A364-4DB3-9E9A-C2FB54BD6D1E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6E5AC0AC-A364-4DB3-9E9A-C2FB54BD6D1E}.Release|x64.ActiveCfg = Release|Any CPU
+ {6E5AC0AC-A364-4DB3-9E9A-C2FB54BD6D1E}.Release|x64.Build.0 = Release|Any CPU
+ {6E5AC0AC-A364-4DB3-9E9A-C2FB54BD6D1E}.Release|x86.ActiveCfg = Release|Any CPU
+ {6E5AC0AC-A364-4DB3-9E9A-C2FB54BD6D1E}.Release|x86.Build.0 = Release|Any CPU
{6019F9E6-C377-416D-9E3B-3D7104FAEB63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6019F9E6-C377-416D-9E3B-3D7104FAEB63}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6019F9E6-C377-416D-9E3B-3D7104FAEB63}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6019F9E6-C377-416D-9E3B-3D7104FAEB63}.Debug|x64.Build.0 = Debug|Any CPU
+ {6019F9E6-C377-416D-9E3B-3D7104FAEB63}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6019F9E6-C377-416D-9E3B-3D7104FAEB63}.Debug|x86.Build.0 = Debug|Any CPU
{6019F9E6-C377-416D-9E3B-3D7104FAEB63}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6019F9E6-C377-416D-9E3B-3D7104FAEB63}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6019F9E6-C377-416D-9E3B-3D7104FAEB63}.Release|x64.ActiveCfg = Release|Any CPU
+ {6019F9E6-C377-416D-9E3B-3D7104FAEB63}.Release|x64.Build.0 = Release|Any CPU
+ {6019F9E6-C377-416D-9E3B-3D7104FAEB63}.Release|x86.ActiveCfg = Release|Any CPU
+ {6019F9E6-C377-416D-9E3B-3D7104FAEB63}.Release|x86.Build.0 = Release|Any CPU
{20FAE1F1-D677-4E0E-B3A7-E2B1485C874C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20FAE1F1-D677-4E0E-B3A7-E2B1485C874C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {20FAE1F1-D677-4E0E-B3A7-E2B1485C874C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {20FAE1F1-D677-4E0E-B3A7-E2B1485C874C}.Debug|x64.Build.0 = Debug|Any CPU
+ {20FAE1F1-D677-4E0E-B3A7-E2B1485C874C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {20FAE1F1-D677-4E0E-B3A7-E2B1485C874C}.Debug|x86.Build.0 = Debug|Any CPU
{20FAE1F1-D677-4E0E-B3A7-E2B1485C874C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20FAE1F1-D677-4E0E-B3A7-E2B1485C874C}.Release|Any CPU.Build.0 = Release|Any CPU
- {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Debug|Any CPU.ActiveCfg = Debug|AnyCPU
- {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Debug|Any CPU.Build.0 = Debug|AnyCPU
- {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Release|Any CPU.ActiveCfg = Release|AnyCPU
- {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Release|Any CPU.Build.0 = Release|AnyCPU
- {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Debug|Any CPU.ActiveCfg = Debug|AnyCPU
- {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Debug|Any CPU.Build.0 = Debug|AnyCPU
- {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Release|Any CPU.ActiveCfg = Release|AnyCPU
- {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Release|Any CPU.Build.0 = Release|AnyCPU
- {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Debug|Any CPU.ActiveCfg = Debug|AnyCPU
- {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Debug|Any CPU.Build.0 = Debug|AnyCPU
- {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Release|Any CPU.ActiveCfg = Release|AnyCPU
- {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Release|Any CPU.Build.0 = Release|AnyCPU
+ {20FAE1F1-D677-4E0E-B3A7-E2B1485C874C}.Release|x64.ActiveCfg = Release|Any CPU
+ {20FAE1F1-D677-4E0E-B3A7-E2B1485C874C}.Release|x64.Build.0 = Release|Any CPU
+ {20FAE1F1-D677-4E0E-B3A7-E2B1485C874C}.Release|x86.ActiveCfg = Release|Any CPU
+ {20FAE1F1-D677-4E0E-B3A7-E2B1485C874C}.Release|x86.Build.0 = Release|Any CPU
+ {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Debug|x64.Build.0 = Debug|Any CPU
+ {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Debug|x86.Build.0 = Debug|Any CPU
+ {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Release|x64.ActiveCfg = Release|Any CPU
+ {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Release|x64.Build.0 = Release|Any CPU
+ {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Release|x86.ActiveCfg = Release|Any CPU
+ {CFEB3F3A-8B3E-4286-8596-EF888552C07F}.Release|x86.Build.0 = Release|Any CPU
+ {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Debug|x64.Build.0 = Debug|Any CPU
+ {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Debug|x86.Build.0 = Debug|Any CPU
+ {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Release|x64.ActiveCfg = Release|Any CPU
+ {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Release|x64.Build.0 = Release|Any CPU
+ {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Release|x86.ActiveCfg = Release|Any CPU
+ {D51C6B82-ABD9-4C43-820E-237EDBD706A1}.Release|x86.Build.0 = Release|Any CPU
+ {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Debug|x64.Build.0 = Debug|Any CPU
+ {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Debug|x86.Build.0 = Debug|Any CPU
+ {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Release|x64.ActiveCfg = Release|Any CPU
+ {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Release|x64.Build.0 = Release|Any CPU
+ {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Release|x86.ActiveCfg = Release|Any CPU
+ {C9A7699A-9BCF-4B51-B29F-ABF78DCEA553}.Release|x86.Build.0 = Release|Any CPU
{D376D6CA-3192-4EDC-B840-31F58B6457DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D376D6CA-3192-4EDC-B840-31F58B6457DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D376D6CA-3192-4EDC-B840-31F58B6457DD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D376D6CA-3192-4EDC-B840-31F58B6457DD}.Debug|x64.Build.0 = Debug|Any CPU
+ {D376D6CA-3192-4EDC-B840-31F58B6457DD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D376D6CA-3192-4EDC-B840-31F58B6457DD}.Debug|x86.Build.0 = Debug|Any CPU
{D376D6CA-3192-4EDC-B840-31F58B6457DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D376D6CA-3192-4EDC-B840-31F58B6457DD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D376D6CA-3192-4EDC-B840-31F58B6457DD}.Release|x64.ActiveCfg = Release|Any CPU
+ {D376D6CA-3192-4EDC-B840-31F58B6457DD}.Release|x64.Build.0 = Release|Any CPU
+ {D376D6CA-3192-4EDC-B840-31F58B6457DD}.Release|x86.ActiveCfg = Release|Any CPU
+ {D376D6CA-3192-4EDC-B840-31F58B6457DD}.Release|x86.Build.0 = Release|Any CPU
{447970B9-C5AA-41D9-A07F-330A251597D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{447970B9-C5AA-41D9-A07F-330A251597D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {447970B9-C5AA-41D9-A07F-330A251597D0}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {447970B9-C5AA-41D9-A07F-330A251597D0}.Debug|x64.Build.0 = Debug|Any CPU
+ {447970B9-C5AA-41D9-A07F-330A251597D0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {447970B9-C5AA-41D9-A07F-330A251597D0}.Debug|x86.Build.0 = Debug|Any CPU
{447970B9-C5AA-41D9-A07F-330A251597D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{447970B9-C5AA-41D9-A07F-330A251597D0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {447970B9-C5AA-41D9-A07F-330A251597D0}.Release|x64.ActiveCfg = Release|Any CPU
+ {447970B9-C5AA-41D9-A07F-330A251597D0}.Release|x64.Build.0 = Release|Any CPU
+ {447970B9-C5AA-41D9-A07F-330A251597D0}.Release|x86.ActiveCfg = Release|Any CPU
+ {447970B9-C5AA-41D9-A07F-330A251597D0}.Release|x86.Build.0 = Release|Any CPU
{00358701-D117-4953-A673-D60625D38466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{00358701-D117-4953-A673-D60625D38466}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {00358701-D117-4953-A673-D60625D38466}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {00358701-D117-4953-A673-D60625D38466}.Debug|x64.Build.0 = Debug|Any CPU
+ {00358701-D117-4953-A673-D60625D38466}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {00358701-D117-4953-A673-D60625D38466}.Debug|x86.Build.0 = Debug|Any CPU
{00358701-D117-4953-A673-D60625D38466}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00358701-D117-4953-A673-D60625D38466}.Release|Any CPU.Build.0 = Release|Any CPU
+ {00358701-D117-4953-A673-D60625D38466}.Release|x64.ActiveCfg = Release|Any CPU
+ {00358701-D117-4953-A673-D60625D38466}.Release|x64.Build.0 = Release|Any CPU
+ {00358701-D117-4953-A673-D60625D38466}.Release|x86.ActiveCfg = Release|Any CPU
+ {00358701-D117-4953-A673-D60625D38466}.Release|x86.Build.0 = Release|Any CPU
{77AD462F-A248-43AF-9212-43031F22F23D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{77AD462F-A248-43AF-9212-43031F22F23D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {77AD462F-A248-43AF-9212-43031F22F23D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {77AD462F-A248-43AF-9212-43031F22F23D}.Debug|x64.Build.0 = Debug|Any CPU
+ {77AD462F-A248-43AF-9212-43031F22F23D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {77AD462F-A248-43AF-9212-43031F22F23D}.Debug|x86.Build.0 = Debug|Any CPU
{77AD462F-A248-43AF-9212-43031F22F23D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{77AD462F-A248-43AF-9212-43031F22F23D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {77AD462F-A248-43AF-9212-43031F22F23D}.Release|x64.ActiveCfg = Release|Any CPU
+ {77AD462F-A248-43AF-9212-43031F22F23D}.Release|x64.Build.0 = Release|Any CPU
+ {77AD462F-A248-43AF-9212-43031F22F23D}.Release|x86.ActiveCfg = Release|Any CPU
+ {77AD462F-A248-43AF-9212-43031F22F23D}.Release|x86.Build.0 = Release|Any CPU
{980EDBFE-40C2-4EFD-96C2-FED1032FB5E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{980EDBFE-40C2-4EFD-96C2-FED1032FB5E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {980EDBFE-40C2-4EFD-96C2-FED1032FB5E6}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {980EDBFE-40C2-4EFD-96C2-FED1032FB5E6}.Debug|x64.Build.0 = Debug|Any CPU
+ {980EDBFE-40C2-4EFD-96C2-FED1032FB5E6}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {980EDBFE-40C2-4EFD-96C2-FED1032FB5E6}.Debug|x86.Build.0 = Debug|Any CPU
{980EDBFE-40C2-4EFD-96C2-FED1032FB5E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{980EDBFE-40C2-4EFD-96C2-FED1032FB5E6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {980EDBFE-40C2-4EFD-96C2-FED1032FB5E6}.Release|x64.ActiveCfg = Release|Any CPU
+ {980EDBFE-40C2-4EFD-96C2-FED1032FB5E6}.Release|x64.Build.0 = Release|Any CPU
+ {980EDBFE-40C2-4EFD-96C2-FED1032FB5E6}.Release|x86.ActiveCfg = Release|Any CPU
+ {980EDBFE-40C2-4EFD-96C2-FED1032FB5E6}.Release|x86.Build.0 = Release|Any CPU
{0F9FCB99-D00F-4396-8E2B-6E627076ADA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F9FCB99-D00F-4396-8E2B-6E627076ADA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0F9FCB99-D00F-4396-8E2B-6E627076ADA0}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {0F9FCB99-D00F-4396-8E2B-6E627076ADA0}.Debug|x64.Build.0 = Debug|Any CPU
+ {0F9FCB99-D00F-4396-8E2B-6E627076ADA0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {0F9FCB99-D00F-4396-8E2B-6E627076ADA0}.Debug|x86.Build.0 = Debug|Any CPU
{0F9FCB99-D00F-4396-8E2B-6E627076ADA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F9FCB99-D00F-4396-8E2B-6E627076ADA0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0F9FCB99-D00F-4396-8E2B-6E627076ADA0}.Release|x64.ActiveCfg = Release|Any CPU
+ {0F9FCB99-D00F-4396-8E2B-6E627076ADA0}.Release|x64.Build.0 = Release|Any CPU
+ {0F9FCB99-D00F-4396-8E2B-6E627076ADA0}.Release|x86.ActiveCfg = Release|Any CPU
+ {0F9FCB99-D00F-4396-8E2B-6E627076ADA0}.Release|x86.Build.0 = Release|Any CPU
{20BD7107-8199-4CA8-815B-4D156B522B82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20BD7107-8199-4CA8-815B-4D156B522B82}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {20BD7107-8199-4CA8-815B-4D156B522B82}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {20BD7107-8199-4CA8-815B-4D156B522B82}.Debug|x64.Build.0 = Debug|Any CPU
+ {20BD7107-8199-4CA8-815B-4D156B522B82}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {20BD7107-8199-4CA8-815B-4D156B522B82}.Debug|x86.Build.0 = Debug|Any CPU
{20BD7107-8199-4CA8-815B-4D156B522B82}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20BD7107-8199-4CA8-815B-4D156B522B82}.Release|Any CPU.Build.0 = Release|Any CPU
+ {20BD7107-8199-4CA8-815B-4D156B522B82}.Release|x64.ActiveCfg = Release|Any CPU
+ {20BD7107-8199-4CA8-815B-4D156B522B82}.Release|x64.Build.0 = Release|Any CPU
+ {20BD7107-8199-4CA8-815B-4D156B522B82}.Release|x86.ActiveCfg = Release|Any CPU
+ {20BD7107-8199-4CA8-815B-4D156B522B82}.Release|x86.Build.0 = Release|Any CPU
{19167D25-6383-46B4-9449-B9E364F809FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19167D25-6383-46B4-9449-B9E364F809FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {19167D25-6383-46B4-9449-B9E364F809FF}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {19167D25-6383-46B4-9449-B9E364F809FF}.Debug|x64.Build.0 = Debug|Any CPU
+ {19167D25-6383-46B4-9449-B9E364F809FF}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {19167D25-6383-46B4-9449-B9E364F809FF}.Debug|x86.Build.0 = Debug|Any CPU
{19167D25-6383-46B4-9449-B9E364F809FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19167D25-6383-46B4-9449-B9E364F809FF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {19167D25-6383-46B4-9449-B9E364F809FF}.Release|x64.ActiveCfg = Release|Any CPU
+ {19167D25-6383-46B4-9449-B9E364F809FF}.Release|x64.Build.0 = Release|Any CPU
+ {19167D25-6383-46B4-9449-B9E364F809FF}.Release|x86.ActiveCfg = Release|Any CPU
+ {19167D25-6383-46B4-9449-B9E364F809FF}.Release|x86.Build.0 = Release|Any CPU
{81A01446-A8AA-4F9D-BB9B-B66E21B2C348}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81A01446-A8AA-4F9D-BB9B-B66E21B2C348}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {81A01446-A8AA-4F9D-BB9B-B66E21B2C348}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {81A01446-A8AA-4F9D-BB9B-B66E21B2C348}.Debug|x64.Build.0 = Debug|Any CPU
+ {81A01446-A8AA-4F9D-BB9B-B66E21B2C348}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {81A01446-A8AA-4F9D-BB9B-B66E21B2C348}.Debug|x86.Build.0 = Debug|Any CPU
{81A01446-A8AA-4F9D-BB9B-B66E21B2C348}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81A01446-A8AA-4F9D-BB9B-B66E21B2C348}.Release|Any CPU.Build.0 = Release|Any CPU
+ {81A01446-A8AA-4F9D-BB9B-B66E21B2C348}.Release|x64.ActiveCfg = Release|Any CPU
+ {81A01446-A8AA-4F9D-BB9B-B66E21B2C348}.Release|x64.Build.0 = Release|Any CPU
+ {81A01446-A8AA-4F9D-BB9B-B66E21B2C348}.Release|x86.ActiveCfg = Release|Any CPU
+ {81A01446-A8AA-4F9D-BB9B-B66E21B2C348}.Release|x86.Build.0 = Release|Any CPU
{0E60F75D-C44B-428A-9252-A11C365E2C56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E60F75D-C44B-428A-9252-A11C365E2C56}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0E60F75D-C44B-428A-9252-A11C365E2C56}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {0E60F75D-C44B-428A-9252-A11C365E2C56}.Debug|x64.Build.0 = Debug|Any CPU
+ {0E60F75D-C44B-428A-9252-A11C365E2C56}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {0E60F75D-C44B-428A-9252-A11C365E2C56}.Debug|x86.Build.0 = Debug|Any CPU
{0E60F75D-C44B-428A-9252-A11C365E2C56}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E60F75D-C44B-428A-9252-A11C365E2C56}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0E60F75D-C44B-428A-9252-A11C365E2C56}.Release|x64.ActiveCfg = Release|Any CPU
+ {0E60F75D-C44B-428A-9252-A11C365E2C56}.Release|x64.Build.0 = Release|Any CPU
+ {0E60F75D-C44B-428A-9252-A11C365E2C56}.Release|x86.ActiveCfg = Release|Any CPU
+ {0E60F75D-C44B-428A-9252-A11C365E2C56}.Release|x86.Build.0 = Release|Any CPU
{FC86D3AB-778D-45D7-AF36-1F89FC16DE55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC86D3AB-778D-45D7-AF36-1F89FC16DE55}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FC86D3AB-778D-45D7-AF36-1F89FC16DE55}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {FC86D3AB-778D-45D7-AF36-1F89FC16DE55}.Debug|x64.Build.0 = Debug|Any CPU
+ {FC86D3AB-778D-45D7-AF36-1F89FC16DE55}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {FC86D3AB-778D-45D7-AF36-1F89FC16DE55}.Debug|x86.Build.0 = Debug|Any CPU
{FC86D3AB-778D-45D7-AF36-1F89FC16DE55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC86D3AB-778D-45D7-AF36-1F89FC16DE55}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FC86D3AB-778D-45D7-AF36-1F89FC16DE55}.Release|x64.ActiveCfg = Release|Any CPU
+ {FC86D3AB-778D-45D7-AF36-1F89FC16DE55}.Release|x64.Build.0 = Release|Any CPU
+ {FC86D3AB-778D-45D7-AF36-1F89FC16DE55}.Release|x86.ActiveCfg = Release|Any CPU
+ {FC86D3AB-778D-45D7-AF36-1F89FC16DE55}.Release|x86.Build.0 = Release|Any CPU
{4D1F117D-48D7-47AD-9DAC-3B2DB45E628A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D1F117D-48D7-47AD-9DAC-3B2DB45E628A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4D1F117D-48D7-47AD-9DAC-3B2DB45E628A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4D1F117D-48D7-47AD-9DAC-3B2DB45E628A}.Debug|x64.Build.0 = Debug|Any CPU
+ {4D1F117D-48D7-47AD-9DAC-3B2DB45E628A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4D1F117D-48D7-47AD-9DAC-3B2DB45E628A}.Debug|x86.Build.0 = Debug|Any CPU
{4D1F117D-48D7-47AD-9DAC-3B2DB45E628A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4D1F117D-48D7-47AD-9DAC-3B2DB45E628A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4D1F117D-48D7-47AD-9DAC-3B2DB45E628A}.Release|x64.ActiveCfg = Release|Any CPU
+ {4D1F117D-48D7-47AD-9DAC-3B2DB45E628A}.Release|x64.Build.0 = Release|Any CPU
+ {4D1F117D-48D7-47AD-9DAC-3B2DB45E628A}.Release|x86.ActiveCfg = Release|Any CPU
+ {4D1F117D-48D7-47AD-9DAC-3B2DB45E628A}.Release|x86.Build.0 = Release|Any CPU
{64E12D4C-FBB2-4004-8316-C886CBFC614B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64E12D4C-FBB2-4004-8316-C886CBFC614B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Debug|x64.Build.0 = Debug|Any CPU
+ {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Debug|x86.Build.0 = Debug|Any CPU
{64E12D4C-FBB2-4004-8316-C886CBFC614B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64E12D4C-FBB2-4004-8316-C886CBFC614B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Release|x64.ActiveCfg = Release|Any CPU
+ {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Release|x64.Build.0 = Release|Any CPU
+ {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Release|x86.ActiveCfg = Release|Any CPU
+ {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Release|x86.Build.0 = Release|Any CPU
{268BA5C7-C6FB-4A6B-875A-492659ED4573}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{268BA5C7-C6FB-4A6B-875A-492659ED4573}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {268BA5C7-C6FB-4A6B-875A-492659ED4573}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {268BA5C7-C6FB-4A6B-875A-492659ED4573}.Debug|x64.Build.0 = Debug|Any CPU
+ {268BA5C7-C6FB-4A6B-875A-492659ED4573}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {268BA5C7-C6FB-4A6B-875A-492659ED4573}.Debug|x86.Build.0 = Debug|Any CPU
{268BA5C7-C6FB-4A6B-875A-492659ED4573}.Release|Any CPU.ActiveCfg = Release|Any CPU
{268BA5C7-C6FB-4A6B-875A-492659ED4573}.Release|Any CPU.Build.0 = Release|Any CPU
+ {268BA5C7-C6FB-4A6B-875A-492659ED4573}.Release|x64.ActiveCfg = Release|Any CPU
+ {268BA5C7-C6FB-4A6B-875A-492659ED4573}.Release|x64.Build.0 = Release|Any CPU
+ {268BA5C7-C6FB-4A6B-875A-492659ED4573}.Release|x86.ActiveCfg = Release|Any CPU
+ {268BA5C7-C6FB-4A6B-875A-492659ED4573}.Release|x86.Build.0 = Release|Any CPU
{44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Debug|x64.Build.0 = Debug|Any CPU
+ {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Debug|x86.Build.0 = Debug|Any CPU
{44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Release|Any CPU.Build.0 = Release|Any CPU
+ {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Release|x64.ActiveCfg = Release|Any CPU
+ {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Release|x64.Build.0 = Release|Any CPU
+ {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Release|x86.ActiveCfg = Release|Any CPU
+ {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Release|x86.Build.0 = Release|Any CPU
{472BC645-9E2F-4205-A571-4D9184747EC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{472BC645-9E2F-4205-A571-4D9184747EC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {472BC645-9E2F-4205-A571-4D9184747EC5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {472BC645-9E2F-4205-A571-4D9184747EC5}.Debug|x64.Build.0 = Debug|Any CPU
+ {472BC645-9E2F-4205-A571-4D9184747EC5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {472BC645-9E2F-4205-A571-4D9184747EC5}.Debug|x86.Build.0 = Debug|Any CPU
{472BC645-9E2F-4205-A571-4D9184747EC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{472BC645-9E2F-4205-A571-4D9184747EC5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {472BC645-9E2F-4205-A571-4D9184747EC5}.Release|x64.ActiveCfg = Release|Any CPU
+ {472BC645-9E2F-4205-A571-4D9184747EC5}.Release|x64.Build.0 = Release|Any CPU
+ {472BC645-9E2F-4205-A571-4D9184747EC5}.Release|x86.ActiveCfg = Release|Any CPU
+ {472BC645-9E2F-4205-A571-4D9184747EC5}.Release|x86.Build.0 = Release|Any CPU
{84410C57-0F59-F31F-B921-4C1F3D3FF144}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{84410C57-0F59-F31F-B921-4C1F3D3FF144}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {84410C57-0F59-F31F-B921-4C1F3D3FF144}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {84410C57-0F59-F31F-B921-4C1F3D3FF144}.Debug|x64.Build.0 = Debug|Any CPU
+ {84410C57-0F59-F31F-B921-4C1F3D3FF144}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {84410C57-0F59-F31F-B921-4C1F3D3FF144}.Debug|x86.Build.0 = Debug|Any CPU
{84410C57-0F59-F31F-B921-4C1F3D3FF144}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84410C57-0F59-F31F-B921-4C1F3D3FF144}.Release|Any CPU.Build.0 = Release|Any CPU
+ {84410C57-0F59-F31F-B921-4C1F3D3FF144}.Release|x64.ActiveCfg = Release|Any CPU
+ {84410C57-0F59-F31F-B921-4C1F3D3FF144}.Release|x64.Build.0 = Release|Any CPU
+ {84410C57-0F59-F31F-B921-4C1F3D3FF144}.Release|x86.ActiveCfg = Release|Any CPU
+ {84410C57-0F59-F31F-B921-4C1F3D3FF144}.Release|x86.Build.0 = Release|Any CPU
{4FAE6C5E-53EE-4CCE-85A6-B7551A92C488}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FAE6C5E-53EE-4CCE-85A6-B7551A92C488}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4FAE6C5E-53EE-4CCE-85A6-B7551A92C488}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4FAE6C5E-53EE-4CCE-85A6-B7551A92C488}.Debug|x64.Build.0 = Debug|Any CPU
+ {4FAE6C5E-53EE-4CCE-85A6-B7551A92C488}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4FAE6C5E-53EE-4CCE-85A6-B7551A92C488}.Debug|x86.Build.0 = Debug|Any CPU
{4FAE6C5E-53EE-4CCE-85A6-B7551A92C488}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FAE6C5E-53EE-4CCE-85A6-B7551A92C488}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4FAE6C5E-53EE-4CCE-85A6-B7551A92C488}.Release|x64.ActiveCfg = Release|Any CPU
+ {4FAE6C5E-53EE-4CCE-85A6-B7551A92C488}.Release|x64.Build.0 = Release|Any CPU
+ {4FAE6C5E-53EE-4CCE-85A6-B7551A92C488}.Release|x86.ActiveCfg = Release|Any CPU
+ {4FAE6C5E-53EE-4CCE-85A6-B7551A92C488}.Release|x86.Build.0 = Release|Any CPU
{F7C9F778-EFDB-4F02-8F19-43A9F4A86003}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F7C9F778-EFDB-4F02-8F19-43A9F4A86003}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F7C9F778-EFDB-4F02-8F19-43A9F4A86003}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F7C9F778-EFDB-4F02-8F19-43A9F4A86003}.Debug|x64.Build.0 = Debug|Any CPU
+ {F7C9F778-EFDB-4F02-8F19-43A9F4A86003}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F7C9F778-EFDB-4F02-8F19-43A9F4A86003}.Debug|x86.Build.0 = Debug|Any CPU
{F7C9F778-EFDB-4F02-8F19-43A9F4A86003}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7C9F778-EFDB-4F02-8F19-43A9F4A86003}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F7C9F778-EFDB-4F02-8F19-43A9F4A86003}.Release|x64.ActiveCfg = Release|Any CPU
+ {F7C9F778-EFDB-4F02-8F19-43A9F4A86003}.Release|x64.Build.0 = Release|Any CPU
+ {F7C9F778-EFDB-4F02-8F19-43A9F4A86003}.Release|x86.ActiveCfg = Release|Any CPU
+ {F7C9F778-EFDB-4F02-8F19-43A9F4A86003}.Release|x86.Build.0 = Release|Any CPU
+ {67B76C68-F28F-4A01-AB02-DB661785C690}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {67B76C68-F28F-4A01-AB02-DB661785C690}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {67B76C68-F28F-4A01-AB02-DB661785C690}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {67B76C68-F28F-4A01-AB02-DB661785C690}.Debug|x64.Build.0 = Debug|Any CPU
+ {67B76C68-F28F-4A01-AB02-DB661785C690}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {67B76C68-F28F-4A01-AB02-DB661785C690}.Debug|x86.Build.0 = Debug|Any CPU
+ {67B76C68-F28F-4A01-AB02-DB661785C690}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {67B76C68-F28F-4A01-AB02-DB661785C690}.Release|Any CPU.Build.0 = Release|Any CPU
+ {67B76C68-F28F-4A01-AB02-DB661785C690}.Release|x64.ActiveCfg = Release|Any CPU
+ {67B76C68-F28F-4A01-AB02-DB661785C690}.Release|x64.Build.0 = Release|Any CPU
+ {67B76C68-F28F-4A01-AB02-DB661785C690}.Release|x86.ActiveCfg = Release|Any CPU
+ {67B76C68-F28F-4A01-AB02-DB661785C690}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -247,6 +530,7 @@ Global
{84410C57-0F59-F31F-B921-4C1F3D3FF144} = {4C68702C-1661-4AD9-83FD-E0B52B791969}
{4FAE6C5E-53EE-4CCE-85A6-B7551A92C488} = {C31F8C0F-8BCF-4959-9BA1-8645D058EAA0}
{F7C9F778-EFDB-4F02-8F19-43A9F4A86003} = {1C5D462D-168D-4D3F-B96E-CCE5517DB197}
+ {67B76C68-F28F-4A01-AB02-DB661785C690} = {1C5D462D-168D-4D3F-B96E-CCE5517DB197}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E20303B6-8AC9-4FFF-B645-4608309ADA94}
diff --git a/README.md b/README.md
index 7798527..a0f00b9 100644
--- a/README.md
+++ b/README.md
@@ -135,6 +135,9 @@
- **Cortex.Mediator.Behaviors.Transactional:** implementation of the Transactional Behaviors for Commands
[](https://www.nuget.org/packages/Cortex.Mediator.Behaviors.Transactional)
+- **Cortex.Mediator.SourceGenerator:** optional Roslyn source generator that eliminates runtime reflection by generating compile-time dispatch with switch-based routing and explicit DI registrations.
+[](https://www.nuget.org/packages/Cortex.Mediator.SourceGenerator)
+
- **Cortex.Vectors:** is a High‑performance vector types—Dense, Sparse, and Bit—for AI.
[](https://www.nuget.org/packages/Cortex.Vectors)
diff --git a/src/Cortex.Mediator.SourceGenerator/Assets/andyX.png b/src/Cortex.Mediator.SourceGenerator/Assets/andyX.png
new file mode 100644
index 0000000..101a1fb
Binary files /dev/null and b/src/Cortex.Mediator.SourceGenerator/Assets/andyX.png differ
diff --git a/src/Cortex.Mediator.SourceGenerator/Assets/license.md b/src/Cortex.Mediator.SourceGenerator/Assets/license.md
new file mode 100644
index 0000000..caa98b4
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/Assets/license.md
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2026 Buildersoft
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/src/Cortex.Mediator.SourceGenerator/Cortex.Mediator.SourceGenerator.csproj b/src/Cortex.Mediator.SourceGenerator/Cortex.Mediator.SourceGenerator.csproj
new file mode 100644
index 0000000..5a8e199
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/Cortex.Mediator.SourceGenerator.csproj
@@ -0,0 +1,77 @@
+
+
+
+ netstandard2.0
+ 12
+ true
+ true
+ false
+ true
+ true
+
+ 1.0.0
+ 1.0.0
+ Buildersoft Cortex Framework
+ Buildersoft
+ Buildersoft,EnesHoxha
+ Copyright © Buildersoft 2026
+
+
+ Roslyn source generator for Cortex.Mediator that eliminates runtime reflection by generating compile-time dispatch. Produces a GeneratedMediator with switch-based routing and explicit DI registrations, enabling NativeAOT compatibility and faster cold starts.
+
+
+ https://github.com/buildersoftio/cortex
+ cortex vortex mediator eda cqrs source-generator
+
+ 1.0.0
+ license.md
+ andyX.png
+ Cortex.Mediator.SourceGenerator
+ True
+ True
+ True
+
+ Just as the Cortex in our brains handles complex processing efficiently, Cortex Data Framework brings brainpower to your data management!
+ https://buildersoft.io/
+ Cortex Mediator Source Generator
+ README.md
+
+
+
+
+
+
+
+
+
+ True
+ \
+ Always
+
+
+
+
+ True
+
+
+
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Cortex.Mediator.SourceGenerator/CortexMediatorGenerator.cs b/src/Cortex.Mediator.SourceGenerator/CortexMediatorGenerator.cs
new file mode 100644
index 0000000..700b0e6
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/CortexMediatorGenerator.cs
@@ -0,0 +1,247 @@
+using Cortex.Mediator.SourceGenerator.Diagnostics;
+using Cortex.Mediator.SourceGenerator.Discovery;
+using Cortex.Mediator.SourceGenerator.Emitters;
+using Cortex.Mediator.SourceGenerator.Models;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading;
+
+namespace Cortex.Mediator.SourceGenerator
+{
+ [Generator(LanguageNames.CSharp)]
+ public sealed class CortexMediatorGenerator : IIncrementalGenerator
+ {
+ private const string AttributeFullName = "Cortex.Mediator.SourceGeneration.CortexMediatorGenerationAttribute";
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ // Step 1: Emit the marker attribute so consumers only need the NuGet package
+ context.RegisterPostInitializationOutput(static ctx =>
+ {
+ ctx.AddSource("CortexMediatorGenerationAttribute.g.cs", @"//
+namespace Cortex.Mediator.SourceGeneration
+{
+ ///
+ /// Apply this attribute at assembly level to enable the Cortex Mediator source generator.
+ ///
+ [System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = false)]
+ internal sealed class CortexMediatorGenerationAttribute : System.Attribute
+ {
+ }
+}
+");
+ });
+
+ // Step 2: Check if the assembly attribute is present (opt-in gate)
+ var hasAttribute = context.SyntaxProvider
+ .ForAttributeWithMetadataName(
+ AttributeFullName,
+ predicate: static (node, _) => node is CompilationUnitSyntax,
+ transform: static (ctx, _) => true)
+ .Collect()
+ .Select(static (items, _) => items.Length > 0);
+
+ // Step 3: Discover handlers from class declarations
+ var handlerRegistrations = context.SyntaxProvider
+ .CreateSyntaxProvider(
+ predicate: static (node, _) => node is ClassDeclarationSyntax cds && cds.BaseList != null,
+ transform: static (ctx, ct) => GetHandlerRegistrations(ctx, ct))
+ .Where(static r => !r.IsEmpty)
+ .SelectMany(static (r, _) => r);
+
+ var collectedHandlers = handlerRegistrations.Collect();
+
+ // Step 4: Discover message types for diagnostics
+ var messageRegistrations = context.SyntaxProvider
+ .CreateSyntaxProvider(
+ predicate: static (node, _) => node is ClassDeclarationSyntax cds && cds.BaseList != null,
+ transform: static (ctx, ct) => GetMessageRegistrations(ctx, ct))
+ .Where(static r => !r.IsEmpty)
+ .SelectMany(static (r, _) => r);
+
+ var collectedMessages = messageRegistrations.Collect();
+
+ // Step 5: Combine everything and generate
+ var combined = collectedHandlers.Combine(collectedMessages).Combine(hasAttribute);
+
+ context.RegisterSourceOutput(combined, static (spc, input) =>
+ {
+ var ((handlers, messages), isEnabled) = input;
+
+ if (!isEnabled)
+ return;
+
+ // Report diagnostics
+ ReportDiagnostics(spc, handlers, messages);
+
+ // Emit GeneratedMediator
+ var mediatorSource = GeneratedMediatorEmitter.Emit(handlers);
+ spc.AddSource("GeneratedMediator.g.cs", mediatorSource);
+
+ // Emit GeneratedServiceCollectionExtensions
+ var diSource = GeneratedDIEmitter.Emit(handlers);
+ spc.AddSource("GeneratedServiceCollectionExtensions.g.cs", diSource);
+ });
+ }
+
+ private static ImmutableArray GetHandlerRegistrations(
+ GeneratorSyntaxContext context,
+ CancellationToken cancellationToken)
+ {
+ var classDeclaration = (ClassDeclarationSyntax)context.Node;
+ var symbol = context.SemanticModel.GetDeclaredSymbol(classDeclaration, cancellationToken);
+
+ if (symbol is not INamedTypeSymbol namedType)
+ return ImmutableArray.Empty;
+
+ return HandlerDiscovery.FindHandlers(namedType);
+ }
+
+ private static ImmutableArray GetMessageRegistrations(
+ GeneratorSyntaxContext context,
+ CancellationToken cancellationToken)
+ {
+ var classDeclaration = (ClassDeclarationSyntax)context.Node;
+ var symbol = context.SemanticModel.GetDeclaredSymbol(classDeclaration, cancellationToken);
+
+ if (symbol is not INamedTypeSymbol namedType)
+ return ImmutableArray.Empty;
+
+ return MessageDiscovery.FindMessages(namedType);
+ }
+
+ private static void ReportDiagnostics(
+ SourceProductionContext spc,
+ ImmutableArray handlers,
+ ImmutableArray messages)
+ {
+ var handledCommands = new HashSet(
+ handlers.Where(h => h.Kind == InterfaceKind.ReturningCommand)
+ .Select(h => h.MessageFullyQualifiedName));
+
+ var handledVoidCommands = new HashSet(
+ handlers.Where(h => h.Kind == InterfaceKind.VoidCommand)
+ .Select(h => h.MessageFullyQualifiedName));
+
+ var handledQueries = new HashSet(
+ handlers.Where(h => h.Kind == InterfaceKind.Query)
+ .Select(h => h.MessageFullyQualifiedName));
+
+ var handledStreamQueries = new HashSet(
+ handlers.Where(h => h.Kind == InterfaceKind.StreamQuery)
+ .Select(h => h.MessageFullyQualifiedName));
+
+ var handledNotifications = new HashSet(
+ handlers.Where(h => h.Kind == InterfaceKind.Notification)
+ .Select(h => h.MessageFullyQualifiedName));
+
+ // Check for unhandled messages
+ foreach (var msg in messages)
+ {
+ switch (msg.Kind)
+ {
+ case InterfaceKind.ReturningCommand:
+ if (!handledCommands.Contains(msg.MessageFullyQualifiedName))
+ {
+ spc.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.CommandHasNoHandler,
+ msg.Location,
+ msg.MessageFullyQualifiedName));
+ }
+ break;
+
+ case InterfaceKind.VoidCommand:
+ if (!handledVoidCommands.Contains(msg.MessageFullyQualifiedName))
+ {
+ spc.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.CommandHasNoHandler,
+ msg.Location,
+ msg.MessageFullyQualifiedName));
+ }
+ break;
+
+ case InterfaceKind.Query:
+ if (!handledQueries.Contains(msg.MessageFullyQualifiedName))
+ {
+ spc.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.QueryHasNoHandler,
+ msg.Location,
+ msg.MessageFullyQualifiedName));
+ }
+ break;
+
+ case InterfaceKind.StreamQuery:
+ if (!handledStreamQueries.Contains(msg.MessageFullyQualifiedName))
+ {
+ spc.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.StreamQueryHasNoHandler,
+ msg.Location,
+ msg.MessageFullyQualifiedName));
+ }
+ break;
+
+ case InterfaceKind.Notification:
+ if (!handledNotifications.Contains(msg.MessageFullyQualifiedName))
+ {
+ spc.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.NotificationHasNoHandler,
+ msg.Location,
+ msg.MessageFullyQualifiedName));
+ }
+ break;
+ }
+ }
+
+ // Check for duplicate command handlers
+ var commandHandlerGroups = handlers
+ .Where(h => h.Kind == InterfaceKind.ReturningCommand)
+ .GroupBy(h => h.MessageFullyQualifiedName)
+ .Where(g => g.Count() > 1);
+
+ foreach (var group in commandHandlerGroups)
+ {
+ var handlerNames = string.Join(", ", group.Select(h => h.HandlerFullyQualifiedName));
+ spc.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.MultipleHandlersForCommand,
+ Location.None,
+ group.Key,
+ handlerNames));
+ }
+
+ // Check for duplicate void command handlers
+ var voidCommandHandlerGroups = handlers
+ .Where(h => h.Kind == InterfaceKind.VoidCommand)
+ .GroupBy(h => h.MessageFullyQualifiedName)
+ .Where(g => g.Count() > 1);
+
+ foreach (var group in voidCommandHandlerGroups)
+ {
+ var handlerNames = string.Join(", ", group.Select(h => h.HandlerFullyQualifiedName));
+ spc.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.MultipleHandlersForCommand,
+ Location.None,
+ group.Key,
+ handlerNames));
+ }
+
+ // Check for duplicate query handlers
+ var queryHandlerGroups = handlers
+ .Where(h => h.Kind == InterfaceKind.Query)
+ .GroupBy(h => h.MessageFullyQualifiedName)
+ .Where(g => g.Count() > 1);
+
+ foreach (var group in queryHandlerGroups)
+ {
+ var handlerNames = string.Join(", ", group.Select(h => h.HandlerFullyQualifiedName));
+ spc.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.MultipleHandlersForQuery,
+ Location.None,
+ group.Key,
+ handlerNames));
+ }
+ }
+ }
+}
diff --git a/src/Cortex.Mediator.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs b/src/Cortex.Mediator.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs
new file mode 100644
index 0000000..4a835e6
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs
@@ -0,0 +1,55 @@
+using Microsoft.CodeAnalysis;
+
+namespace Cortex.Mediator.SourceGenerator.Diagnostics
+{
+ internal static class DiagnosticDescriptors
+ {
+ public static readonly DiagnosticDescriptor CommandHasNoHandler = new(
+ id: "CXMED001",
+ title: "Command has no handler",
+ messageFormat: "Command '{0}' has no registered ICommandHandler<{0}, TResult>",
+ category: "Cortex.Mediator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ public static readonly DiagnosticDescriptor QueryHasNoHandler = new(
+ id: "CXMED002",
+ title: "Query has no handler",
+ messageFormat: "Query '{0}' has no registered IQueryHandler<{0}, TResult>",
+ category: "Cortex.Mediator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ public static readonly DiagnosticDescriptor StreamQueryHasNoHandler = new(
+ id: "CXMED003",
+ title: "StreamQuery has no handler",
+ messageFormat: "StreamQuery '{0}' has no registered IStreamQueryHandler<{0}, TResult>",
+ category: "Cortex.Mediator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ public static readonly DiagnosticDescriptor NotificationHasNoHandler = new(
+ id: "CXMED004",
+ title: "Notification has no handler",
+ messageFormat: "Notification '{0}' has no registered INotificationHandler<{0}>",
+ category: "Cortex.Mediator",
+ defaultSeverity: DiagnosticSeverity.Info,
+ isEnabledByDefault: true);
+
+ public static readonly DiagnosticDescriptor MultipleHandlersForCommand = new(
+ id: "CXMED005",
+ title: "Multiple handlers for same command",
+ messageFormat: "Command '{0}' has multiple handlers: {1}. Only one handler per command is allowed.",
+ category: "Cortex.Mediator",
+ defaultSeverity: DiagnosticSeverity.Error,
+ isEnabledByDefault: true);
+
+ public static readonly DiagnosticDescriptor MultipleHandlersForQuery = new(
+ id: "CXMED006",
+ title: "Multiple handlers for same query",
+ messageFormat: "Query '{0}' has multiple handlers: {1}. Only one handler per query is allowed.",
+ category: "Cortex.Mediator",
+ defaultSeverity: DiagnosticSeverity.Error,
+ isEnabledByDefault: true);
+ }
+}
diff --git a/src/Cortex.Mediator.SourceGenerator/Discovery/HandlerDiscovery.cs b/src/Cortex.Mediator.SourceGenerator/Discovery/HandlerDiscovery.cs
new file mode 100644
index 0000000..96275fd
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/Discovery/HandlerDiscovery.cs
@@ -0,0 +1,61 @@
+using Cortex.Mediator.SourceGenerator.Models;
+using Microsoft.CodeAnalysis;
+using System.Collections.Immutable;
+
+namespace Cortex.Mediator.SourceGenerator.Discovery
+{
+ internal static class HandlerDiscovery
+ {
+ private static readonly (string InterfaceName, InterfaceKind Kind, int TypeArgCount)[] HandlerInterfaces =
+ {
+ ("Cortex.Mediator.Commands.ICommandHandler", InterfaceKind.ReturningCommand, 2),
+ ("Cortex.Mediator.Commands.ICommandHandler", InterfaceKind.VoidCommand, 1),
+ ("Cortex.Mediator.Queries.IQueryHandler", InterfaceKind.Query, 2),
+ ("Cortex.Mediator.Streaming.IStreamQueryHandler", InterfaceKind.StreamQuery, 2),
+ ("Cortex.Mediator.Notifications.INotificationHandler", InterfaceKind.Notification, 1),
+ ("Cortex.Mediator.Processors.IRequestPreProcessor", InterfaceKind.PreProcessor, 1),
+ ("Cortex.Mediator.Processors.IRequestPostProcessor", InterfaceKind.PostProcessorWithResponse, 2),
+ ("Cortex.Mediator.Processors.IRequestPostProcessor", InterfaceKind.PostProcessorVoid, 1),
+ };
+
+ public static ImmutableArray FindHandlers(INamedTypeSymbol classSymbol)
+ {
+ var builder = ImmutableArray.CreateBuilder();
+
+ if (classSymbol.IsAbstractOrInterface() || classSymbol.IsGenericType)
+ return builder.ToImmutable();
+
+ foreach (var iface in classSymbol.AllInterfaces)
+ {
+ if (!iface.IsGenericType)
+ continue;
+
+ var constructed = iface.ConstructedFrom;
+ var typeArgCount = constructed.TypeArguments.Length;
+
+ foreach (var (interfaceName, kind, expectedArgCount) in HandlerInterfaces)
+ {
+ if (typeArgCount != expectedArgCount)
+ continue;
+
+ if (!constructed.HasFullyQualifiedMetadataName(interfaceName))
+ continue;
+
+ var messageType = iface.TypeArguments[0];
+ var resultType = typeArgCount >= 2 ? iface.TypeArguments[1] : null;
+
+ var registration = new HandlerRegistration(
+ kind: kind,
+ handlerFullyQualifiedName: classSymbol.ToFullyQualifiedString(),
+ messageFullyQualifiedName: messageType.ToFullyQualifiedString(),
+ resultFullyQualifiedName: resultType?.ToFullyQualifiedString(),
+ serviceInterfaceFullyQualifiedName: iface.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+
+ builder.Add(registration);
+ }
+ }
+
+ return builder.ToImmutable();
+ }
+ }
+}
diff --git a/src/Cortex.Mediator.SourceGenerator/Discovery/MessageDiscovery.cs b/src/Cortex.Mediator.SourceGenerator/Discovery/MessageDiscovery.cs
new file mode 100644
index 0000000..27a52f2
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/Discovery/MessageDiscovery.cs
@@ -0,0 +1,77 @@
+using Cortex.Mediator.SourceGenerator.Models;
+using Microsoft.CodeAnalysis;
+using System.Collections.Immutable;
+
+namespace Cortex.Mediator.SourceGenerator.Discovery
+{
+ internal static class MessageDiscovery
+ {
+ private static readonly (string InterfaceName, InterfaceKind Kind, bool HasResult)[] MessageInterfaces =
+ {
+ ("Cortex.Mediator.Commands.ICommand", InterfaceKind.ReturningCommand, true),
+ ("Cortex.Mediator.Commands.ICommand", InterfaceKind.VoidCommand, false),
+ ("Cortex.Mediator.Queries.IQuery", InterfaceKind.Query, true),
+ ("Cortex.Mediator.Streaming.IStreamQuery", InterfaceKind.StreamQuery, true),
+ ("Cortex.Mediator.Notifications.INotification", InterfaceKind.Notification, false),
+ };
+
+ public static ImmutableArray FindMessages(INamedTypeSymbol classSymbol)
+ {
+ var builder = ImmutableArray.CreateBuilder();
+
+ if (classSymbol.IsAbstractOrInterface())
+ return builder.ToImmutable();
+
+ foreach (var iface in classSymbol.AllInterfaces)
+ {
+ foreach (var (interfaceName, kind, hasResult) in MessageInterfaces)
+ {
+ if (hasResult)
+ {
+ if (!iface.IsGenericType || iface.TypeArguments.Length != 1)
+ continue;
+
+ if (!iface.ConstructedFrom.HasFullyQualifiedMetadataName(interfaceName))
+ continue;
+
+ var resultType = iface.TypeArguments[0];
+ var location = classSymbol.Locations.Length > 0
+ ? classSymbol.Locations[0]
+ : Location.None;
+
+ builder.Add(new MessageRegistration(
+ kind: kind,
+ messageFullyQualifiedName: classSymbol.ToFullyQualifiedString(),
+ resultFullyQualifiedName: resultType.ToFullyQualifiedString(),
+ location: location));
+ }
+ else
+ {
+ // Non-generic interface: ICommand (void) or INotification
+ if (iface.IsGenericType)
+ continue;
+
+ var ifaceName = iface.ToDisplayString(new SymbolDisplayFormat(
+ globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
+ typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces));
+
+ if (ifaceName != interfaceName)
+ continue;
+
+ var location = classSymbol.Locations.Length > 0
+ ? classSymbol.Locations[0]
+ : Location.None;
+
+ builder.Add(new MessageRegistration(
+ kind: kind,
+ messageFullyQualifiedName: classSymbol.ToFullyQualifiedString(),
+ resultFullyQualifiedName: null,
+ location: location));
+ }
+ }
+ }
+
+ return builder.ToImmutable();
+ }
+ }
+}
diff --git a/src/Cortex.Mediator.SourceGenerator/Discovery/SymbolExtensions.cs b/src/Cortex.Mediator.SourceGenerator/Discovery/SymbolExtensions.cs
new file mode 100644
index 0000000..161a989
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/Discovery/SymbolExtensions.cs
@@ -0,0 +1,34 @@
+using Microsoft.CodeAnalysis;
+
+namespace Cortex.Mediator.SourceGenerator.Discovery
+{
+ internal static class SymbolExtensions
+ {
+ public static string ToFullyQualifiedString(this ITypeSymbol symbol)
+ {
+ return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ }
+
+ public static string ToGlobalPrefixed(this ITypeSymbol symbol)
+ {
+ var fqn = symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ return fqn;
+ }
+
+ public static bool HasFullyQualifiedMetadataName(this INamedTypeSymbol symbol, string metadataName)
+ {
+ var constructed = symbol.ConstructedFrom;
+ var name = constructed.ToDisplayString(new SymbolDisplayFormat(
+ globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
+ typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
+ genericsOptions: SymbolDisplayGenericsOptions.None));
+
+ return name == metadataName;
+ }
+
+ public static bool IsAbstractOrInterface(this INamedTypeSymbol symbol)
+ {
+ return symbol.IsAbstract || symbol.TypeKind == TypeKind.Interface;
+ }
+ }
+}
diff --git a/src/Cortex.Mediator.SourceGenerator/Emitters/GeneratedDIEmitter.cs b/src/Cortex.Mediator.SourceGenerator/Emitters/GeneratedDIEmitter.cs
new file mode 100644
index 0000000..f22cedf
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/Emitters/GeneratedDIEmitter.cs
@@ -0,0 +1,104 @@
+using Cortex.Mediator.SourceGenerator.Models;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace Cortex.Mediator.SourceGenerator.Emitters
+{
+ internal static class GeneratedDIEmitter
+ {
+ public static string Emit(ImmutableArray handlers)
+ {
+ var sb = new SourceTextBuilder();
+
+ sb.AppendLine("// ");
+ sb.AppendLine("#nullable enable");
+ sb.AppendLine();
+ sb.AppendLine("using Cortex.Mediator;");
+ sb.AppendLine("using Cortex.Mediator.DependencyInjection;");
+ sb.AppendLine("using Cortex.Mediator.Notifications;");
+ sb.AppendLine("using Cortex.Mediator.SourceGeneration;");
+ sb.AppendLine("using Microsoft.Extensions.DependencyInjection;");
+ sb.AppendLine("using System;");
+ sb.AppendLine();
+ sb.AppendLine("namespace Cortex.Mediator.SourceGeneration");
+ sb.OpenBrace();
+
+ sb.AppendLine("/// ");
+ sb.AppendLine("/// Source-generated DI registration that avoids assembly scanning.");
+ sb.AppendLine("/// ");
+ sb.AppendLine("[System.CodeDom.Compiler.GeneratedCodeAttribute(\"Cortex.Mediator.SourceGenerator\", \"1.0.0\")]");
+ sb.AppendLine("internal static class GeneratedServiceCollectionExtensions");
+ sb.OpenBrace();
+
+ sb.AppendLine("public static IServiceCollection AddCortexGeneratedMediator(");
+ sb.AppendLine(" this IServiceCollection services,");
+ sb.AppendLine(" Action? configure = null)");
+ sb.OpenBrace();
+
+ sb.AppendLine("var options = new MediatorOptions();");
+ sb.AppendLine("configure?.Invoke(options);");
+ sb.AppendLine();
+
+ sb.AppendLine("// Register the generated mediator");
+ sb.AppendLine("services.AddScoped();");
+ sb.AppendLine();
+
+ sb.AppendLine("// Register notification publish strategy");
+ sb.AppendLine("services.AddSingleton(typeof(INotificationPublishStrategy), options.NotificationPublishStrategyType);");
+ sb.AppendLine();
+
+ // Group handlers by kind for readability
+ var handlerKinds = new[]
+ {
+ InterfaceKind.ReturningCommand,
+ InterfaceKind.VoidCommand,
+ InterfaceKind.Query,
+ InterfaceKind.StreamQuery,
+ InterfaceKind.Notification,
+ };
+
+ var processorKinds = new[]
+ {
+ InterfaceKind.PreProcessor,
+ InterfaceKind.PostProcessorWithResponse,
+ InterfaceKind.PostProcessorVoid,
+ };
+
+ // Emit handler registrations
+ var handlerRegs = handlers.Where(h => handlerKinds.Contains(h.Kind)).ToArray();
+ if (handlerRegs.Length > 0)
+ {
+ sb.AppendLine("// Handler registrations");
+ foreach (var reg in handlerRegs)
+ {
+ sb.AppendLine($"services.Add(new ServiceDescriptor(typeof({reg.ServiceInterfaceFullyQualifiedName}), typeof({reg.HandlerFullyQualifiedName}), ServiceLifetime.Scoped));");
+ }
+ sb.AppendLine();
+ }
+
+ // Emit processor registrations
+ var processorRegs = handlers.Where(h => processorKinds.Contains(h.Kind)).ToArray();
+ if (processorRegs.Length > 0)
+ {
+ sb.AppendLine("// Processor registrations");
+ foreach (var reg in processorRegs)
+ {
+ sb.AppendLine($"services.Add(new ServiceDescriptor(typeof({reg.ServiceInterfaceFullyQualifiedName}), typeof({reg.HandlerFullyQualifiedName}), ServiceLifetime.Transient));");
+ }
+ sb.AppendLine();
+ }
+
+ sb.AppendLine("// Register pipeline behaviors (runtime-configured, cannot be generated)");
+ sb.AppendLine("ServiceCollectionExtensions.RegisterPipelineBehaviors(services, options);");
+ sb.AppendLine();
+ sb.AppendLine("return services;");
+
+ sb.CloseBrace(); // method
+
+ sb.CloseBrace(); // class
+ sb.CloseBrace(); // namespace
+
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/Cortex.Mediator.SourceGenerator/Emitters/GeneratedMediatorEmitter.cs b/src/Cortex.Mediator.SourceGenerator/Emitters/GeneratedMediatorEmitter.cs
new file mode 100644
index 0000000..d6722dc
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/Emitters/GeneratedMediatorEmitter.cs
@@ -0,0 +1,459 @@
+using Cortex.Mediator.SourceGenerator.Models;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace Cortex.Mediator.SourceGenerator.Emitters
+{
+ internal static class GeneratedMediatorEmitter
+ {
+ public static string Emit(ImmutableArray handlers)
+ {
+ var returningCommands = handlers
+ .Where(h => h.Kind == InterfaceKind.ReturningCommand)
+ .ToArray();
+
+ var voidCommands = handlers
+ .Where(h => h.Kind == InterfaceKind.VoidCommand)
+ .ToArray();
+
+ var queries = handlers
+ .Where(h => h.Kind == InterfaceKind.Query)
+ .ToArray();
+
+ var streamQueries = handlers
+ .Where(h => h.Kind == InterfaceKind.StreamQuery)
+ .ToArray();
+
+ var notifications = handlers
+ .Where(h => h.Kind == InterfaceKind.Notification)
+ .ToArray();
+
+ var sb = new SourceTextBuilder();
+
+ sb.AppendLine("// ");
+ sb.AppendLine("#nullable enable");
+ sb.AppendLine();
+ sb.AppendLine("using Cortex.Mediator;");
+ sb.AppendLine("using Cortex.Mediator.Commands;");
+ sb.AppendLine("using Cortex.Mediator.Notifications;");
+ sb.AppendLine("using Cortex.Mediator.Queries;");
+ sb.AppendLine("using Cortex.Mediator.Streaming;");
+ sb.AppendLine("using Microsoft.Extensions.DependencyInjection;");
+ sb.AppendLine("using System;");
+ sb.AppendLine("using System.Collections.Generic;");
+ sb.AppendLine("using System.Linq;");
+ sb.AppendLine("using System.Threading;");
+ sb.AppendLine("using System.Threading.Tasks;");
+ sb.AppendLine();
+ sb.AppendLine("namespace Cortex.Mediator.SourceGeneration");
+ sb.OpenBrace();
+
+ sb.AppendLine("/// ");
+ sb.AppendLine("/// Source-generated mediator that uses compile-time dispatch instead of runtime reflection.");
+ sb.AppendLine("/// ");
+ sb.AppendLine("[System.CodeDom.Compiler.GeneratedCodeAttribute(\"Cortex.Mediator.SourceGenerator\", \"1.0.0\")]");
+ sb.AppendLine("internal sealed class GeneratedMediator : IMediator");
+ sb.OpenBrace();
+
+ sb.AppendLine("private readonly IServiceProvider _serviceProvider;");
+ sb.AppendLine();
+ sb.AppendLine("public GeneratedMediator(IServiceProvider serviceProvider)");
+ sb.OpenBrace();
+ sb.AppendLine("_serviceProvider = serviceProvider;");
+ sb.CloseBrace();
+
+ // SendCommandAsync - strongly typed (no reflection needed)
+ sb.AppendLine();
+ EmitStronglyTypedSendCommandReturning(sb);
+
+ // SendCommandAsync(ICommand) - switch dispatch
+ sb.AppendLine();
+ EmitSwitchSendCommandReturning(sb, returningCommands);
+
+ // SendCommandAsync(TCommand) - strongly typed (no reflection needed)
+ sb.AppendLine();
+ EmitStronglyTypedSendCommandVoid(sb);
+
+ // SendCommandAsync(ICommand) - switch dispatch
+ sb.AppendLine();
+ EmitSwitchSendCommandVoid(sb, voidCommands);
+
+ // SendQueryAsync - strongly typed
+ sb.AppendLine();
+ EmitStronglyTypedSendQuery(sb);
+
+ // SendQueryAsync(IQuery) - switch dispatch
+ sb.AppendLine();
+ EmitSwitchSendQuery(sb, queries);
+
+ // PublishAsync - strongly typed
+ sb.AppendLine();
+ EmitStronglyTypedPublish(sb);
+
+ // PublishAsync(INotification) - switch dispatch
+ sb.AppendLine();
+ EmitSwitchPublish(sb, notifications);
+
+ // CreateStream - strongly typed
+ sb.AppendLine();
+ EmitStronglyTypedCreateStream(sb);
+
+ // CreateStream(IStreamQuery) - switch dispatch
+ sb.AppendLine();
+ EmitSwitchCreateStream(sb, streamQueries);
+
+ // Pipeline wrapper classes
+ sb.AppendLine();
+ EmitPipelineWrapperClasses(sb);
+
+ sb.CloseBrace(); // class
+ sb.CloseBrace(); // namespace
+
+ return sb.ToString();
+ }
+
+ private static void EmitStronglyTypedSendCommandReturning(SourceTextBuilder sb)
+ {
+ sb.AppendLine("public async Task SendCommandAsync(TCommand command, CancellationToken cancellationToken = default)");
+ sb.AppendLine(" where TCommand : ICommand");
+ sb.OpenBrace();
+ sb.AppendLine("var handler = _serviceProvider.GetRequiredService>();");
+ sb.AppendLine();
+ sb.AppendLine("foreach (var behavior in _serviceProvider.GetServices>().Reverse())");
+ sb.OpenBrace();
+ sb.AppendLine("handler = new PipelineBehaviorNextDelegate(behavior, handler);");
+ sb.CloseBrace();
+ sb.AppendLine();
+ sb.AppendLine("return await handler.Handle(command, cancellationToken);");
+ sb.CloseBrace();
+ }
+
+ private static void EmitSwitchSendCommandReturning(SourceTextBuilder sb, HandlerRegistration[] registrations)
+ {
+ sb.AppendLine("public Task SendCommandAsync(ICommand command, CancellationToken cancellationToken = default)");
+ sb.OpenBrace();
+ sb.AppendLine("if (command == null) throw new ArgumentNullException(nameof(command));");
+ sb.AppendLine();
+
+ if (registrations.Length > 0)
+ {
+ sb.AppendLine("switch (command)");
+ sb.OpenBrace();
+ foreach (var reg in registrations)
+ {
+ sb.AppendLine($"case {reg.MessageFullyQualifiedName} typed:");
+ sb.Indent();
+ sb.AppendLine($"return (Task)(object)SendCommandAsync<{reg.MessageFullyQualifiedName}, {reg.ResultFullyQualifiedName}>(typed, cancellationToken);");
+ sb.Unindent();
+ }
+ sb.AppendLine("default:");
+ sb.Indent();
+ sb.AppendLine("throw new InvalidOperationException($\"No handler registered for command type {command.GetType().FullName}. Ensure the handler is in the same compilation where [assembly: CortexMediatorGeneration] is applied.\");");
+ sb.Unindent();
+ sb.CloseBrace();
+ }
+ else
+ {
+ sb.AppendLine("throw new InvalidOperationException($\"No handler registered for command type {command.GetType().FullName}. Ensure the handler is in the same compilation where [assembly: CortexMediatorGeneration] is applied.\");");
+ }
+
+ sb.CloseBrace();
+ }
+
+ private static void EmitStronglyTypedSendCommandVoid(SourceTextBuilder sb)
+ {
+ sb.AppendLine("public async Task SendCommandAsync(TCommand command, CancellationToken cancellationToken = default)");
+ sb.AppendLine(" where TCommand : ICommand");
+ sb.OpenBrace();
+ sb.AppendLine("var handler = _serviceProvider.GetRequiredService>();");
+ sb.AppendLine();
+ sb.AppendLine("foreach (var behavior in _serviceProvider.GetServices>().Reverse())");
+ sb.OpenBrace();
+ sb.AppendLine("handler = new VoidPipelineBehaviorNextDelegate(behavior, handler);");
+ sb.CloseBrace();
+ sb.AppendLine();
+ sb.AppendLine("await handler.Handle(command, cancellationToken);");
+ sb.CloseBrace();
+ }
+
+ private static void EmitSwitchSendCommandVoid(SourceTextBuilder sb, HandlerRegistration[] registrations)
+ {
+ sb.AppendLine("public Task SendCommandAsync(ICommand command, CancellationToken cancellationToken = default)");
+ sb.OpenBrace();
+ sb.AppendLine("if (command == null) throw new ArgumentNullException(nameof(command));");
+ sb.AppendLine();
+
+ if (registrations.Length > 0)
+ {
+ sb.AppendLine("switch (command)");
+ sb.OpenBrace();
+ foreach (var reg in registrations)
+ {
+ sb.AppendLine($"case {reg.MessageFullyQualifiedName} typed:");
+ sb.Indent();
+ sb.AppendLine($"return SendCommandAsync<{reg.MessageFullyQualifiedName}>(typed, cancellationToken);");
+ sb.Unindent();
+ }
+ sb.AppendLine("default:");
+ sb.Indent();
+ sb.AppendLine("throw new InvalidOperationException($\"No handler registered for command type {command.GetType().FullName}. Ensure the handler is in the same compilation where [assembly: CortexMediatorGeneration] is applied.\");");
+ sb.Unindent();
+ sb.CloseBrace();
+ }
+ else
+ {
+ sb.AppendLine("throw new InvalidOperationException($\"No handler registered for command type {command.GetType().FullName}. Ensure the handler is in the same compilation where [assembly: CortexMediatorGeneration] is applied.\");");
+ }
+
+ sb.CloseBrace();
+ }
+
+ private static void EmitStronglyTypedSendQuery(SourceTextBuilder sb)
+ {
+ sb.AppendLine("public async Task SendQueryAsync(TQuery query, CancellationToken cancellationToken = default)");
+ sb.AppendLine(" where TQuery : IQuery");
+ sb.OpenBrace();
+ sb.AppendLine("var handler = _serviceProvider.GetRequiredService>();");
+ sb.AppendLine();
+ sb.AppendLine("foreach (var behavior in _serviceProvider.GetServices>().Reverse())");
+ sb.OpenBrace();
+ sb.AppendLine("handler = new QueryPipelineBehaviorNextDelegate(behavior, handler);");
+ sb.CloseBrace();
+ sb.AppendLine();
+ sb.AppendLine("return await handler.Handle(query, cancellationToken);");
+ sb.CloseBrace();
+ }
+
+ private static void EmitSwitchSendQuery(SourceTextBuilder sb, HandlerRegistration[] registrations)
+ {
+ sb.AppendLine("public Task SendQueryAsync(IQuery query, CancellationToken cancellationToken = default)");
+ sb.OpenBrace();
+ sb.AppendLine("if (query == null) throw new ArgumentNullException(nameof(query));");
+ sb.AppendLine();
+
+ if (registrations.Length > 0)
+ {
+ sb.AppendLine("switch (query)");
+ sb.OpenBrace();
+ foreach (var reg in registrations)
+ {
+ sb.AppendLine($"case {reg.MessageFullyQualifiedName} typed:");
+ sb.Indent();
+ sb.AppendLine($"return (Task)(object)SendQueryAsync<{reg.MessageFullyQualifiedName}, {reg.ResultFullyQualifiedName}>(typed, cancellationToken);");
+ sb.Unindent();
+ }
+ sb.AppendLine("default:");
+ sb.Indent();
+ sb.AppendLine("throw new InvalidOperationException($\"No handler registered for query type {query.GetType().FullName}. Ensure the handler is in the same compilation where [assembly: CortexMediatorGeneration] is applied.\");");
+ sb.Unindent();
+ sb.CloseBrace();
+ }
+ else
+ {
+ sb.AppendLine("throw new InvalidOperationException($\"No handler registered for query type {query.GetType().FullName}. Ensure the handler is in the same compilation where [assembly: CortexMediatorGeneration] is applied.\");");
+ }
+
+ sb.CloseBrace();
+ }
+
+ private static void EmitStronglyTypedPublish(SourceTextBuilder sb)
+ {
+ sb.AppendLine("public async Task PublishAsync(TNotification notification, CancellationToken cancellationToken = default)");
+ sb.AppendLine(" where TNotification : INotification");
+ sb.OpenBrace();
+ sb.AppendLine("var handlers = _serviceProvider.GetServices>();");
+ sb.AppendLine("var behaviors = _serviceProvider.GetServices>();");
+ sb.AppendLine();
+ sb.AppendLine("var behaviorList = behaviors as INotificationPipelineBehavior[] ?? behaviors.ToArray();");
+ sb.AppendLine("Array.Reverse(behaviorList);");
+ sb.AppendLine();
+ sb.AppendLine("var handlerList = handlers as INotificationHandler[] ?? handlers.ToArray();");
+ sb.AppendLine("if (handlerList.Length == 0) return;");
+ sb.AppendLine();
+ sb.AppendLine("var handlerDelegates = new Func[handlerList.Length];");
+ sb.AppendLine("for (int i = 0; i < handlerList.Length; i++)");
+ sb.OpenBrace();
+ sb.AppendLine("var handler = handlerList[i];");
+ sb.AppendLine("NotificationHandlerDelegate handlerDelegate = () => handler.Handle(notification, cancellationToken);");
+ sb.AppendLine();
+ sb.AppendLine("foreach (var behavior in behaviorList)");
+ sb.OpenBrace();
+ sb.AppendLine("var currentDelegate = handlerDelegate;");
+ sb.AppendLine("var currentBehavior = behavior;");
+ sb.AppendLine("handlerDelegate = () => currentBehavior.Handle(notification, currentDelegate, cancellationToken);");
+ sb.CloseBrace();
+ sb.AppendLine();
+ sb.AppendLine("var finalDelegate = handlerDelegate;");
+ sb.AppendLine("handlerDelegates[i] = () => finalDelegate();");
+ sb.CloseBrace();
+ sb.AppendLine();
+ sb.AppendLine("var strategy = _serviceProvider.GetService();");
+ sb.AppendLine("if (strategy != null)");
+ sb.OpenBrace();
+ sb.AppendLine("await strategy.PublishAsync(handlerDelegates, cancellationToken);");
+ sb.CloseBrace();
+ sb.AppendLine("else");
+ sb.OpenBrace();
+ sb.AppendLine("await Task.WhenAll(handlerDelegates.Select(d => d()));");
+ sb.CloseBrace();
+ sb.CloseBrace();
+ }
+
+ private static void EmitSwitchPublish(SourceTextBuilder sb, HandlerRegistration[] registrations)
+ {
+ sb.AppendLine("public Task PublishAsync(INotification notification, CancellationToken cancellationToken = default)");
+ sb.OpenBrace();
+ sb.AppendLine("if (notification == null) throw new ArgumentNullException(nameof(notification));");
+ sb.AppendLine();
+
+ if (registrations.Length > 0)
+ {
+ sb.AppendLine("switch (notification)");
+ sb.OpenBrace();
+ // Deduplicate notification types (multiple handlers for same notification)
+ var distinctNotifications = registrations
+ .Select(r => r.MessageFullyQualifiedName)
+ .Distinct()
+ .ToArray();
+ foreach (var msgFqn in distinctNotifications)
+ {
+ sb.AppendLine($"case {msgFqn} typed:");
+ sb.Indent();
+ sb.AppendLine($"return PublishAsync<{msgFqn}>(typed, cancellationToken);");
+ sb.Unindent();
+ }
+ sb.AppendLine("default:");
+ sb.Indent();
+ sb.AppendLine("throw new InvalidOperationException($\"No handler registered for notification type {notification.GetType().FullName}. Ensure the handler is in the same compilation where [assembly: CortexMediatorGeneration] is applied.\");");
+ sb.Unindent();
+ sb.CloseBrace();
+ }
+ else
+ {
+ sb.AppendLine("throw new InvalidOperationException($\"No handler registered for notification type {notification.GetType().FullName}. Ensure the handler is in the same compilation where [assembly: CortexMediatorGeneration] is applied.\");");
+ }
+
+ sb.CloseBrace();
+ }
+
+ private static void EmitStronglyTypedCreateStream(SourceTextBuilder sb)
+ {
+ sb.AppendLine("public IAsyncEnumerable CreateStream(TQuery query, CancellationToken cancellationToken = default)");
+ sb.AppendLine(" where TQuery : IStreamQuery");
+ sb.OpenBrace();
+ sb.AppendLine("if (query == null) throw new ArgumentNullException(nameof(query));");
+ sb.AppendLine();
+ sb.AppendLine("var handler = _serviceProvider.GetRequiredService>();");
+ sb.AppendLine("var behaviors = _serviceProvider.GetServices>();");
+ sb.AppendLine();
+ sb.AppendLine("StreamQueryHandlerDelegate handlerDelegate = () => handler.Handle(query, cancellationToken);");
+ sb.AppendLine();
+ sb.AppendLine("var behaviorArray = behaviors as IStreamQueryPipelineBehavior[] ?? behaviors.ToArray();");
+ sb.AppendLine("for (int i = behaviorArray.Length - 1; i >= 0; i--)");
+ sb.OpenBrace();
+ sb.AppendLine("var currentDelegate = handlerDelegate;");
+ sb.AppendLine("var currentBehavior = behaviorArray[i];");
+ sb.AppendLine("handlerDelegate = () => currentBehavior.Handle(query, currentDelegate, cancellationToken);");
+ sb.CloseBrace();
+ sb.AppendLine();
+ sb.AppendLine("return handlerDelegate();");
+ sb.CloseBrace();
+ }
+
+ private static void EmitSwitchCreateStream(SourceTextBuilder sb, HandlerRegistration[] registrations)
+ {
+ sb.AppendLine("public IAsyncEnumerable CreateStream(IStreamQuery query, CancellationToken cancellationToken = default)");
+ sb.OpenBrace();
+ sb.AppendLine("if (query == null) throw new ArgumentNullException(nameof(query));");
+ sb.AppendLine();
+
+ if (registrations.Length > 0)
+ {
+ sb.AppendLine("switch (query)");
+ sb.OpenBrace();
+ foreach (var reg in registrations)
+ {
+ sb.AppendLine($"case {reg.MessageFullyQualifiedName} typed:");
+ sb.Indent();
+ sb.AppendLine($"return (IAsyncEnumerable)CreateStream<{reg.MessageFullyQualifiedName}, {reg.ResultFullyQualifiedName}>(typed, cancellationToken);");
+ sb.Unindent();
+ }
+ sb.AppendLine("default:");
+ sb.Indent();
+ sb.AppendLine("throw new InvalidOperationException($\"No handler registered for stream query type {query.GetType().FullName}. Ensure the handler is in the same compilation where [assembly: CortexMediatorGeneration] is applied.\");");
+ sb.Unindent();
+ sb.CloseBrace();
+ }
+ else
+ {
+ sb.AppendLine("throw new InvalidOperationException($\"No handler registered for stream query type {query.GetType().FullName}. Ensure the handler is in the same compilation where [assembly: CortexMediatorGeneration] is applied.\");");
+ }
+
+ sb.CloseBrace();
+ }
+
+ private static void EmitPipelineWrapperClasses(SourceTextBuilder sb)
+ {
+ // PipelineBehaviorNextDelegate
+ sb.AppendLine("private sealed class PipelineBehaviorNextDelegate : ICommandHandler");
+ sb.AppendLine(" where TCommand : ICommand");
+ sb.OpenBrace();
+ sb.AppendLine("private readonly ICommandPipelineBehavior _behavior;");
+ sb.AppendLine("private readonly ICommandHandler _next;");
+ sb.AppendLine();
+ sb.AppendLine("public PipelineBehaviorNextDelegate(ICommandPipelineBehavior behavior, ICommandHandler next)");
+ sb.OpenBrace();
+ sb.AppendLine("_behavior = behavior;");
+ sb.AppendLine("_next = next;");
+ sb.CloseBrace();
+ sb.AppendLine();
+ sb.AppendLine("public Task Handle(TCommand command, CancellationToken cancellationToken)");
+ sb.OpenBrace();
+ sb.AppendLine("return _behavior.Handle(command, () => _next.Handle(command, cancellationToken), cancellationToken);");
+ sb.CloseBrace();
+ sb.CloseBrace();
+
+ sb.AppendLine();
+
+ // VoidPipelineBehaviorNextDelegate
+ sb.AppendLine("private sealed class VoidPipelineBehaviorNextDelegate : ICommandHandler");
+ sb.AppendLine(" where TCommand : ICommand");
+ sb.OpenBrace();
+ sb.AppendLine("private readonly ICommandPipelineBehavior _behavior;");
+ sb.AppendLine("private readonly ICommandHandler _next;");
+ sb.AppendLine();
+ sb.AppendLine("public VoidPipelineBehaviorNextDelegate(ICommandPipelineBehavior behavior, ICommandHandler next)");
+ sb.OpenBrace();
+ sb.AppendLine("_behavior = behavior;");
+ sb.AppendLine("_next = next;");
+ sb.CloseBrace();
+ sb.AppendLine();
+ sb.AppendLine("public Task Handle(TCommand command, CancellationToken cancellationToken)");
+ sb.OpenBrace();
+ sb.AppendLine("return _behavior.Handle(command, () => _next.Handle(command, cancellationToken), cancellationToken);");
+ sb.CloseBrace();
+ sb.CloseBrace();
+
+ sb.AppendLine();
+
+ // QueryPipelineBehaviorNextDelegate
+ sb.AppendLine("private sealed class QueryPipelineBehaviorNextDelegate : IQueryHandler");
+ sb.AppendLine(" where TQuery : IQuery");
+ sb.OpenBrace();
+ sb.AppendLine("private readonly IQueryPipelineBehavior _behavior;");
+ sb.AppendLine("private readonly IQueryHandler _next;");
+ sb.AppendLine();
+ sb.AppendLine("public QueryPipelineBehaviorNextDelegate(IQueryPipelineBehavior behavior, IQueryHandler next)");
+ sb.OpenBrace();
+ sb.AppendLine("_behavior = behavior;");
+ sb.AppendLine("_next = next;");
+ sb.CloseBrace();
+ sb.AppendLine();
+ sb.AppendLine("public Task Handle(TQuery query, CancellationToken cancellationToken)");
+ sb.OpenBrace();
+ sb.AppendLine("return _behavior.Handle(query, () => _next.Handle(query, cancellationToken), cancellationToken);");
+ sb.CloseBrace();
+ sb.CloseBrace();
+ }
+ }
+}
diff --git a/src/Cortex.Mediator.SourceGenerator/Emitters/SourceTextBuilder.cs b/src/Cortex.Mediator.SourceGenerator/Emitters/SourceTextBuilder.cs
new file mode 100644
index 0000000..ef72823
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/Emitters/SourceTextBuilder.cs
@@ -0,0 +1,52 @@
+using System.Text;
+
+namespace Cortex.Mediator.SourceGenerator.Emitters
+{
+ internal sealed class SourceTextBuilder
+ {
+ private readonly StringBuilder _sb = new StringBuilder();
+ private int _indent;
+
+ public SourceTextBuilder Indent()
+ {
+ _indent++;
+ return this;
+ }
+
+ public SourceTextBuilder Unindent()
+ {
+ if (_indent > 0) _indent--;
+ return this;
+ }
+
+ public SourceTextBuilder AppendLine(string line = "")
+ {
+ if (string.IsNullOrEmpty(line))
+ {
+ _sb.AppendLine();
+ }
+ else
+ {
+ _sb.Append(new string(' ', _indent * 4));
+ _sb.AppendLine(line);
+ }
+ return this;
+ }
+
+ public SourceTextBuilder OpenBrace()
+ {
+ AppendLine("{");
+ Indent();
+ return this;
+ }
+
+ public SourceTextBuilder CloseBrace(string suffix = "")
+ {
+ Unindent();
+ AppendLine("}" + suffix);
+ return this;
+ }
+
+ public override string ToString() => _sb.ToString();
+ }
+}
diff --git a/src/Cortex.Mediator.SourceGenerator/Models/HandlerRegistration.cs b/src/Cortex.Mediator.SourceGenerator/Models/HandlerRegistration.cs
new file mode 100644
index 0000000..ea611ac
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/Models/HandlerRegistration.cs
@@ -0,0 +1,54 @@
+using System;
+
+namespace Cortex.Mediator.SourceGenerator.Models
+{
+ internal sealed class HandlerRegistration : IEquatable
+ {
+ public InterfaceKind Kind { get; }
+ public string HandlerFullyQualifiedName { get; }
+ public string MessageFullyQualifiedName { get; }
+ public string ResultFullyQualifiedName { get; }
+ public string ServiceInterfaceFullyQualifiedName { get; }
+
+ public HandlerRegistration(
+ InterfaceKind kind,
+ string handlerFullyQualifiedName,
+ string messageFullyQualifiedName,
+ string resultFullyQualifiedName,
+ string serviceInterfaceFullyQualifiedName)
+ {
+ Kind = kind;
+ HandlerFullyQualifiedName = handlerFullyQualifiedName;
+ MessageFullyQualifiedName = messageFullyQualifiedName;
+ ResultFullyQualifiedName = resultFullyQualifiedName ?? "";
+ ServiceInterfaceFullyQualifiedName = serviceInterfaceFullyQualifiedName;
+ }
+
+ public bool Equals(HandlerRegistration other)
+ {
+ if (other is null) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Kind == other.Kind
+ && HandlerFullyQualifiedName == other.HandlerFullyQualifiedName
+ && MessageFullyQualifiedName == other.MessageFullyQualifiedName
+ && ResultFullyQualifiedName == other.ResultFullyQualifiedName
+ && ServiceInterfaceFullyQualifiedName == other.ServiceInterfaceFullyQualifiedName;
+ }
+
+ public override bool Equals(object obj) => Equals(obj as HandlerRegistration);
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hash = 17;
+ hash = hash * 31 + Kind.GetHashCode();
+ hash = hash * 31 + HandlerFullyQualifiedName.GetHashCode();
+ hash = hash * 31 + MessageFullyQualifiedName.GetHashCode();
+ hash = hash * 31 + ResultFullyQualifiedName.GetHashCode();
+ hash = hash * 31 + ServiceInterfaceFullyQualifiedName.GetHashCode();
+ return hash;
+ }
+ }
+ }
+}
diff --git a/src/Cortex.Mediator.SourceGenerator/Models/InterfaceKind.cs b/src/Cortex.Mediator.SourceGenerator/Models/InterfaceKind.cs
new file mode 100644
index 0000000..d501bab
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/Models/InterfaceKind.cs
@@ -0,0 +1,14 @@
+namespace Cortex.Mediator.SourceGenerator.Models
+{
+ internal enum InterfaceKind
+ {
+ ReturningCommand,
+ VoidCommand,
+ Query,
+ StreamQuery,
+ Notification,
+ PreProcessor,
+ PostProcessorWithResponse,
+ PostProcessorVoid
+ }
+}
diff --git a/src/Cortex.Mediator.SourceGenerator/Models/MessageRegistration.cs b/src/Cortex.Mediator.SourceGenerator/Models/MessageRegistration.cs
new file mode 100644
index 0000000..353c141
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/Models/MessageRegistration.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace Cortex.Mediator.SourceGenerator.Models
+{
+ internal sealed class MessageRegistration : IEquatable
+ {
+ public InterfaceKind Kind { get; }
+ public string MessageFullyQualifiedName { get; }
+ public string ResultFullyQualifiedName { get; }
+ public Microsoft.CodeAnalysis.Location Location { get; }
+
+ public MessageRegistration(
+ InterfaceKind kind,
+ string messageFullyQualifiedName,
+ string resultFullyQualifiedName,
+ Microsoft.CodeAnalysis.Location location)
+ {
+ Kind = kind;
+ MessageFullyQualifiedName = messageFullyQualifiedName;
+ ResultFullyQualifiedName = resultFullyQualifiedName ?? "";
+ Location = location;
+ }
+
+ public bool Equals(MessageRegistration other)
+ {
+ if (other is null) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Kind == other.Kind
+ && MessageFullyQualifiedName == other.MessageFullyQualifiedName
+ && ResultFullyQualifiedName == other.ResultFullyQualifiedName;
+ }
+
+ public override bool Equals(object obj) => Equals(obj as MessageRegistration);
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hash = 17;
+ hash = hash * 31 + Kind.GetHashCode();
+ hash = hash * 31 + MessageFullyQualifiedName.GetHashCode();
+ hash = hash * 31 + ResultFullyQualifiedName.GetHashCode();
+ return hash;
+ }
+ }
+ }
+}
diff --git a/src/Cortex.Mediator.SourceGenerator/README.md b/src/Cortex.Mediator.SourceGenerator/README.md
new file mode 100644
index 0000000..5d3bf27
--- /dev/null
+++ b/src/Cortex.Mediator.SourceGenerator/README.md
@@ -0,0 +1,182 @@
+# Cortex.Mediator.SourceGenerator
+
+**Cortex.Mediator.SourceGenerator** is an optional Roslyn incremental source generator for [Cortex.Mediator](https://www.nuget.org/packages/Cortex.Mediator) that eliminates runtime reflection by generating compile-time dispatch.
+
+Built as part of the [Cortex Data Framework](https://github.com/buildersoftio/cortex), this package replaces the five `ConcurrentDictionary` caches and `MakeGenericMethod` + `Invoke` calls in the default `Mediator` with a `GeneratedMediator` class that uses `switch`-based routing and explicit DI registrations.
+
+---
+
+[](https://github.com/buildersoftio/cortex/blob/master/LICENSE)
+[](https://www.nuget.org/packages/Cortex.Mediator)
+[](https://github.com/buildersoftio/cortex)
+[](https://discord.gg/JnMJV33QHu)
+
+## Why Use This?
+
+| Aspect | Default `Mediator` | `GeneratedMediator` |
+|--------|-------------------|---------------------|
+| Non-generic dispatch | Runtime reflection (`MakeGenericMethod`) | Compile-time `switch` |
+| Cold start | Dictionary allocation + reflection lookup | Zero overhead |
+| DI registrations | Assembly scanning at startup | Explicit `ServiceDescriptor` calls |
+| NativeAOT / Trimming | Incompatible (reflection) | Compatible |
+| Pipeline behaviors | Fully supported | Fully supported (identical logic) |
+
+## Getting Started
+
+### Install via NuGet
+
+```bash
+dotnet add package Cortex.Mediator
+dotnet add package Cortex.Mediator.SourceGenerator
+```
+
+Or in your `.csproj`:
+
+```xml
+
+
+```
+
+### Enable the Generator
+
+Add the assembly attribute in any `.cs` file in your project:
+
+```csharp
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+```
+
+### Register Services
+
+Replace `AddCortexMediator` with `AddCortexGeneratedMediator`:
+
+```csharp
+// Before (reflection-based):
+services.AddCortexMediator(
+ new[] { typeof(Program) },
+ options => options.AddDefaultBehaviors()
+);
+
+// After (source-generated):
+services.AddCortexGeneratedMediator(options =>
+{
+ options.AddDefaultBehaviors();
+});
+```
+
+No assembly marker types needed — the generator discovers all handlers at compile time.
+
+## What Gets Generated
+
+### `GeneratedMediator.g.cs`
+
+A class implementing `IMediator` with switch-based dispatch for all non-generic overloads:
+
+```csharp
+public Task SendCommandAsync(ICommand command, CancellationToken ct)
+{
+ switch (command)
+ {
+ case CreateUserCommand typed:
+ return (Task)(object)SendCommandAsync(typed, ct);
+ case UpdateUserCommand typed:
+ return (Task)(object)SendCommandAsync(typed, ct);
+ default:
+ throw new InvalidOperationException(...);
+ }
+}
+```
+
+The strongly-typed methods (`SendCommandAsync`, etc.) use the same pipeline behavior wrapping as the default `Mediator` — no reflection involved.
+
+### `GeneratedServiceCollectionExtensions.g.cs`
+
+Explicit DI registrations for every discovered handler and processor:
+
+```csharp
+public static IServiceCollection AddCortexGeneratedMediator(
+ this IServiceCollection services,
+ Action? configure = null)
+{
+ services.AddScoped();
+ services.AddSingleton(typeof(INotificationPublishStrategy), options.NotificationPublishStrategyType);
+
+ // Explicit handler registrations (no assembly scanning)
+ services.Add(new ServiceDescriptor(
+ typeof(ICommandHandler),
+ typeof(CreateUserCommandHandler),
+ ServiceLifetime.Scoped));
+ // ...
+
+ // Pipeline behaviors are still runtime-configured
+ ServiceCollectionExtensions.RegisterPipelineBehaviors(services, options);
+ return services;
+}
+```
+
+## Compile-Time Diagnostics
+
+The generator reports diagnostics during compilation:
+
+| ID | Severity | Condition |
+|----|----------|-----------|
+| `CXMED001` | Warning | Command has no handler |
+| `CXMED002` | Warning | Query has no handler |
+| `CXMED003` | Warning | StreamQuery has no handler |
+| `CXMED004` | Info | Notification has no handler (zero handlers is valid) |
+| `CXMED005` | Error | Multiple handlers for same command |
+| `CXMED006` | Error | Multiple handlers for same query |
+
+## Supported Handler Types
+
+The generator discovers all handler and processor implementations:
+
+- `ICommandHandler` — Returning commands
+- `ICommandHandler` — Void commands
+- `IQueryHandler` — Queries
+- `IStreamQueryHandler` — Streaming queries
+- `INotificationHandler` — Notifications
+- `IRequestPreProcessor` — Pre-processors
+- `IRequestPostProcessor` — Post-processors with response
+- `IRequestPostProcessor` — Post-processors for void commands
+
+## Scope & Limitations
+
+- **Single compilation**: The generator scans handlers in the compilation where `[assembly: CortexMediatorGeneration]` is applied. Cross-assembly handlers are not discovered.
+- **Pipeline behaviors**: Configured at runtime via `MediatorOptions`, not generated (they use open generics).
+- **Fallback**: The existing reflection-based `Mediator` remains the default. The generator is opt-in.
+
+## Contributing
+
+We welcome contributions from the community! Whether it's reporting bugs, suggesting features, or submitting pull requests, your involvement helps improve Cortex for everyone.
+
+### How to Contribute
+1. **Fork the Repository**
+2. **Create a Feature Branch**
+```bash
+git checkout -b feature/YourFeature
+```
+3. **Commit Your Changes**
+```bash
+git commit -m "Add your feature"
+```
+4. **Push to Your Fork**
+```bash
+git push origin feature/YourFeature
+```
+5. **Open a Pull Request**
+
+Describe your changes and submit the pull request for review.
+
+## License
+This project is licensed under the MIT License.
+
+## Contact
+- Email: cortex@buildersoft.io
+- Website: https://buildersoft.io
+- GitHub Issues: [Cortex Data Framework Issues](https://github.com/buildersoftio/cortex/issues)
+- Join our Discord Community: [](https://discord.gg/JnMJV33QHu)
+
+Thank you for using Cortex Data Framework! We hope it empowers you to build scalable and efficient data processing pipelines effortlessly.
+
+Built with love by the Buildersoft team.
diff --git a/src/Cortex.Mediator/Cortex.Mediator.csproj b/src/Cortex.Mediator/Cortex.Mediator.csproj
index e0e6275..bbe3f66 100644
--- a/src/Cortex.Mediator/Cortex.Mediator.csproj
+++ b/src/Cortex.Mediator/Cortex.Mediator.csproj
@@ -67,5 +67,6 @@
+
diff --git a/src/Cortex.Mediator/DependencyInjection/MediatorOptions.cs b/src/Cortex.Mediator/DependencyInjection/MediatorOptions.cs
index bd2ad5e..553be84 100644
--- a/src/Cortex.Mediator/DependencyInjection/MediatorOptions.cs
+++ b/src/Cortex.Mediator/DependencyInjection/MediatorOptions.cs
@@ -31,7 +31,7 @@ public class MediatorOptions
/// Defaults to .
/// Use to change.
///
- internal Type NotificationPublishStrategyType { get; private set; } = typeof(ParallelNotificationStrategy);
+ public Type NotificationPublishStrategyType { get; private set; } = typeof(ParallelNotificationStrategy);
///
/// Sets the strategy used to publish notifications to multiple handlers.
diff --git a/src/Cortex.Mediator/DependencyInjection/ServiceCollectionExtensions.cs b/src/Cortex.Mediator/DependencyInjection/ServiceCollectionExtensions.cs
index 60e0c06..78a19e9 100644
--- a/src/Cortex.Mediator/DependencyInjection/ServiceCollectionExtensions.cs
+++ b/src/Cortex.Mediator/DependencyInjection/ServiceCollectionExtensions.cs
@@ -111,7 +111,7 @@ private static void ScanAndRegister(
}
}
- private static void RegisterPipelineBehaviors(IServiceCollection services, MediatorOptions options)
+ public static void RegisterPipelineBehaviors(IServiceCollection services, MediatorOptions options)
{
// Sort each behavior list by Order (stable sort preserves registration order for equal values).
// OrderBy in LINQ is a stable sort.
diff --git a/src/Cortex.Tests/Cortex.Tests.csproj b/src/Cortex.Tests/Cortex.Tests.csproj
index 24f83f5..d216e09 100644
--- a/src/Cortex.Tests/Cortex.Tests.csproj
+++ b/src/Cortex.Tests/Cortex.Tests.csproj
@@ -23,10 +23,12 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/src/Cortex.Tests/Mediator/SourceGenerator/DiagnosticTests.cs b/src/Cortex.Tests/Mediator/SourceGenerator/DiagnosticTests.cs
new file mode 100644
index 0000000..5f9472c
--- /dev/null
+++ b/src/Cortex.Tests/Mediator/SourceGenerator/DiagnosticTests.cs
@@ -0,0 +1,254 @@
+using Cortex.Mediator.SourceGenerator;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using System.Reflection;
+
+namespace Cortex.Tests.Mediator.SourceGenerator;
+
+public class DiagnosticTests
+{
+ private static readonly MetadataReference[] SharedReferences = GetSharedReferences();
+
+ private static MetadataReference[] GetSharedReferences()
+ {
+ var assemblies = new[]
+ {
+ typeof(object).Assembly,
+ typeof(Task).Assembly,
+ typeof(IServiceProvider).Assembly,
+ typeof(Microsoft.Extensions.DependencyInjection.IServiceCollection).Assembly,
+ typeof(Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions).Assembly,
+ typeof(Cortex.Mediator.IMediator).Assembly,
+ Assembly.Load("System.Runtime"),
+ Assembly.Load("System.Collections"),
+ Assembly.Load("System.Linq"),
+ Assembly.Load("netstandard"),
+ };
+
+ return assemblies.Select(a => MetadataReference.CreateFromFile(a.Location))
+ .Cast()
+ .ToArray();
+ }
+
+ private static GeneratorDriverRunResult RunGenerator(string source)
+ {
+ var syntaxTree = CSharpSyntaxTree.ParseText(source);
+
+ var compilation = CSharpCompilation.Create(
+ assemblyName: "TestAssembly",
+ syntaxTrees: new[] { syntaxTree },
+ references: SharedReferences,
+ options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
+
+ var generator = new CortexMediatorGenerator();
+ GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
+
+ driver = driver.RunGeneratorsAndUpdateCompilation(
+ compilation,
+ out _,
+ out _);
+
+ return driver.GetRunResult();
+ }
+
+ [Fact]
+ public void CXMED001_CommandWithNoHandler_ProducesWarning()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class OrphanCommand : ICommand { }
+}";
+
+ var result = RunGenerator(source);
+ var diagnostics = result.Results[0].Diagnostics;
+
+ Assert.Contains(diagnostics, d => d.Id == "CXMED001");
+ }
+
+ [Fact]
+ public void CXMED001_VoidCommandWithNoHandler_ProducesWarning()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class OrphanVoidCommand : ICommand { }
+}";
+
+ var result = RunGenerator(source);
+ var diagnostics = result.Results[0].Diagnostics;
+
+ Assert.Contains(diagnostics, d => d.Id == "CXMED001");
+ }
+
+ [Fact]
+ public void CXMED002_QueryWithNoHandler_ProducesWarning()
+ {
+ var source = @"
+using Cortex.Mediator.Queries;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class OrphanQuery : IQuery { }
+}";
+
+ var result = RunGenerator(source);
+ var diagnostics = result.Results[0].Diagnostics;
+
+ Assert.Contains(diagnostics, d => d.Id == "CXMED002");
+ }
+
+ [Fact]
+ public void CXMED003_StreamQueryWithNoHandler_ProducesWarning()
+ {
+ var source = @"
+using Cortex.Mediator.Streaming;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class OrphanStreamQuery : IStreamQuery { }
+}";
+
+ var result = RunGenerator(source);
+ var diagnostics = result.Results[0].Diagnostics;
+
+ Assert.Contains(diagnostics, d => d.Id == "CXMED003");
+ }
+
+ [Fact]
+ public void CXMED004_NotificationWithNoHandler_ProducesInfo()
+ {
+ var source = @"
+using Cortex.Mediator.Notifications;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class OrphanNotification : INotification { }
+}";
+
+ var result = RunGenerator(source);
+ var diagnostics = result.Results[0].Diagnostics;
+
+ Assert.Contains(diagnostics, d => d.Id == "CXMED004");
+ }
+
+ [Fact]
+ public void CXMED005_MultipleHandlersForSameCommand_ProducesError()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class MyCommand : ICommand { }
+
+ public class Handler1 : ICommandHandler
+ {
+ public Task Handle(MyCommand cmd, CancellationToken ct) => Task.FromResult(1);
+ }
+
+ public class Handler2 : ICommandHandler
+ {
+ public Task Handle(MyCommand cmd, CancellationToken ct) => Task.FromResult(2);
+ }
+}";
+
+ var result = RunGenerator(source);
+ var diagnostics = result.Results[0].Diagnostics;
+
+ Assert.Contains(diagnostics, d => d.Id == "CXMED005" && d.Severity == DiagnosticSeverity.Error);
+ }
+
+ [Fact]
+ public void CXMED006_MultipleHandlersForSameQuery_ProducesError()
+ {
+ var source = @"
+using Cortex.Mediator.Queries;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class MyQuery : IQuery { }
+
+ public class QueryHandler1 : IQueryHandler
+ {
+ public Task Handle(MyQuery q, CancellationToken ct) => Task.FromResult(""a"");
+ }
+
+ public class QueryHandler2 : IQueryHandler
+ {
+ public Task Handle(MyQuery q, CancellationToken ct) => Task.FromResult(""b"");
+ }
+}";
+
+ var result = RunGenerator(source);
+ var diagnostics = result.Results[0].Diagnostics;
+
+ Assert.Contains(diagnostics, d => d.Id == "CXMED006" && d.Severity == DiagnosticSeverity.Error);
+ }
+
+ [Fact]
+ public void NoAttribute_NoDiagnostics()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+
+namespace TestApp
+{
+ public class OrphanCommand : ICommand { }
+}";
+
+ var result = RunGenerator(source);
+ var diagnostics = result.Results[0].Diagnostics;
+
+ // No diagnostics should be produced without the assembly attribute
+ Assert.Empty(diagnostics);
+ }
+
+ [Fact]
+ public void CommandWithHandler_NoDiagnostic()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class MyCommand : ICommand { }
+
+ public class MyHandler : ICommandHandler
+ {
+ public Task Handle(MyCommand cmd, CancellationToken ct) => Task.FromResult(1);
+ }
+}";
+
+ var result = RunGenerator(source);
+ var diagnostics = result.Results[0].Diagnostics;
+
+ Assert.DoesNotContain(diagnostics, d => d.Id == "CXMED001");
+ }
+}
diff --git a/src/Cortex.Tests/Mediator/SourceGenerator/GeneratedMediatorIntegrationTests.cs b/src/Cortex.Tests/Mediator/SourceGenerator/GeneratedMediatorIntegrationTests.cs
new file mode 100644
index 0000000..0437e8b
--- /dev/null
+++ b/src/Cortex.Tests/Mediator/SourceGenerator/GeneratedMediatorIntegrationTests.cs
@@ -0,0 +1,326 @@
+using Cortex.Mediator.SourceGenerator;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using System.Reflection;
+
+namespace Cortex.Tests.Mediator.SourceGenerator;
+
+public class GeneratedMediatorIntegrationTests
+{
+ private static readonly MetadataReference[] SharedReferences = GetSharedReferences();
+
+ private static MetadataReference[] GetSharedReferences()
+ {
+ var assemblies = new[]
+ {
+ typeof(object).Assembly,
+ typeof(Task).Assembly,
+ typeof(IServiceProvider).Assembly,
+ typeof(Microsoft.Extensions.DependencyInjection.IServiceCollection).Assembly,
+ typeof(Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions).Assembly,
+ typeof(Microsoft.Extensions.DependencyInjection.ServiceCollection).Assembly,
+ typeof(Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions).Assembly,
+ typeof(Cortex.Mediator.IMediator).Assembly,
+ Assembly.Load("System.Runtime"),
+ Assembly.Load("System.Collections"),
+ Assembly.Load("System.Linq"),
+ Assembly.Load("netstandard"),
+ };
+
+ return assemblies.Select(a => MetadataReference.CreateFromFile(a.Location))
+ .Cast()
+ .ToArray();
+ }
+
+ private static (GeneratorDriverRunResult Result, Compilation OutputCompilation) RunGenerator(string source)
+ {
+ var syntaxTree = CSharpSyntaxTree.ParseText(source);
+
+ var compilation = CSharpCompilation.Create(
+ assemblyName: "TestAssembly",
+ syntaxTrees: new[] { syntaxTree },
+ references: SharedReferences,
+ options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
+
+ var generator = new CortexMediatorGenerator();
+ GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
+
+ driver = driver.RunGeneratorsAndUpdateCompilation(
+ compilation,
+ out var outputCompilation,
+ out _);
+
+ var result = driver.GetRunResult();
+ return (result, outputCompilation);
+ }
+
+ [Fact]
+ public void GeneratedCode_CompilesWith_AllHandlerTypes()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+using Cortex.Mediator.Queries;
+using Cortex.Mediator.Notifications;
+using Cortex.Mediator.Streaming;
+using Cortex.Mediator.Processors;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ // Returning command
+ public class CreateCommand : ICommand { public string Name { get; set; } }
+ public class CreateHandler : ICommandHandler
+ {
+ public Task Handle(CreateCommand cmd, CancellationToken ct) => Task.FromResult(42);
+ }
+
+ // Void command
+ public class DeleteCommand : ICommand { public int Id { get; set; } }
+ public class DeleteHandler : ICommandHandler
+ {
+ public Task Handle(DeleteCommand cmd, CancellationToken ct) => Task.CompletedTask;
+ }
+
+ // Query
+ public class GetQuery : IQuery { public int Id { get; set; } }
+ public class GetHandler : IQueryHandler
+ {
+ public Task Handle(GetQuery q, CancellationToken ct) => Task.FromResult(""result"");
+ }
+
+ // Notification
+ public class ItemCreated : INotification { public int ItemId { get; set; } }
+ public class ItemCreatedHandler1 : INotificationHandler
+ {
+ public Task Handle(ItemCreated n, CancellationToken ct) => Task.CompletedTask;
+ }
+ public class ItemCreatedHandler2 : INotificationHandler
+ {
+ public Task Handle(ItemCreated n, CancellationToken ct) => Task.CompletedTask;
+ }
+
+ // Stream query
+ public class StreamItems : IStreamQuery { }
+ public class StreamItemsHandler : IStreamQueryHandler
+ {
+ public async IAsyncEnumerable Handle(StreamItems q, CancellationToken ct)
+ {
+ yield return ""item1"";
+ yield return ""item2"";
+ }
+ }
+
+ // Pre-processor
+ public class CreatePreProcessor : IRequestPreProcessor
+ {
+ public Task ProcessAsync(CreateCommand request, CancellationToken ct) => Task.CompletedTask;
+ }
+
+ // Post-processor
+ public class CreatePostProcessor : IRequestPostProcessor
+ {
+ public Task ProcessAsync(CreateCommand request, int response, CancellationToken ct) => Task.CompletedTask;
+ }
+
+ // Void post-processor
+ public class DeletePostProcessor : IRequestPostProcessor
+ {
+ public Task ProcessAsync(DeleteCommand request, CancellationToken ct) => Task.CompletedTask;
+ }
+}";
+
+ var (result, outputCompilation) = RunGenerator(source);
+
+ // Check no compilation errors in generated code
+ var errors = outputCompilation.GetDiagnostics()
+ .Where(d => d.Severity == DiagnosticSeverity.Error)
+ .ToArray();
+
+ Assert.Empty(errors);
+
+ // Verify all expected files were generated
+ var generatedSources = result.Results[0].GeneratedSources;
+ Assert.Equal(3, generatedSources.Length);
+
+ // Verify mediator source content
+ var mediatorSource = generatedSources
+ .First(s => s.HintName == "GeneratedMediator.g.cs")
+ .SourceText.ToString();
+
+ // All switch cases should be present
+ Assert.Contains("case global::TestApp.CreateCommand typed:", mediatorSource);
+ Assert.Contains("case global::TestApp.DeleteCommand typed:", mediatorSource);
+ Assert.Contains("case global::TestApp.GetQuery typed:", mediatorSource);
+ Assert.Contains("case global::TestApp.ItemCreated typed:", mediatorSource);
+ Assert.Contains("case global::TestApp.StreamItems typed:", mediatorSource);
+
+ // Verify DI source content
+ var diSource = generatedSources
+ .First(s => s.HintName == "GeneratedServiceCollectionExtensions.g.cs")
+ .SourceText.ToString();
+
+ // All handlers registered
+ Assert.Contains("global::TestApp.CreateHandler", diSource);
+ Assert.Contains("global::TestApp.DeleteHandler", diSource);
+ Assert.Contains("global::TestApp.GetHandler", diSource);
+ Assert.Contains("global::TestApp.ItemCreatedHandler1", diSource);
+ Assert.Contains("global::TestApp.ItemCreatedHandler2", diSource);
+ Assert.Contains("global::TestApp.StreamItemsHandler", diSource);
+
+ // Processors registered
+ Assert.Contains("global::TestApp.CreatePreProcessor", diSource);
+ Assert.Contains("global::TestApp.CreatePostProcessor", diSource);
+ Assert.Contains("global::TestApp.DeletePostProcessor", diSource);
+ }
+
+ [Fact]
+ public void GeneratedMediator_HasCorrectPipelineWrappers()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class MyCommand : ICommand { }
+ public class MyHandler : ICommandHandler
+ {
+ public Task Handle(MyCommand cmd, CancellationToken ct) => Task.FromResult(1);
+ }
+}";
+
+ var (result, outputCompilation) = RunGenerator(source);
+
+ var mediatorSource = result.Results[0].GeneratedSources
+ .First(s => s.HintName == "GeneratedMediator.g.cs")
+ .SourceText.ToString();
+
+ // Verify pipeline wrapper classes exist
+ Assert.Contains("PipelineBehaviorNextDelegate", mediatorSource);
+ Assert.Contains("VoidPipelineBehaviorNextDelegate", mediatorSource);
+ Assert.Contains("QueryPipelineBehaviorNextDelegate", mediatorSource);
+
+ // No compilation errors
+ var errors = outputCompilation.GetDiagnostics()
+ .Where(d => d.Severity == DiagnosticSeverity.Error)
+ .ToArray();
+ Assert.Empty(errors);
+ }
+
+ [Fact]
+ public void GeneratedDI_RegistersPipelineBehaviors()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class MyCommand : ICommand { }
+ public class MyHandler : ICommandHandler
+ {
+ public Task Handle(MyCommand cmd, CancellationToken ct) => Task.FromResult(1);
+ }
+}";
+
+ var (result, _) = RunGenerator(source);
+
+ var diSource = result.Results[0].GeneratedSources
+ .First(s => s.HintName == "GeneratedServiceCollectionExtensions.g.cs")
+ .SourceText.ToString();
+
+ // Verify it calls RegisterPipelineBehaviors
+ Assert.Contains("ServiceCollectionExtensions.RegisterPipelineBehaviors(services, options)", diSource);
+
+ // Verify it registers the notification publish strategy
+ Assert.Contains("options.NotificationPublishStrategyType", diSource);
+
+ // Verify it accepts configuration action
+ Assert.Contains("Action?", diSource);
+ }
+
+ [Fact]
+ public void Notification_MultipleHandlers_SingleSwitchCase()
+ {
+ var source = @"
+using Cortex.Mediator.Notifications;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class MyEvent : INotification { }
+
+ public class Handler1 : INotificationHandler
+ {
+ public Task Handle(MyEvent n, CancellationToken ct) => Task.CompletedTask;
+ }
+
+ public class Handler2 : INotificationHandler
+ {
+ public Task Handle(MyEvent n, CancellationToken ct) => Task.CompletedTask;
+ }
+}";
+
+ var (result, _) = RunGenerator(source);
+
+ var mediatorSource = result.Results[0].GeneratedSources
+ .First(s => s.HintName == "GeneratedMediator.g.cs")
+ .SourceText.ToString();
+
+ // Only one switch case for the notification (deduplicated)
+ var caseCount = mediatorSource.Split("case global::TestApp.MyEvent typed:").Length - 1;
+ Assert.Equal(1, caseCount);
+
+ // But both handlers registered in DI
+ var diSource = result.Results[0].GeneratedSources
+ .First(s => s.HintName == "GeneratedServiceCollectionExtensions.g.cs")
+ .SourceText.ToString();
+
+ Assert.Contains("global::TestApp.Handler1", diSource);
+ Assert.Contains("global::TestApp.Handler2", diSource);
+ }
+
+ [Fact]
+ public void GeneratedMediator_IncludesNullChecks()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class MyCommand : ICommand { }
+ public class MyHandler : ICommandHandler
+ {
+ public Task Handle(MyCommand cmd, CancellationToken ct) => Task.FromResult(1);
+ }
+}";
+
+ var (result, _) = RunGenerator(source);
+
+ var mediatorSource = result.Results[0].GeneratedSources
+ .First(s => s.HintName == "GeneratedMediator.g.cs")
+ .SourceText.ToString();
+
+ // Non-generic overloads should have null checks
+ Assert.Contains("throw new ArgumentNullException(nameof(command))", mediatorSource);
+ Assert.Contains("throw new ArgumentNullException(nameof(query))", mediatorSource);
+ Assert.Contains("throw new ArgumentNullException(nameof(notification))", mediatorSource);
+ }
+}
diff --git a/src/Cortex.Tests/Mediator/SourceGenerator/GeneratorDriverTests.cs b/src/Cortex.Tests/Mediator/SourceGenerator/GeneratorDriverTests.cs
new file mode 100644
index 0000000..5913ba9
--- /dev/null
+++ b/src/Cortex.Tests/Mediator/SourceGenerator/GeneratorDriverTests.cs
@@ -0,0 +1,420 @@
+using Cortex.Mediator.SourceGenerator;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using System.Collections.Immutable;
+using System.Reflection;
+
+namespace Cortex.Tests.Mediator.SourceGenerator;
+
+public class GeneratorDriverTests
+{
+ private static readonly MetadataReference[] SharedReferences = GetSharedReferences();
+
+ private static MetadataReference[] GetSharedReferences()
+ {
+ var assemblies = new[]
+ {
+ typeof(object).Assembly,
+ typeof(Task).Assembly,
+ typeof(IServiceProvider).Assembly,
+ typeof(Microsoft.Extensions.DependencyInjection.IServiceCollection).Assembly,
+ typeof(Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions).Assembly,
+ typeof(Cortex.Mediator.IMediator).Assembly,
+ Assembly.Load("System.Runtime"),
+ Assembly.Load("System.Collections"),
+ Assembly.Load("System.Linq"),
+ Assembly.Load("netstandard"),
+ };
+
+ return assemblies.Select(a => MetadataReference.CreateFromFile(a.Location))
+ .Cast()
+ .ToArray();
+ }
+
+ private static (GeneratorDriverRunResult Result, Compilation OutputCompilation) RunGenerator(string source)
+ {
+ var syntaxTree = CSharpSyntaxTree.ParseText(source);
+
+ var compilation = CSharpCompilation.Create(
+ assemblyName: "TestAssembly",
+ syntaxTrees: new[] { syntaxTree },
+ references: SharedReferences,
+ options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
+
+ var generator = new CortexMediatorGenerator();
+ GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
+
+ driver = driver.RunGeneratorsAndUpdateCompilation(
+ compilation,
+ out var outputCompilation,
+ out var diagnostics);
+
+ var result = driver.GetRunResult();
+ return (result, outputCompilation);
+ }
+
+ [Fact]
+ public void NoAttributeProducesNoGeneratedSource()
+ {
+ var source = @"
+namespace TestApp
+{
+ public class MyClass { }
+}";
+
+ var (result, _) = RunGenerator(source);
+
+ // Without the assembly attribute, only the attribute definition file is generated (PostInitializationOutput)
+ // No GeneratedMediator or GeneratedServiceCollectionExtensions should be emitted
+ var generatedFiles = result.Results[0].GeneratedSources;
+ Assert.Single(generatedFiles); // Only the attribute definition
+ Assert.Contains("CortexMediatorGenerationAttribute", generatedFiles[0].SourceText.ToString());
+ Assert.DoesNotContain(generatedFiles, f => f.HintName == "GeneratedMediator.g.cs");
+ }
+
+ [Fact]
+ public void AttributePresent_GeneratesMediator()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class CreateUserCommand : ICommand
+ {
+ public string Name { get; set; }
+ }
+
+ public class CreateUserCommandHandler : ICommandHandler
+ {
+ public Task Handle(CreateUserCommand command, CancellationToken cancellationToken)
+ {
+ return Task.FromResult(System.Guid.NewGuid());
+ }
+ }
+}";
+
+ var (result, outputCompilation) = RunGenerator(source);
+
+ var generatedSources = result.Results[0].GeneratedSources;
+
+ // Should have 3 files: attribute + GeneratedMediator + GeneratedServiceCollectionExtensions
+ Assert.Equal(3, generatedSources.Length);
+
+ var mediatorSource = generatedSources
+ .First(s => s.HintName == "GeneratedMediator.g.cs")
+ .SourceText.ToString();
+
+ // Verify switch dispatch
+ Assert.Contains("case global::TestApp.CreateUserCommand typed:", mediatorSource);
+ Assert.Contains("SendCommandAsync", mediatorSource);
+ }
+
+ [Fact]
+ public void AttributePresent_GeneratesDIRegistrations()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class CreateUserCommand : ICommand
+ {
+ public string Name { get; set; }
+ }
+
+ public class CreateUserCommandHandler : ICommandHandler
+ {
+ public Task Handle(CreateUserCommand command, CancellationToken cancellationToken)
+ {
+ return Task.FromResult(System.Guid.NewGuid());
+ }
+ }
+}";
+
+ var (result, _) = RunGenerator(source);
+
+ var diSource = result.Results[0].GeneratedSources
+ .First(s => s.HintName == "GeneratedServiceCollectionExtensions.g.cs")
+ .SourceText.ToString();
+
+ Assert.Contains("AddCortexGeneratedMediator", diSource);
+ Assert.Contains("GeneratedMediator", diSource);
+ Assert.Contains("global::TestApp.CreateUserCommandHandler", diSource);
+ Assert.Contains("ServiceLifetime.Scoped", diSource);
+ }
+
+ [Fact]
+ public void VoidCommand_GeneratesSwitchDispatch()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class DeleteUserCommand : ICommand
+ {
+ public int UserId { get; set; }
+ }
+
+ public class DeleteUserCommandHandler : ICommandHandler
+ {
+ public Task Handle(DeleteUserCommand command, CancellationToken cancellationToken)
+ {
+ return Task.CompletedTask;
+ }
+ }
+}";
+
+ var (result, _) = RunGenerator(source);
+
+ var mediatorSource = result.Results[0].GeneratedSources
+ .First(s => s.HintName == "GeneratedMediator.g.cs")
+ .SourceText.ToString();
+
+ Assert.Contains("case global::TestApp.DeleteUserCommand typed:", mediatorSource);
+ }
+
+ [Fact]
+ public void Query_GeneratesSwitchDispatch()
+ {
+ var source = @"
+using Cortex.Mediator.Queries;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class GetUserQuery : IQuery
+ {
+ public int UserId { get; set; }
+ }
+
+ public class GetUserQueryHandler : IQueryHandler
+ {
+ public Task Handle(GetUserQuery query, CancellationToken cancellationToken)
+ {
+ return Task.FromResult(""user"");
+ }
+ }
+}";
+
+ var (result, _) = RunGenerator(source);
+
+ var mediatorSource = result.Results[0].GeneratedSources
+ .First(s => s.HintName == "GeneratedMediator.g.cs")
+ .SourceText.ToString();
+
+ Assert.Contains("case global::TestApp.GetUserQuery typed:", mediatorSource);
+ Assert.Contains("SendQueryAsync", mediatorSource);
+ }
+
+ [Fact]
+ public void Notification_GeneratesSwitchDispatch()
+ {
+ var source = @"
+using Cortex.Mediator.Notifications;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class UserCreatedNotification : INotification
+ {
+ public int UserId { get; set; }
+ }
+
+ public class UserCreatedHandler : INotificationHandler
+ {
+ public Task Handle(UserCreatedNotification notification, CancellationToken cancellationToken)
+ {
+ return Task.CompletedTask;
+ }
+ }
+}";
+
+ var (result, _) = RunGenerator(source);
+
+ var mediatorSource = result.Results[0].GeneratedSources
+ .First(s => s.HintName == "GeneratedMediator.g.cs")
+ .SourceText.ToString();
+
+ Assert.Contains("case global::TestApp.UserCreatedNotification typed:", mediatorSource);
+ }
+
+ [Fact]
+ public void StreamQuery_GeneratesSwitchDispatch()
+ {
+ var source = @"
+using Cortex.Mediator.Streaming;
+using System.Collections.Generic;
+using System.Threading;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class GetUsersStreamQuery : IStreamQuery
+ {
+ }
+
+ public class GetUsersStreamQueryHandler : IStreamQueryHandler
+ {
+ public async IAsyncEnumerable Handle(GetUsersStreamQuery query, System.Threading.CancellationToken cancellationToken)
+ {
+ yield return ""user1"";
+ }
+ }
+}";
+
+ var (result, _) = RunGenerator(source);
+
+ var mediatorSource = result.Results[0].GeneratedSources
+ .First(s => s.HintName == "GeneratedMediator.g.cs")
+ .SourceText.ToString();
+
+ Assert.Contains("case global::TestApp.GetUsersStreamQuery typed:", mediatorSource);
+ }
+
+ [Fact]
+ public void Processor_RegisteredInDI()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+using Cortex.Mediator.Processors;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class MyCommand : ICommand { }
+
+ public class MyCommandHandler : ICommandHandler
+ {
+ public Task Handle(MyCommand command, CancellationToken ct) => Task.FromResult(1);
+ }
+
+ public class MyPreProcessor : IRequestPreProcessor
+ {
+ public Task ProcessAsync(MyCommand request, CancellationToken ct) => Task.CompletedTask;
+ }
+}";
+
+ var (result, _) = RunGenerator(source);
+
+ var diSource = result.Results[0].GeneratedSources
+ .First(s => s.HintName == "GeneratedServiceCollectionExtensions.g.cs")
+ .SourceText.ToString();
+
+ Assert.Contains("global::TestApp.MyPreProcessor", diSource);
+ Assert.Contains("ServiceLifetime.Transient", diSource);
+ }
+
+ [Fact]
+ public void MultipleHandlers_AllIncludedInSwitch()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+using Cortex.Mediator.Queries;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class CommandA : ICommand { }
+ public class CommandB : ICommand { }
+ public class QueryA : IQuery { }
+
+ public class HandlerA : ICommandHandler
+ {
+ public Task Handle(CommandA command, CancellationToken ct) => Task.FromResult(1);
+ }
+
+ public class HandlerB : ICommandHandler
+ {
+ public Task Handle(CommandB command, CancellationToken ct) => Task.FromResult(""b"");
+ }
+
+ public class QueryHandlerA : IQueryHandler
+ {
+ public Task Handle(QueryA query, CancellationToken ct) => Task.FromResult(true);
+ }
+}";
+
+ var (result, _) = RunGenerator(source);
+
+ var mediatorSource = result.Results[0].GeneratedSources
+ .First(s => s.HintName == "GeneratedMediator.g.cs")
+ .SourceText.ToString();
+
+ Assert.Contains("case global::TestApp.CommandA typed:", mediatorSource);
+ Assert.Contains("case global::TestApp.CommandB typed:", mediatorSource);
+ Assert.Contains("case global::TestApp.QueryA typed:", mediatorSource);
+ }
+
+ [Fact]
+ public void GeneratedCode_CompilesToValidOutput()
+ {
+ var source = @"
+using Cortex.Mediator.Commands;
+using Cortex.Mediator.Queries;
+using Cortex.Mediator.Notifications;
+using System.Threading;
+using System.Threading.Tasks;
+
+[assembly: Cortex.Mediator.SourceGeneration.CortexMediatorGeneration]
+
+namespace TestApp
+{
+ public class CreateCommand : ICommand { }
+ public class DeleteCommand : ICommand { }
+ public class GetQuery : IQuery { }
+ public class AlertNotification : INotification { }
+
+ public class CreateHandler : ICommandHandler
+ {
+ public Task Handle(CreateCommand cmd, CancellationToken ct) => Task.FromResult(42);
+ }
+ public class DeleteHandler : ICommandHandler
+ {
+ public Task Handle(DeleteCommand cmd, CancellationToken ct) => Task.CompletedTask;
+ }
+ public class GetHandler : IQueryHandler
+ {
+ public Task Handle(GetQuery q, CancellationToken ct) => Task.FromResult(""ok"");
+ }
+ public class AlertHandler : INotificationHandler
+ {
+ public Task Handle(AlertNotification n, CancellationToken ct) => Task.CompletedTask;
+ }
+}";
+
+ var (result, outputCompilation) = RunGenerator(source);
+
+ var diagnostics = outputCompilation.GetDiagnostics()
+ .Where(d => d.Severity == DiagnosticSeverity.Error)
+ .ToArray();
+
+ Assert.Empty(diagnostics);
+ }
+}