diff --git a/.gitignore b/.gitignore
index c207240c..70984902 100644
--- a/.gitignore
+++ b/.gitignore
@@ -263,3 +263,5 @@ publish.*.bat
BenchmarkDotNet.Artifacts/
tools/
+.omc
+.oh-my-code
\ No newline at end of file
diff --git a/AspectCore-Framework.sln b/AspectCore-Framework.sln
index 07362618..2c3f6ddc 100644
--- a/AspectCore-Framework.sln
+++ b/AspectCore-Framework.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.12.35514.174 d17.12
+VisualStudioVersion = 17.12.35514.174
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{73F38B6C-1A05-41C8-8029-D1F2F41D3279}"
EndProject
@@ -89,112 +89,330 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.Refle
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Core.Benchmark", "benchmark\AspectCore.Core.Benchmark\AspectCore.Core.Benchmark.csproj", "{CBE603D2-7D4F-462E-B728-F5268950FA07}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspectCore.SourceGenerator", "src\AspectCore.SourceGenerator\AspectCore.SourceGenerator.csproj", "{D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}"
+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
{3C7F3B0E-1967-4D18-BECC-78A2C81821A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C7F3B0E-1967-4D18-BECC-78A2C81821A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3C7F3B0E-1967-4D18-BECC-78A2C81821A3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3C7F3B0E-1967-4D18-BECC-78A2C81821A3}.Debug|x64.Build.0 = Debug|Any CPU
+ {3C7F3B0E-1967-4D18-BECC-78A2C81821A3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {3C7F3B0E-1967-4D18-BECC-78A2C81821A3}.Debug|x86.Build.0 = Debug|Any CPU
{3C7F3B0E-1967-4D18-BECC-78A2C81821A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C7F3B0E-1967-4D18-BECC-78A2C81821A3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3C7F3B0E-1967-4D18-BECC-78A2C81821A3}.Release|x64.ActiveCfg = Release|Any CPU
+ {3C7F3B0E-1967-4D18-BECC-78A2C81821A3}.Release|x64.Build.0 = Release|Any CPU
+ {3C7F3B0E-1967-4D18-BECC-78A2C81821A3}.Release|x86.ActiveCfg = Release|Any CPU
+ {3C7F3B0E-1967-4D18-BECC-78A2C81821A3}.Release|x86.Build.0 = Release|Any CPU
{1C8AA590-9123-42AC-ACFE-53019D69919B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1C8AA590-9123-42AC-ACFE-53019D69919B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1C8AA590-9123-42AC-ACFE-53019D69919B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1C8AA590-9123-42AC-ACFE-53019D69919B}.Debug|x64.Build.0 = Debug|Any CPU
+ {1C8AA590-9123-42AC-ACFE-53019D69919B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1C8AA590-9123-42AC-ACFE-53019D69919B}.Debug|x86.Build.0 = Debug|Any CPU
{1C8AA590-9123-42AC-ACFE-53019D69919B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C8AA590-9123-42AC-ACFE-53019D69919B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1C8AA590-9123-42AC-ACFE-53019D69919B}.Release|x64.ActiveCfg = Release|Any CPU
+ {1C8AA590-9123-42AC-ACFE-53019D69919B}.Release|x64.Build.0 = Release|Any CPU
+ {1C8AA590-9123-42AC-ACFE-53019D69919B}.Release|x86.ActiveCfg = Release|Any CPU
+ {1C8AA590-9123-42AC-ACFE-53019D69919B}.Release|x86.Build.0 = Release|Any CPU
{A071427F-FD85-467E-ABF5-720482D91939}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A071427F-FD85-467E-ABF5-720482D91939}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A071427F-FD85-467E-ABF5-720482D91939}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A071427F-FD85-467E-ABF5-720482D91939}.Debug|x64.Build.0 = Debug|Any CPU
+ {A071427F-FD85-467E-ABF5-720482D91939}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A071427F-FD85-467E-ABF5-720482D91939}.Debug|x86.Build.0 = Debug|Any CPU
{A071427F-FD85-467E-ABF5-720482D91939}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A071427F-FD85-467E-ABF5-720482D91939}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A071427F-FD85-467E-ABF5-720482D91939}.Release|x64.ActiveCfg = Release|Any CPU
+ {A071427F-FD85-467E-ABF5-720482D91939}.Release|x64.Build.0 = Release|Any CPU
+ {A071427F-FD85-467E-ABF5-720482D91939}.Release|x86.ActiveCfg = Release|Any CPU
+ {A071427F-FD85-467E-ABF5-720482D91939}.Release|x86.Build.0 = Release|Any CPU
{46768117-585A-493D-BA33-F3FEB6F072FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{46768117-585A-493D-BA33-F3FEB6F072FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {46768117-585A-493D-BA33-F3FEB6F072FA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {46768117-585A-493D-BA33-F3FEB6F072FA}.Debug|x64.Build.0 = Debug|Any CPU
+ {46768117-585A-493D-BA33-F3FEB6F072FA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {46768117-585A-493D-BA33-F3FEB6F072FA}.Debug|x86.Build.0 = Debug|Any CPU
{46768117-585A-493D-BA33-F3FEB6F072FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46768117-585A-493D-BA33-F3FEB6F072FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {46768117-585A-493D-BA33-F3FEB6F072FA}.Release|x64.ActiveCfg = Release|Any CPU
+ {46768117-585A-493D-BA33-F3FEB6F072FA}.Release|x64.Build.0 = Release|Any CPU
+ {46768117-585A-493D-BA33-F3FEB6F072FA}.Release|x86.ActiveCfg = Release|Any CPU
+ {46768117-585A-493D-BA33-F3FEB6F072FA}.Release|x86.Build.0 = Release|Any CPU
{F9D4B333-04E5-4452-BB8B-D2312504A22A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F9D4B333-04E5-4452-BB8B-D2312504A22A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F9D4B333-04E5-4452-BB8B-D2312504A22A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F9D4B333-04E5-4452-BB8B-D2312504A22A}.Debug|x64.Build.0 = Debug|Any CPU
+ {F9D4B333-04E5-4452-BB8B-D2312504A22A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F9D4B333-04E5-4452-BB8B-D2312504A22A}.Debug|x86.Build.0 = Debug|Any CPU
{F9D4B333-04E5-4452-BB8B-D2312504A22A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F9D4B333-04E5-4452-BB8B-D2312504A22A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F9D4B333-04E5-4452-BB8B-D2312504A22A}.Release|x64.ActiveCfg = Release|Any CPU
+ {F9D4B333-04E5-4452-BB8B-D2312504A22A}.Release|x64.Build.0 = Release|Any CPU
+ {F9D4B333-04E5-4452-BB8B-D2312504A22A}.Release|x86.ActiveCfg = Release|Any CPU
+ {F9D4B333-04E5-4452-BB8B-D2312504A22A}.Release|x86.Build.0 = Release|Any CPU
{5231CC65-7F24-44A8-BBBF-C540E661963C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5231CC65-7F24-44A8-BBBF-C540E661963C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5231CC65-7F24-44A8-BBBF-C540E661963C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5231CC65-7F24-44A8-BBBF-C540E661963C}.Debug|x64.Build.0 = Debug|Any CPU
+ {5231CC65-7F24-44A8-BBBF-C540E661963C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5231CC65-7F24-44A8-BBBF-C540E661963C}.Debug|x86.Build.0 = Debug|Any CPU
{5231CC65-7F24-44A8-BBBF-C540E661963C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5231CC65-7F24-44A8-BBBF-C540E661963C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5231CC65-7F24-44A8-BBBF-C540E661963C}.Release|x64.ActiveCfg = Release|Any CPU
+ {5231CC65-7F24-44A8-BBBF-C540E661963C}.Release|x64.Build.0 = Release|Any CPU
+ {5231CC65-7F24-44A8-BBBF-C540E661963C}.Release|x86.ActiveCfg = Release|Any CPU
+ {5231CC65-7F24-44A8-BBBF-C540E661963C}.Release|x86.Build.0 = Release|Any CPU
{64679CBB-C106-49E3-9D0C-80485E6651DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64679CBB-C106-49E3-9D0C-80485E6651DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {64679CBB-C106-49E3-9D0C-80485E6651DA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {64679CBB-C106-49E3-9D0C-80485E6651DA}.Debug|x64.Build.0 = Debug|Any CPU
+ {64679CBB-C106-49E3-9D0C-80485E6651DA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {64679CBB-C106-49E3-9D0C-80485E6651DA}.Debug|x86.Build.0 = Debug|Any CPU
{64679CBB-C106-49E3-9D0C-80485E6651DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64679CBB-C106-49E3-9D0C-80485E6651DA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {64679CBB-C106-49E3-9D0C-80485E6651DA}.Release|x64.ActiveCfg = Release|Any CPU
+ {64679CBB-C106-49E3-9D0C-80485E6651DA}.Release|x64.Build.0 = Release|Any CPU
+ {64679CBB-C106-49E3-9D0C-80485E6651DA}.Release|x86.ActiveCfg = Release|Any CPU
+ {64679CBB-C106-49E3-9D0C-80485E6651DA}.Release|x86.Build.0 = Release|Any CPU
{9496C6A6-49B7-4C35-AB84-843680585F02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9496C6A6-49B7-4C35-AB84-843680585F02}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9496C6A6-49B7-4C35-AB84-843680585F02}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9496C6A6-49B7-4C35-AB84-843680585F02}.Debug|x64.Build.0 = Debug|Any CPU
+ {9496C6A6-49B7-4C35-AB84-843680585F02}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9496C6A6-49B7-4C35-AB84-843680585F02}.Debug|x86.Build.0 = Debug|Any CPU
{9496C6A6-49B7-4C35-AB84-843680585F02}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9496C6A6-49B7-4C35-AB84-843680585F02}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9496C6A6-49B7-4C35-AB84-843680585F02}.Release|x64.ActiveCfg = Release|Any CPU
+ {9496C6A6-49B7-4C35-AB84-843680585F02}.Release|x64.Build.0 = Release|Any CPU
+ {9496C6A6-49B7-4C35-AB84-843680585F02}.Release|x86.ActiveCfg = Release|Any CPU
+ {9496C6A6-49B7-4C35-AB84-843680585F02}.Release|x86.Build.0 = Release|Any CPU
{1121F98F-5F1C-4FC3-9A8C-BF1FBE0619C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1121F98F-5F1C-4FC3-9A8C-BF1FBE0619C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1121F98F-5F1C-4FC3-9A8C-BF1FBE0619C5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1121F98F-5F1C-4FC3-9A8C-BF1FBE0619C5}.Debug|x64.Build.0 = Debug|Any CPU
+ {1121F98F-5F1C-4FC3-9A8C-BF1FBE0619C5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1121F98F-5F1C-4FC3-9A8C-BF1FBE0619C5}.Debug|x86.Build.0 = Debug|Any CPU
{1121F98F-5F1C-4FC3-9A8C-BF1FBE0619C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1121F98F-5F1C-4FC3-9A8C-BF1FBE0619C5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1121F98F-5F1C-4FC3-9A8C-BF1FBE0619C5}.Release|x64.ActiveCfg = Release|Any CPU
+ {1121F98F-5F1C-4FC3-9A8C-BF1FBE0619C5}.Release|x64.Build.0 = Release|Any CPU
+ {1121F98F-5F1C-4FC3-9A8C-BF1FBE0619C5}.Release|x86.ActiveCfg = Release|Any CPU
+ {1121F98F-5F1C-4FC3-9A8C-BF1FBE0619C5}.Release|x86.Build.0 = Release|Any CPU
{5BC13EE3-9148-46DF-9B15-F2FE715FEF88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5BC13EE3-9148-46DF-9B15-F2FE715FEF88}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5BC13EE3-9148-46DF-9B15-F2FE715FEF88}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5BC13EE3-9148-46DF-9B15-F2FE715FEF88}.Debug|x64.Build.0 = Debug|Any CPU
+ {5BC13EE3-9148-46DF-9B15-F2FE715FEF88}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5BC13EE3-9148-46DF-9B15-F2FE715FEF88}.Debug|x86.Build.0 = Debug|Any CPU
{5BC13EE3-9148-46DF-9B15-F2FE715FEF88}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5BC13EE3-9148-46DF-9B15-F2FE715FEF88}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5BC13EE3-9148-46DF-9B15-F2FE715FEF88}.Release|x64.ActiveCfg = Release|Any CPU
+ {5BC13EE3-9148-46DF-9B15-F2FE715FEF88}.Release|x64.Build.0 = Release|Any CPU
+ {5BC13EE3-9148-46DF-9B15-F2FE715FEF88}.Release|x86.ActiveCfg = Release|Any CPU
+ {5BC13EE3-9148-46DF-9B15-F2FE715FEF88}.Release|x86.Build.0 = Release|Any CPU
{CD119E92-C6AC-4EB9-8B31-8744DEB1A20C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD119E92-C6AC-4EB9-8B31-8744DEB1A20C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD119E92-C6AC-4EB9-8B31-8744DEB1A20C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CD119E92-C6AC-4EB9-8B31-8744DEB1A20C}.Debug|x64.Build.0 = Debug|Any CPU
+ {CD119E92-C6AC-4EB9-8B31-8744DEB1A20C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CD119E92-C6AC-4EB9-8B31-8744DEB1A20C}.Debug|x86.Build.0 = Debug|Any CPU
{CD119E92-C6AC-4EB9-8B31-8744DEB1A20C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD119E92-C6AC-4EB9-8B31-8744DEB1A20C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CD119E92-C6AC-4EB9-8B31-8744DEB1A20C}.Release|x64.ActiveCfg = Release|Any CPU
+ {CD119E92-C6AC-4EB9-8B31-8744DEB1A20C}.Release|x64.Build.0 = Release|Any CPU
+ {CD119E92-C6AC-4EB9-8B31-8744DEB1A20C}.Release|x86.ActiveCfg = Release|Any CPU
+ {CD119E92-C6AC-4EB9-8B31-8744DEB1A20C}.Release|x86.Build.0 = Release|Any CPU
{5A420DCD-683F-4E30-AE63-DA5B9253E157}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5A420DCD-683F-4E30-AE63-DA5B9253E157}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5A420DCD-683F-4E30-AE63-DA5B9253E157}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5A420DCD-683F-4E30-AE63-DA5B9253E157}.Debug|x64.Build.0 = Debug|Any CPU
+ {5A420DCD-683F-4E30-AE63-DA5B9253E157}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5A420DCD-683F-4E30-AE63-DA5B9253E157}.Debug|x86.Build.0 = Debug|Any CPU
{5A420DCD-683F-4E30-AE63-DA5B9253E157}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5A420DCD-683F-4E30-AE63-DA5B9253E157}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5A420DCD-683F-4E30-AE63-DA5B9253E157}.Release|x64.ActiveCfg = Release|Any CPU
+ {5A420DCD-683F-4E30-AE63-DA5B9253E157}.Release|x64.Build.0 = Release|Any CPU
+ {5A420DCD-683F-4E30-AE63-DA5B9253E157}.Release|x86.ActiveCfg = Release|Any CPU
+ {5A420DCD-683F-4E30-AE63-DA5B9253E157}.Release|x86.Build.0 = Release|Any CPU
{6D1211BC-34A4-4D17-B3D5-3D51F19F9D81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6D1211BC-34A4-4D17-B3D5-3D51F19F9D81}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6D1211BC-34A4-4D17-B3D5-3D51F19F9D81}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6D1211BC-34A4-4D17-B3D5-3D51F19F9D81}.Debug|x64.Build.0 = Debug|Any CPU
+ {6D1211BC-34A4-4D17-B3D5-3D51F19F9D81}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6D1211BC-34A4-4D17-B3D5-3D51F19F9D81}.Debug|x86.Build.0 = Debug|Any CPU
{6D1211BC-34A4-4D17-B3D5-3D51F19F9D81}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6D1211BC-34A4-4D17-B3D5-3D51F19F9D81}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6D1211BC-34A4-4D17-B3D5-3D51F19F9D81}.Release|x64.ActiveCfg = Release|Any CPU
+ {6D1211BC-34A4-4D17-B3D5-3D51F19F9D81}.Release|x64.Build.0 = Release|Any CPU
+ {6D1211BC-34A4-4D17-B3D5-3D51F19F9D81}.Release|x86.ActiveCfg = Release|Any CPU
+ {6D1211BC-34A4-4D17-B3D5-3D51F19F9D81}.Release|x86.Build.0 = Release|Any CPU
{D58DB130-DC4F-4E88-BA19-558CAB4DB542}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D58DB130-DC4F-4E88-BA19-558CAB4DB542}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D58DB130-DC4F-4E88-BA19-558CAB4DB542}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D58DB130-DC4F-4E88-BA19-558CAB4DB542}.Debug|x64.Build.0 = Debug|Any CPU
+ {D58DB130-DC4F-4E88-BA19-558CAB4DB542}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D58DB130-DC4F-4E88-BA19-558CAB4DB542}.Debug|x86.Build.0 = Debug|Any CPU
{D58DB130-DC4F-4E88-BA19-558CAB4DB542}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D58DB130-DC4F-4E88-BA19-558CAB4DB542}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D58DB130-DC4F-4E88-BA19-558CAB4DB542}.Release|x64.ActiveCfg = Release|Any CPU
+ {D58DB130-DC4F-4E88-BA19-558CAB4DB542}.Release|x64.Build.0 = Release|Any CPU
+ {D58DB130-DC4F-4E88-BA19-558CAB4DB542}.Release|x86.ActiveCfg = Release|Any CPU
+ {D58DB130-DC4F-4E88-BA19-558CAB4DB542}.Release|x86.Build.0 = Release|Any CPU
{677D3CD4-2B4B-4E09-A671-5014F2D85261}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{677D3CD4-2B4B-4E09-A671-5014F2D85261}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {677D3CD4-2B4B-4E09-A671-5014F2D85261}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {677D3CD4-2B4B-4E09-A671-5014F2D85261}.Debug|x64.Build.0 = Debug|Any CPU
+ {677D3CD4-2B4B-4E09-A671-5014F2D85261}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {677D3CD4-2B4B-4E09-A671-5014F2D85261}.Debug|x86.Build.0 = Debug|Any CPU
{677D3CD4-2B4B-4E09-A671-5014F2D85261}.Release|Any CPU.ActiveCfg = Release|Any CPU
{677D3CD4-2B4B-4E09-A671-5014F2D85261}.Release|Any CPU.Build.0 = Release|Any CPU
+ {677D3CD4-2B4B-4E09-A671-5014F2D85261}.Release|x64.ActiveCfg = Release|Any CPU
+ {677D3CD4-2B4B-4E09-A671-5014F2D85261}.Release|x64.Build.0 = Release|Any CPU
+ {677D3CD4-2B4B-4E09-A671-5014F2D85261}.Release|x86.ActiveCfg = Release|Any CPU
+ {677D3CD4-2B4B-4E09-A671-5014F2D85261}.Release|x86.Build.0 = Release|Any CPU
{D095BB95-1558-48A8-9556-2085E4BC3E47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D095BB95-1558-48A8-9556-2085E4BC3E47}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D095BB95-1558-48A8-9556-2085E4BC3E47}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D095BB95-1558-48A8-9556-2085E4BC3E47}.Debug|x64.Build.0 = Debug|Any CPU
+ {D095BB95-1558-48A8-9556-2085E4BC3E47}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D095BB95-1558-48A8-9556-2085E4BC3E47}.Debug|x86.Build.0 = Debug|Any CPU
{D095BB95-1558-48A8-9556-2085E4BC3E47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D095BB95-1558-48A8-9556-2085E4BC3E47}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D095BB95-1558-48A8-9556-2085E4BC3E47}.Release|x64.ActiveCfg = Release|Any CPU
+ {D095BB95-1558-48A8-9556-2085E4BC3E47}.Release|x64.Build.0 = Release|Any CPU
+ {D095BB95-1558-48A8-9556-2085E4BC3E47}.Release|x86.ActiveCfg = Release|Any CPU
+ {D095BB95-1558-48A8-9556-2085E4BC3E47}.Release|x86.Build.0 = Release|Any CPU
{4E2B14E4-9669-4770-BB25-584545A15AB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E2B14E4-9669-4770-BB25-584545A15AB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4E2B14E4-9669-4770-BB25-584545A15AB3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4E2B14E4-9669-4770-BB25-584545A15AB3}.Debug|x64.Build.0 = Debug|Any CPU
+ {4E2B14E4-9669-4770-BB25-584545A15AB3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4E2B14E4-9669-4770-BB25-584545A15AB3}.Debug|x86.Build.0 = Debug|Any CPU
{4E2B14E4-9669-4770-BB25-584545A15AB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E2B14E4-9669-4770-BB25-584545A15AB3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4E2B14E4-9669-4770-BB25-584545A15AB3}.Release|x64.ActiveCfg = Release|Any CPU
+ {4E2B14E4-9669-4770-BB25-584545A15AB3}.Release|x64.Build.0 = Release|Any CPU
+ {4E2B14E4-9669-4770-BB25-584545A15AB3}.Release|x86.ActiveCfg = Release|Any CPU
+ {4E2B14E4-9669-4770-BB25-584545A15AB3}.Release|x86.Build.0 = Release|Any CPU
{34DD55AF-02A7-48DF-A770-3E565C56DCE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34DD55AF-02A7-48DF-A770-3E565C56DCE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {34DD55AF-02A7-48DF-A770-3E565C56DCE9}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {34DD55AF-02A7-48DF-A770-3E565C56DCE9}.Debug|x64.Build.0 = Debug|Any CPU
+ {34DD55AF-02A7-48DF-A770-3E565C56DCE9}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {34DD55AF-02A7-48DF-A770-3E565C56DCE9}.Debug|x86.Build.0 = Debug|Any CPU
{34DD55AF-02A7-48DF-A770-3E565C56DCE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34DD55AF-02A7-48DF-A770-3E565C56DCE9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {34DD55AF-02A7-48DF-A770-3E565C56DCE9}.Release|x64.ActiveCfg = Release|Any CPU
+ {34DD55AF-02A7-48DF-A770-3E565C56DCE9}.Release|x64.Build.0 = Release|Any CPU
+ {34DD55AF-02A7-48DF-A770-3E565C56DCE9}.Release|x86.ActiveCfg = Release|Any CPU
+ {34DD55AF-02A7-48DF-A770-3E565C56DCE9}.Release|x86.Build.0 = Release|Any CPU
{B666F5B6-084B-40F9-96C7-1E593168357C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B666F5B6-084B-40F9-96C7-1E593168357C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B666F5B6-084B-40F9-96C7-1E593168357C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B666F5B6-084B-40F9-96C7-1E593168357C}.Debug|x64.Build.0 = Debug|Any CPU
+ {B666F5B6-084B-40F9-96C7-1E593168357C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B666F5B6-084B-40F9-96C7-1E593168357C}.Debug|x86.Build.0 = Debug|Any CPU
{B666F5B6-084B-40F9-96C7-1E593168357C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B666F5B6-084B-40F9-96C7-1E593168357C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B666F5B6-084B-40F9-96C7-1E593168357C}.Release|x64.ActiveCfg = Release|Any CPU
+ {B666F5B6-084B-40F9-96C7-1E593168357C}.Release|x64.Build.0 = Release|Any CPU
+ {B666F5B6-084B-40F9-96C7-1E593168357C}.Release|x86.ActiveCfg = Release|Any CPU
+ {B666F5B6-084B-40F9-96C7-1E593168357C}.Release|x86.Build.0 = Release|Any CPU
{2F836A77-7BDA-4AC1-ABB9-B0FB07F90F03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F836A77-7BDA-4AC1-ABB9-B0FB07F90F03}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F836A77-7BDA-4AC1-ABB9-B0FB07F90F03}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2F836A77-7BDA-4AC1-ABB9-B0FB07F90F03}.Debug|x64.Build.0 = Debug|Any CPU
+ {2F836A77-7BDA-4AC1-ABB9-B0FB07F90F03}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2F836A77-7BDA-4AC1-ABB9-B0FB07F90F03}.Debug|x86.Build.0 = Debug|Any CPU
{2F836A77-7BDA-4AC1-ABB9-B0FB07F90F03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F836A77-7BDA-4AC1-ABB9-B0FB07F90F03}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2F836A77-7BDA-4AC1-ABB9-B0FB07F90F03}.Release|x64.ActiveCfg = Release|Any CPU
+ {2F836A77-7BDA-4AC1-ABB9-B0FB07F90F03}.Release|x64.Build.0 = Release|Any CPU
+ {2F836A77-7BDA-4AC1-ABB9-B0FB07F90F03}.Release|x86.ActiveCfg = Release|Any CPU
+ {2F836A77-7BDA-4AC1-ABB9-B0FB07F90F03}.Release|x86.Build.0 = Release|Any CPU
{97E4229E-E69A-4B79-A454-68309839FD3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{97E4229E-E69A-4B79-A454-68309839FD3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {97E4229E-E69A-4B79-A454-68309839FD3D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {97E4229E-E69A-4B79-A454-68309839FD3D}.Debug|x64.Build.0 = Debug|Any CPU
+ {97E4229E-E69A-4B79-A454-68309839FD3D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {97E4229E-E69A-4B79-A454-68309839FD3D}.Debug|x86.Build.0 = Debug|Any CPU
{97E4229E-E69A-4B79-A454-68309839FD3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97E4229E-E69A-4B79-A454-68309839FD3D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {97E4229E-E69A-4B79-A454-68309839FD3D}.Release|x64.ActiveCfg = Release|Any CPU
+ {97E4229E-E69A-4B79-A454-68309839FD3D}.Release|x64.Build.0 = Release|Any CPU
+ {97E4229E-E69A-4B79-A454-68309839FD3D}.Release|x86.ActiveCfg = Release|Any CPU
+ {97E4229E-E69A-4B79-A454-68309839FD3D}.Release|x86.Build.0 = Release|Any CPU
{907C3D70-09B6-4F3A-8C30-47699E30CAC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{907C3D70-09B6-4F3A-8C30-47699E30CAC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {907C3D70-09B6-4F3A-8C30-47699E30CAC3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {907C3D70-09B6-4F3A-8C30-47699E30CAC3}.Debug|x64.Build.0 = Debug|Any CPU
+ {907C3D70-09B6-4F3A-8C30-47699E30CAC3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {907C3D70-09B6-4F3A-8C30-47699E30CAC3}.Debug|x86.Build.0 = Debug|Any CPU
{907C3D70-09B6-4F3A-8C30-47699E30CAC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{907C3D70-09B6-4F3A-8C30-47699E30CAC3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {907C3D70-09B6-4F3A-8C30-47699E30CAC3}.Release|x64.ActiveCfg = Release|Any CPU
+ {907C3D70-09B6-4F3A-8C30-47699E30CAC3}.Release|x64.Build.0 = Release|Any CPU
+ {907C3D70-09B6-4F3A-8C30-47699E30CAC3}.Release|x86.ActiveCfg = Release|Any CPU
+ {907C3D70-09B6-4F3A-8C30-47699E30CAC3}.Release|x86.Build.0 = Release|Any CPU
{4A182A2D-0100-4376-B748-895CD2E5C294}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A182A2D-0100-4376-B748-895CD2E5C294}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4A182A2D-0100-4376-B748-895CD2E5C294}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4A182A2D-0100-4376-B748-895CD2E5C294}.Debug|x64.Build.0 = Debug|Any CPU
+ {4A182A2D-0100-4376-B748-895CD2E5C294}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4A182A2D-0100-4376-B748-895CD2E5C294}.Debug|x86.Build.0 = Debug|Any CPU
{4A182A2D-0100-4376-B748-895CD2E5C294}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A182A2D-0100-4376-B748-895CD2E5C294}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4A182A2D-0100-4376-B748-895CD2E5C294}.Release|x64.ActiveCfg = Release|Any CPU
+ {4A182A2D-0100-4376-B748-895CD2E5C294}.Release|x64.Build.0 = Release|Any CPU
+ {4A182A2D-0100-4376-B748-895CD2E5C294}.Release|x86.ActiveCfg = Release|Any CPU
+ {4A182A2D-0100-4376-B748-895CD2E5C294}.Release|x86.Build.0 = Release|Any CPU
{3758A961-3528-45B7-922A-8FFC3B771A61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3758A961-3528-45B7-922A-8FFC3B771A61}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3758A961-3528-45B7-922A-8FFC3B771A61}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3758A961-3528-45B7-922A-8FFC3B771A61}.Debug|x64.Build.0 = Debug|Any CPU
+ {3758A961-3528-45B7-922A-8FFC3B771A61}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {3758A961-3528-45B7-922A-8FFC3B771A61}.Debug|x86.Build.0 = Debug|Any CPU
{3758A961-3528-45B7-922A-8FFC3B771A61}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3758A961-3528-45B7-922A-8FFC3B771A61}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3758A961-3528-45B7-922A-8FFC3B771A61}.Release|x64.ActiveCfg = Release|Any CPU
+ {3758A961-3528-45B7-922A-8FFC3B771A61}.Release|x64.Build.0 = Release|Any CPU
+ {3758A961-3528-45B7-922A-8FFC3B771A61}.Release|x86.ActiveCfg = Release|Any CPU
+ {3758A961-3528-45B7-922A-8FFC3B771A61}.Release|x86.Build.0 = Release|Any CPU
{CBE603D2-7D4F-462E-B728-F5268950FA07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CBE603D2-7D4F-462E-B728-F5268950FA07}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CBE603D2-7D4F-462E-B728-F5268950FA07}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CBE603D2-7D4F-462E-B728-F5268950FA07}.Debug|x64.Build.0 = Debug|Any CPU
+ {CBE603D2-7D4F-462E-B728-F5268950FA07}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CBE603D2-7D4F-462E-B728-F5268950FA07}.Debug|x86.Build.0 = Debug|Any CPU
{CBE603D2-7D4F-462E-B728-F5268950FA07}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBE603D2-7D4F-462E-B728-F5268950FA07}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CBE603D2-7D4F-462E-B728-F5268950FA07}.Release|x64.ActiveCfg = Release|Any CPU
+ {CBE603D2-7D4F-462E-B728-F5268950FA07}.Release|x64.Build.0 = Release|Any CPU
+ {CBE603D2-7D4F-462E-B728-F5268950FA07}.Release|x86.ActiveCfg = Release|Any CPU
+ {CBE603D2-7D4F-462E-B728-F5268950FA07}.Release|x86.Build.0 = Release|Any CPU
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}.Debug|x64.Build.0 = Debug|Any CPU
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}.Debug|x86.Build.0 = Debug|Any CPU
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}.Release|x64.ActiveCfg = Release|Any CPU
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}.Release|x64.Build.0 = Release|Any CPU
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}.Release|x86.ActiveCfg = Release|Any CPU
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -229,6 +447,7 @@ Global
{4A182A2D-0100-4376-B748-895CD2E5C294} = {70C4B2AF-82FE-476F-83D0-725E8FFE2D58}
{3758A961-3528-45B7-922A-8FFC3B771A61} = {F997EA8C-8583-42B6-8945-5AEF383EDA95}
{CBE603D2-7D4F-462E-B728-F5268950FA07} = {F997EA8C-8583-42B6-8945-5AEF383EDA95}
+ {D21D0DD4-08B0-4A6A-80E4-D6F2F829FCCD} = {73F38B6C-1A05-41C8-8029-D1F2F41D3279}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {71EABBEC-431F-413C-B6CB-CFE9145BCAD4}
diff --git "a/docs/1.\344\275\277\347\224\250\346\214\207\345\215\227.md" "b/docs/1.\344\275\277\347\224\250\346\214\207\345\215\227.md"
index 77645e2a..dbc7f8a9 100644
--- "a/docs/1.\344\275\277\347\224\250\346\214\207\345\215\227.md"
+++ "b/docs/1.\344\275\277\347\224\250\346\214\207\345\215\227.md"
@@ -88,6 +88,306 @@
.UseServiceProviderFactory(new DynamicProxyServiceProviderFactory());
```
---
+
+## Source Generator AOP(可选)
+
+从现有版本开始,AspectCore 在传统的运行时 `DynamicProxy` 之外,新增了**编译期 Source Generator AOP**(下文简称 SG AOP)。它对现有用户是**显式 opt-in**:
+
+- **默认不变**:不做任何新配置时,仍然使用运行时 `DynamicProxy`(与旧版本行为一致)。
+- 你仍然使用原来的拦截器配置 API:`ConfigureDynamicProxy(...)`、`IAspectConfiguration`、`Predicates` 等都不变。
+
+### 1) 三种引擎:DynamicProxy vs SourceGenerator vs Auto
+
+引擎选择通过 `ProxyEngineOptions.Engine` 完成:
+
+```csharp
+using AspectCore.DynamicProxy;
+
+// ProxyEngine.DynamicProxy // 运行时 Emit(默认)
+// ProxyEngine.SourceGenerator // 仅使用编译期生成物(缺失则报错)
+// ProxyEngine.Auto // 优先 SG,缺失时可按策略回退 DynamicProxy
+```
+
+推荐理解方式:
+
+- **DynamicProxy**:运行时生成代理(默认)。
+- **SourceGenerator**:编译期生成代理 + registry,运行时直接查表拿到代理类型。
+- **Auto(优先 SG)**:能用 SG 就用 SG;找不到生成物时,**可选**回退到 DynamicProxy(适合渐进迁移)。
+
+### 2) 默认行为不变(不配置仍是 DynamicProxy)
+
+只有当你显式调用下列 API 之一时,才会启用“可选引擎”逻辑:
+
+- MS.DI:`ServiceCollectionExtensions.ConfigureDynamicProxyEngine(...)`
+- ServiceContext:`ServiceContextExtensions.ConfigureDynamicProxyEngine(...)`
+
+否则:即使你引用了 `AspectCore.SourceGenerator`,也仍然按默认 `DynamicProxy` 工作。
+
+### 3) 如何启用 SG AOP(必须同时满足三件事)
+
+启用 SG AOP 需要同时满足:
+
+1. **引用 analyzer**:`AspectCore.SourceGenerator`(以 analyzer 方式引用)
+2. **触发生成**:在要生成代理的类型上标记 `[AspectCoreGenerateProxy]`
+3. **运行时选择引擎**:调用 `ConfigureDynamicProxyEngine(...)` 选择 `SourceGenerator` 或 `Auto`
+
+#### 3.1 引用 analyzer:AspectCore.SourceGenerator
+
+以下是常见写法(示例):
+
+```xml
+
+
+
+
+
+
+
+
+
+```
+
+#### 3.2 触发生成:在类型上标记 [AspectCoreGenerateProxy]
+
+当前版本主要支持 **type-level** 触发:把特性标到 class 或 interface 上。
+
+```csharp
+using AspectCore.DynamicProxy;
+
+[AspectCoreGenerateProxy]
+public class MyService
+{
+ public virtual void DoWork() { }
+}
+
+[AspectCoreGenerateProxy]
+public interface IMyService
+{
+ void DoWork();
+}
+```
+
+注意:
+
+- **class 代理只会拦截可重写成员**(例如 `virtual` 方法、可重写属性)。
+- 当前 SG 节点对一些类型形态可能有限制(例如泛型类型、嵌套类型等):如果没生成成功,请看本文末 FAQ 的排查建议。
+
+#### 3.3 运行时选择引擎:ConfigureDynamicProxyEngine
+
+MS.DI(`IServiceCollection`)示例:
+
+```csharp
+using AspectCore.DynamicProxy;
+using AspectCore.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection;
+
+services.ConfigureDynamicProxyEngine(o =>
+{
+ o.Engine = ProxyEngine.Auto; // 推荐从 Auto 开始
+});
+```
+
+ServiceContext 示例:
+
+```csharp
+using AspectCore.DependencyInjection;
+using AspectCore.DynamicProxy;
+
+serviceContext.ConfigureDynamicProxyEngine(o =>
+{
+ o.Engine = ProxyEngine.Auto;
+});
+```
+
+### 4) Strict / AllowRuntimeFallback / Auto:行为与适用场景
+
+`ProxyEngineOptions` 提供三个关键开关:
+
+```csharp
+using AspectCore.DynamicProxy;
+
+services.ConfigureDynamicProxyEngine(o =>
+{
+ o.Engine = ProxyEngine.Auto;
+ o.AllowRuntimeFallback = true; // 可选:是否允许缺失生成物时回退 DynamicProxy
+ o.Strict = false; // 可选:严格模式,缺失生成物直接抛异常
+});
+```
+
+行为总结(按当前实现):
+
+- `Engine = DynamicProxy`:始终走运行时 DynamicProxy(与旧版本一致)。
+- `Engine = SourceGenerator`:只使用 SG 生成物;**缺失时会抛异常**(即使你设置了 `AllowRuntimeFallback=true` 也不会回退)。
+- `Engine = Auto`:优先使用 SG;缺失时**默认允许**回退到 DynamicProxy。
+ - 如显式设置 `AllowRuntimeFallback=false`,则缺失生成物会抛异常。
+ - 如 `Strict=true`,则缺失生成物也会抛异常(常用于 CI 做“覆盖率”强约束)。
+
+推荐实践:
+
+- **渐进迁移/本地开发**:`Auto`(默认允许回退),先让系统跑起来。
+- **CI 强约束**:`Auto + Strict=true` 或 `Auto + AllowRuntimeFallback=false`,确保“该生成的都生成了”。
+- **AOT/Trim 场景**:通常选择 `SourceGenerator`(并配合手动 registry 注册;见下文)。
+
+### 5) 常见坑(必读)
+
+#### 5.1 构造函数(ctor)匹配:为什么“看起来能注入”但运行时报错?
+
+SG AOP 下,最终注入到容器里的其实是“代理类型”。以 **class 代理**为例:
+
+- 代理类型会在你的构造函数参数列表前面额外增加一个 `IAspectActivatorFactory` 参数。
+- 代理类型会生成一组构造函数,去匹配你原本可见的构造函数(public/protected 等)。
+
+因此:
+
+- 你的服务类型必须存在**可用的构造函数**(public/protected),并且其参数都能被容器解析。
+- 如果你的服务只有私有构造函数,或依赖未注册,就会在构建/解析服务时失败。
+
+#### 5.2 为什么需要 [Dynamically]?要不要我自己加?
+
+不需要你手动添加。
+
+`[Dynamically]` 是 AspectCore 在生成的代理类型/成员上附加的一个标记(见 `AspectCore.DynamicProxy.DynamicallyAttribute`)。它主要用于标识这些类型属于“动态代理生成物”,在诊断、反射或裁剪(trimming)相关场景中作为识别点。
+
+#### 5.3 ServiceContext 也支持,但注册方式不同
+
+在 ServiceContext 模式下:
+
+- 仍可通过 `serviceContext.ConfigureDynamicProxyEngine(...)` 选择引擎。
+- 手动注册 registry 的 API 为 `serviceContext.AddSourceGeneratedProxyRegistry(ISourceGeneratedProxyRegistry registry)`(不是泛型)。
+
+示例:
+
+```csharp
+using AspectCore.DependencyInjection;
+using AspectCore.DynamicProxy;
+
+serviceContext.ConfigureDynamicProxyEngine(o => o.Engine = ProxyEngine.SourceGenerator);
+// registry 类型名/命名空间以实际生成结果为准(下同)
+serviceContext.AddSourceGeneratedProxyRegistry(new YourGeneratedRegistry());
+```
+
+#### 5.4 Trimming/AOT 下 registry discovery 可能不可用:需要手动注册
+
+运行时为了找到 SG 生成的 registry,会做两件事(概念层面):
+
+1. 扫描已加载程序集的 assembly attribute:`[AspectCoreSourceGeneratedProxyRegistry(...)]`
+2. 找到 `RegistryType` 后使用无参构造创建 registry 实例
+
+在 trimming/AOT 环境里,程序集扫描与反射创建可能受限,导致“生成了但运行时找不到”。此时请使用**手动注册**:
+
+- MS.DI:
+
+```csharp
+using AspectCore.Extensions.DependencyInjection;
+
+// registry 类型名/命名空间以实际生成结果为准
+services.AddSourceGeneratedProxyRegistry();
+```
+
+- ServiceContext:
+
+```csharp
+serviceContext.AddSourceGeneratedProxyRegistry(new YourGeneratedRegistry());
+```
+
+> 提示:当前版本的生成器默认会输出 `AspectCore.SourceGenerated.AspectCoreSourceGeneratedProxyRegistry`,并通过程序集级特性 `AspectCore.DynamicProxy.AspectCoreSourceGeneratedProxyRegistryAttribute` 暴露给运行时扫描;但具体类型名/命名空间仍应以你的项目实际生成结果为准。
+
+### 6) 最小可运行示例(MS.DI + 一个拦截器 + 一个服务)
+
+下面示例展示:引用 SG analyzer + 标记生成 + 运行时选择引擎 + 全局拦截器。
+
+```csharp
+using System;
+using System.Threading.Tasks;
+using AspectCore.Configuration;
+using AspectCore.DynamicProxy;
+using AspectCore.DynamicProxy.Parameters;
+using AspectCore.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection;
+
+public sealed class LogInterceptor : AbstractInterceptor
+{
+ public override Task Invoke(AspectContext context, AspectDelegate next)
+ {
+ Console.WriteLine($"[AOP] before: {context.ImplementationMethod.Name}");
+ return next(context);
+ }
+}
+
+[AspectCoreGenerateProxy] // 触发生成 class proxy
+public class HelloService
+{
+ public virtual void Say(string name)
+ => Console.WriteLine($"Hello, {name}");
+}
+
+public static class Demo
+{
+ public static void Main()
+ {
+ var services = new ServiceCollection();
+
+ services.AddTransient();
+
+ // 选择引擎:建议从 Auto 开始,稳定后再切 Strict/SG
+ services.ConfigureDynamicProxyEngine(o =>
+ {
+ o.Engine = ProxyEngine.Auto;
+ });
+
+ // 拦截器配置方式不变
+ services.ConfigureDynamicProxy(config =>
+ {
+ config.Interceptors.AddTyped(Predicates.ForService("*Service"));
+ });
+
+ var sp = services.BuildDynamicProxyProvider();
+
+ sp.GetRequiredService().Say("AspectCore");
+ }
+}
+```
+
+### 7) FAQ
+
+#### Q1:我启用了 SourceGenerator/Auto,但运行时提示“找不到生成的 proxy”怎么办?
+
+优先按顺序排查:
+
+1. 你的项目是否**真的引用了** `AspectCore.SourceGenerator`(以 analyzer 方式引用)?
+2. 目标类型是否标了 `[AspectCoreGenerateProxy]`?(建议从一个最小类型开始验证)
+3. 你是否调用了 `ConfigureDynamicProxyEngine(...)` 并把 `Engine` 设为 `Auto` 或 `SourceGenerator`?
+4. 你的类型是否属于当前 SG 暂不支持的形态(例如泛型/嵌套类型等)?
+5. 如果是 trimming/AOT:是否需要改为手动注册 registry(见 5.4)?
+
+#### Q2:怎么快速回退到 DynamicProxy?
+
+两种方式:
+
+- 最简单:不调用 `ConfigureDynamicProxyEngine(...)`(默认就是 DynamicProxy)。
+- 或者显式指定:
+
+```csharp
+services.ConfigureDynamicProxyEngine(o =>
+{
+ o.Engine = ProxyEngine.DynamicProxy;
+});
+```
+
+#### Q3:我只标了实现类,但注册的是接口,为什么还是没走 SG?
+
+当你以接口方式注册(例如 `services.AddTransient()`)时,容器需要的是**接口代理**。
+
+当前 SG 的 type-level 触发方式通常要求你在**接口类型**上也标记 `[AspectCoreGenerateProxy]`,以生成对应的 interface proxy:
+
+```csharp
+[AspectCoreGenerateProxy]
+public interface IMyService { void DoWork(); }
+```
+
+---
## 高级拦截器功能
### 获取方法信息
diff --git "a/docs/2.SourceGenerator-AOP-\346\212\200\346\234\257\350\256\276\350\256\241.md" "b/docs/2.SourceGenerator-AOP-\346\212\200\346\234\257\350\256\276\350\256\241.md"
new file mode 100644
index 00000000..ef1ebde3
--- /dev/null
+++ "b/docs/2.SourceGenerator-AOP-\346\212\200\346\234\257\350\256\276\350\256\241.md"
@@ -0,0 +1,255 @@
+# Source Generator AOP(编译时注入)技术设计
+
+> 目标:在保持现有用户配置 API 与默认行为不变的前提下,引入“Source Generator 预生成代理”作为与现有 DynamicProxy 并存的 AOP 方式,并确保两种方式的核心语义一致(可验证)。
+
+## 1. 背景与现状
+
+AspectCore 当前 AOP 核心模型是:
+
+- 代理方法体构造 `AspectActivatorContext`,并调用 `IAspectActivatorFactory.Create().Invoke*` 执行拦截器链。
+- 拦截器链由 `InterceptorCollector` 收集并按 `Order` 排序,最终通过 `AspectActivator` 驱动执行。
+
+关键代码定位:
+
+- 代理方法调用契约(初始化 metadata、创建 context、调用 activator、处理返回值与 ref/out 回写):`src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitor.cs:417`
+- 执行驱动(Invoke/InvokeTask/InvokeValueTask 与异常包装):`src/AspectCore.Core/DynamicProxy/AspectActivator.cs:23`
+- 拦截器收集与排序(按 `Order`):`src/AspectCore.Core/DynamicProxy/InterceptorCollector.cs:47`
+- MS.DI 编织入口(替换 ServiceDescriptor):`src/AspectCore.Extensions.DependencyInjection/ServiceCollectionBuildExtensions.cs:38`
+
+## 2. 目标与非目标
+
+### 2.1 目标(Goals)
+
+1) 提供“Source Generator 编译时注入”AOP:通过 Roslyn Incremental Generator 预生成代理类型。
+
+2) 与现有 DynamicProxy 并存且可选:
+
+- 用户可选择使用 DynamicProxy(运行时 Emit)、SourceGenerator(预生成)、或 Auto(优先 SG,缺失回退 DP)。
+- **默认行为必须不变**:未显式启用 SG 时仍走现有 DP。
+
+3) 配置 API 与能力完全兼容:
+
+- 现有 `ConfigureDynamicProxy(Action)`、`WeaveDynamicProxyService()`、`UseDynamicProxy()` 等入口不需要用户修改即可工作。
+- 拦截器选择/排序/执行语义保持一致。
+
+4) Parity 可验证:
+
+- 同一套测试用例在两种 AOP 后端均通过,并能定位差异原因。
+
+### 2.2 非目标(Non-goals)
+
+1) 不在本次引入新的拦截器模型/排序规则:仍复用 `InterceptorCollector` + `AspectActivator`。
+
+2) 不把 AOP 后端选择塞进 `IAspectConfiguration`:
+
+- **硬约束**:不得修改 `IAspectConfiguration`(接口变更会破坏第三方实现与二进制兼容)。
+
+## 3. 对外配置与兼容策略
+
+### 3.1 对外配置方式(新增但不破坏旧 API)
+
+新增独立 options(不改 `IAspectConfiguration`):
+
+```csharp
+public enum ProxyEngine
+{
+ DynamicProxy = 0, // 现有默认
+ SourceGenerator = 1, // 强制使用 SG(缺失按 Strict 策略处理)
+ Auto = 2 // 优先 SG,缺失可回退 DP
+}
+
+public sealed class ProxyEngineOptions
+{
+ public ProxyEngine Engine { get; set; } = ProxyEngine.DynamicProxy;
+
+ // Auto 下默认 true;SourceGenerator 下默认 false
+ public bool AllowRuntimeFallback { get; set; }
+
+ // true 时缺失生成物直接失败(用于 CI 确保覆盖)
+ public bool Strict { get; set; }
+}
+```
+
+提供新的扩展方法(仅新增 overload,不改旧入口):
+
+```csharp
+services.ConfigureDynamicProxyEngine(o =>
+{
+ o.Engine = ProxyEngine.Auto;
+ o.AllowRuntimeFallback = true;
+ o.Strict = false;
+});
+
+services.ConfigureDynamicProxy(cfg =>
+{
+ // 旧配置保持原样
+ cfg.Interceptors.AddTyped();
+});
+```
+
+### 3.2 默认行为不变
+
+若用户未调用 `ConfigureDynamicProxyEngine(...)` 且未设置 MSBuild 属性,则:
+
+- 仍注册 `IProxyTypeGenerator = ProxyTypeGenerator`(现有实现)
+- 仍使用 DynamicProxy 运行时生成代理类型
+
+证据:`src/AspectCore.Extensions.DependencyInjection/ServiceCollectionExtensions.cs:39`(默认注册 `ProxyTypeGenerator`)。
+
+### 3.3 启用 SG 的方式(必须显式 opt-in)
+
+启用 SG 需要同时满足:
+
+1) 引用 generator 包(analyzer):例如 `AspectCore.SourceGenerator`。
+2) 触发生成(见 §5)。
+3) 运行时选择引擎(`ConfigureDynamicProxyEngine` 或 MSBuild 属性)。
+
+## 4. 核心架构
+
+### 4.1 最小侵入扩展点:`IProxyTypeGenerator` 双实现
+
+保留现有:
+
+- `ProxyTypeGenerator`(DynamicProxy Emit)
+
+新增:
+
+- `SourceGeneratedProxyTypeGenerator`:优先从“生成物注册表”查找代理类型;按 options 决定是否回退 DP。
+
+原因:MS.DI 与其他集成都以 `IProxyTypeGenerator` 作为“获取代理类型”的入口(例如 `WeaveDynamicProxyService()`)。
+
+### 4.2 兼容性硬约束(必须满足)
+
+#### 4.2.1 代理类型身份标识
+
+SG 生成的代理类型必须添加 `DynamicallyAttribute`,否则各容器集成的 `IsProxy()` 识别将失效:
+
+- `src/AspectCore.Core/Utils/ReflectionUtils.cs:21`
+
+#### 4.2.2 ctor 精确匹配契约(interface proxy)
+
+MS.DI 与 ServiceContext 对 interface proxy ctor 的解析是“精确签名匹配”(不是 assignable):
+
+- MS.DI:`src/AspectCore.Extensions.DependencyInjection/ServiceCollectionBuildExtensions.cs:138`
+- ServiceContext:`src/AspectCore.Core/DependencyInjection/ServiceCallSiteResolver.cs:103`
+
+因此 SG interface proxy 必须提供:
+
+- `public Proxy(IAspectActivatorFactory activatorFactory, implementation)`(第二参类型必须精确等于注册的 serviceType)
+- `public Proxy(IAspectActivatorFactory activatorFactory)`(无 target 的 interface proxy 场景)
+
+#### 4.2.3 ctor 契约(class proxy)
+
+class proxy 构造参数顺序必须与现有动态代理一致:在 base ctor 参数前插入 `IAspectActivatorFactory`。
+
+- 现有逻辑参考:`src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/ClassProxyAstBuilder.cs:83`
+
+#### 4.2.4 方法体调用契约(所有代理)
+
+SG 生成的代理方法体必须严格对齐现有 IL 的行为:
+
+- 构造 `AspectActivatorContext` 并携带 `ServiceMethod/ImplementationMethod/ProxyMethod/TargetInstance/ProxyInstance/Parameters`
+- `var activator = _activatorFactory.Create();`
+- 按返回类型分别调用 `Invoke / InvokeTask / InvokeValueTask`
+- ref/out 参数通过 `AspectActivatorContext.Parameters` 回写
+
+规范来源:`src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitor.cs:417`。
+
+### 4.3 Source Generator 生成物与 Registry
+
+#### 4.3.1 生成物结构
+
+- 代理类型:interface proxy / class proxy(必要时含 interface impl stub)
+- Registry:将 `(serviceType, implType, proxyKind)` 映射到生成的 proxy `Type`
+
+#### 4.3.2 Registry 发现机制
+
+运行时需能定位 Registry(支持多程序集):
+
+- 建议:生成一个带属性标记的静态类型,例如:
+ - `[assembly: AspectCoreSourceGeneratedProxyRegistry(typeof(AspectCore.SourceGenerated.ProxyRegistry))]`
+- `SourceGeneratedProxyTypeGenerator` 在启用 SG 时扫描 `AppDomain.CurrentDomain.GetAssemblies()`,从 assembly attribute 提取 registry type 并加载映射。
+
+该机制需要兼顾 trimming/AOT:
+
+- Strict 模式下如果 Assembly 扫描受限,应提供“手动注册 registry”的 API(例如 `AddSourceGeneratedProxyRegistry(typeof(...))`)。
+
+### 4.4 引擎选择与回退策略
+
+`SourceGeneratedProxyTypeGenerator` 行为:
+
+- `Engine=DynamicProxy`:直接委托给 `ProxyTypeGenerator`
+- `Engine=SourceGenerator`:必须命中 registry;若 `Strict=true` 或 `AllowRuntimeFallback=false` 则缺失抛异常;否则可回退
+- `Engine=Auto`:先查 registry,缺失按 `AllowRuntimeFallback` 决定回退 DP 或失败
+
+## 5. 生成器策略(IncrementalGenerator)
+
+### 5.1 触发生成规则
+
+SG 无法可靠推断运行时 DI 注册,因此必须显式触发。提供三种来源(可并存):
+
+1) Attribute opt-in(推荐):
+
+- `[AspectCoreGenerateProxy]` 标记在 class/interface/assembly
+
+2) MSBuild Item:
+
+```xml
+
+
+
+
+```
+
+3) AnalyzerConfig / MSBuild 属性(全局开关,仅用于辅助规则,不作为“自动启用运行时引擎”的依据):
+
+- `build_property.AspectCoreProxyEngine=SourceGenerator|Auto`
+
+### 5.2 可代理性筛选(对齐运行时限制)
+
+生成器需尽量对齐现有运行时 `CanInherited()` 与 validator 逻辑(可生成/不可生成尽早给出诊断)。
+
+关键风险点:sealed 类型、internal 可见性、无可见 ctor、显式接口实现、开放泛型。
+
+## 6. 容器集成范围与差异矩阵
+
+目标:所有现有集成方式均可使用 SG(显式启用时),且不破坏旧行为。
+
+- MS.DI/Hosting:优先支持(可通过 DI 替换 `IProxyTypeGenerator` 实现)
+- Autofac/LightInject:依赖 `IsProxy()` 与 ctor 形状,SG proxy 必须满足 `DynamicallyAttribute` 与 ctor 契约
+- ServiceContext(内置容器):当前 `ServiceTable` 硬编码 `ProxyTypeGenerator`(`src/AspectCore.Core/DependencyInjection/ServiceTable.cs:20`),若要支持 SG 需要先引入可注入/可选择的 generator(不改变对外 API,仅内部结构调整)。
+
+## 7. 测试策略(Parity + Compatibility)
+
+### 7.1 必须覆盖的 parity 用例
+
+- ReturnKind 全覆盖:sync/void、`Task/Task`、`ValueTask/ValueTask`
+- `ref/out` 回写(含值类型、泛型参数)
+- 异常传播与 `ThrowAspectException` 包装(sync/async)
+- 元数据一致性:`AspectContext.ServiceMethod/ImplementationMethod/ProxyMethod`(含泛型方法)
+- 显式接口实现与接口默认实现(DIM)
+- `NonAspect` 与 `NonAspectPredicates` 决策一致(不该代理时 SG 也不能因为命中 registry 而强行代理)
+
+### 7.2 两种后端同跑同用例
+
+测试基架提供 `ProxyEngine` 参数化:
+
+- 以同一套服务注册、同一拦截器配置,分别启动 DP 与 SG 两个容器实例运行同一套断言。
+- Strict 模式用例:在 SG 下开启 `Strict=true` 以保证生成覆盖率。
+
+## 8. 风险与缓解
+
+1) `IAspectConfiguration` 兼容性风险:禁止修改接口;新配置承载于独立 options/DI。
+2) Registry 发现与 trimming/AOT:提供 assembly attribute + 手动注册入口。
+3) ctor 精确匹配:生成器强约束 + 单测验证 `GetConstructor(...)` 结果。
+4) ref/out 与 async 行为偏差:建立 DP/SG 同用例对照测试。
+5) ServiceContext 支持:需要先做内部结构调整(可注入 generator),否则 SG 覆盖不完整。
+
+## 9. 实施里程碑(建议)
+
+1) 新增 `AspectCore.SourceGenerator` 工程:输出 proxy + registry + 诊断。
+2) 新增 `SourceGeneratedProxyTypeGenerator` + options:实现引擎选择/回退。
+3) 接入 MS.DI/Hosting:显式启用后替换 `IProxyTypeGenerator`,默认不变。
+4) 接入 Autofac/LightInject:验证 `IsProxy()` 与 ctor 契约。
+5) 支持 ServiceContext:让 `ServiceTable` 可注入 `IProxyTypeGenerator`。
+6) 完成 parity/compatibility 测试与用户文档。
diff --git a/sample/AspectCore.Extensions.DependencyInjection.ConsoleSample/AspectCore.Extensions.DependencyInjection.ConsoleSample.csproj b/sample/AspectCore.Extensions.DependencyInjection.ConsoleSample/AspectCore.Extensions.DependencyInjection.ConsoleSample.csproj
index a55d8e4a..2ce648e0 100644
--- a/sample/AspectCore.Extensions.DependencyInjection.ConsoleSample/AspectCore.Extensions.DependencyInjection.ConsoleSample.csproj
+++ b/sample/AspectCore.Extensions.DependencyInjection.ConsoleSample/AspectCore.Extensions.DependencyInjection.ConsoleSample.csproj
@@ -13,5 +13,9 @@
+
+
-
\ No newline at end of file
+
diff --git a/sample/AspectCore.Extensions.DependencyInjection.ConsoleSample/Program.cs b/sample/AspectCore.Extensions.DependencyInjection.ConsoleSample/Program.cs
index 6c999d50..18f4ccd5 100644
--- a/sample/AspectCore.Extensions.DependencyInjection.ConsoleSample/Program.cs
+++ b/sample/AspectCore.Extensions.DependencyInjection.ConsoleSample/Program.cs
@@ -1,7 +1,9 @@
using System;
-using System.Linq;
using System.Reflection;
+using System.Threading.Tasks;
+using AspectCore.Configuration;
using AspectCore.DependencyInjection;
+using AspectCore.DynamicProxy;
using Microsoft.Extensions.DependencyInjection;
namespace AspectCore.Extensions.DependencyInjection.ConsoleSample
@@ -10,7 +12,7 @@ class Program
{
static void Main(string[] args)
{
- // sample for property injection
+ // 1) sample for property injection
var services = new ServiceCollection();
services.AddTransient();
services.AddTransient();
@@ -22,7 +24,35 @@ static void Main(string[] args)
// var sampleService = serviceResolver.Resolve();
var sampleService = serviceProvider.GetService();
sampleService.Invoke();
- Console.ReadKey();
+
+ // 2) smoke: SG AOP + registry discovery + class proxy
+ var aopServices = new ServiceCollection();
+ aopServices.AddTransient();
+ aopServices.ConfigureDynamicProxyEngine(o =>
+ {
+ o.Engine = ProxyEngine.SourceGenerator;
+ o.Strict = true;
+ });
+ aopServices.ConfigureDynamicProxy(config =>
+ {
+ config.Interceptors.AddTyped(Predicates.ForService("*Service"));
+ });
+
+ var aopProvider = aopServices.BuildDynamicProxyProvider();
+
+ var v = aopProvider.GetRequiredService().Build();
+ Console.WriteLine($"Validate(InterceptedService, strict=true): {v.Validate(typeof(Demo.InterceptedService), true)}");
+ Console.WriteLine($"IProxyTypeGenerator: {aopProvider.GetRequiredService().GetType().FullName}");
+
+ var intercepted = aopProvider.GetRequiredService();
+ Console.WriteLine($"InterceptedService runtime type: {intercepted.GetType().FullName}");
+ Console.WriteLine($"IsProxyType: {ReflectionUtils.IsProxyType(intercepted.GetType().GetTypeInfo())}");
+ intercepted.Invoke();
+
+ if (args != null && args.Length > 0 && args[0] == "--wait")
+ {
+ Console.ReadKey();
+ }
}
}
@@ -54,4 +84,30 @@ public void Invoke()
Logger?.Info("sample service invoke.");
}
}
-}
\ No newline at end of file
+
+}
+
+namespace Demo
+{
+ using System;
+ using System.Threading.Tasks;
+ using AspectCore.DynamicProxy;
+
+ public sealed class MethodExecuteLoggerInterceptor : AbstractInterceptor
+ {
+ public override Task Invoke(AspectContext context, AspectDelegate next)
+ {
+ Console.WriteLine($"[Interceptor] before: {context.ImplementationMethod.Name}");
+ return next(context);
+ }
+ }
+
+ [AspectCoreGenerateProxy]
+ public class InterceptedService
+ {
+ public virtual void Invoke()
+ {
+ Console.WriteLine("InterceptedService.Invoke");
+ }
+ }
+}
diff --git a/src/AspectCore.Abstractions/DependencyInjection/ServiceContextExtensions.cs b/src/AspectCore.Abstractions/DependencyInjection/ServiceContextExtensions.cs
index bb0acef1..9ace6ca1 100644
--- a/src/AspectCore.Abstractions/DependencyInjection/ServiceContextExtensions.cs
+++ b/src/AspectCore.Abstractions/DependencyInjection/ServiceContextExtensions.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
+using AspectCore.DynamicProxy;
namespace AspectCore.DependencyInjection
{
@@ -84,5 +86,50 @@ public static IServiceContext RemoveAll(this IServiceContext serviceContext, Typ
return serviceContext;
}
+ ///
+ /// 配置 AOP 后端引擎(DynamicProxy / SourceGenerator / Auto)。
+ ///
+ /// ServiceContext 下该配置会被运行时解析并用于选择 IProxyTypeGenerator 的实现。
+ ///
+ public static IServiceContext ConfigureDynamicProxyEngine(this IServiceContext serviceContext, Action configure)
+ {
+ if (serviceContext == null)
+ {
+ throw new ArgumentNullException(nameof(serviceContext));
+ }
+
+ var existing = serviceContext
+ .OfType()
+ .LastOrDefault(x => x.ServiceType == typeof(ProxyEngineOptions));
+
+ var options = (ProxyEngineOptions)existing?.ImplementationInstance ?? new ProxyEngineOptions();
+ configure?.Invoke(options);
+
+ if (existing != null)
+ {
+ serviceContext.Remove(existing);
+ }
+
+ serviceContext.AddInstance(options);
+ return serviceContext;
+ }
+
+ ///
+ /// 手动注册生成的 registry(用于 AOT/trim 场景避免 assembly 扫描不可用)。
+ ///
+ public static IServiceContext AddSourceGeneratedProxyRegistry(this IServiceContext serviceContext, ISourceGeneratedProxyRegistry registry)
+ {
+ if (serviceContext == null)
+ {
+ throw new ArgumentNullException(nameof(serviceContext));
+ }
+ if (registry == null)
+ {
+ throw new ArgumentNullException(nameof(registry));
+ }
+ serviceContext.AddInstance(registry);
+ return serviceContext;
+ }
+
}
}
diff --git a/src/AspectCore.Abstractions/DynamicProxy/AspectCoreGenerateProxyAttribute.cs b/src/AspectCore.Abstractions/DynamicProxy/AspectCoreGenerateProxyAttribute.cs
new file mode 100644
index 00000000..1640efbf
--- /dev/null
+++ b/src/AspectCore.Abstractions/DynamicProxy/AspectCoreGenerateProxyAttribute.cs
@@ -0,0 +1,35 @@
+using System;
+
+namespace AspectCore.DynamicProxy
+{
+ ///
+ /// 触发 AspectCore Source Generator 生成代理。
+ ///
+ /// 本节点支持:标注在 class/interface 上的无参形式。
+ ///
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
+ public sealed class AspectCoreGenerateProxyAttribute : Attribute
+ {
+ public AspectCoreGenerateProxyAttribute() { }
+
+ public AspectCoreGenerateProxyAttribute(Type serviceType, Type implementationType, SourceGeneratedProxyKind kind)
+ {
+ ServiceType = serviceType ?? throw new ArgumentNullException(nameof(serviceType));
+ ImplementationType = implementationType ?? throw new ArgumentNullException(nameof(implementationType));
+ Kind = kind;
+ }
+
+ ///
+ /// 用于 interface proxy with target 场景,指定实现类型。
+ ///
+ public AspectCoreGenerateProxyAttribute(Type implementationType)
+ {
+ ImplementationType = implementationType ?? throw new ArgumentNullException(nameof(implementationType));
+ }
+
+ // 无参构造用于 type-level 触发:此时不会携带 mapping 信息。
+ public Type ServiceType { get; } = null;
+ public Type ImplementationType { get; } = null;
+ public SourceGeneratedProxyKind? Kind { get; }
+ }
+}
diff --git a/src/AspectCore.Abstractions/DynamicProxy/AspectCoreSourceGeneratedProxyRegistryAttribute.cs b/src/AspectCore.Abstractions/DynamicProxy/AspectCoreSourceGeneratedProxyRegistryAttribute.cs
new file mode 100644
index 00000000..c49347eb
--- /dev/null
+++ b/src/AspectCore.Abstractions/DynamicProxy/AspectCoreSourceGeneratedProxyRegistryAttribute.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace AspectCore.DynamicProxy
+{
+ ///
+ /// 标记程序集包含 Source Generator 生成的 proxy registry。
+ /// 运行时通过扫描该 attribute 进行 registry 发现。
+ ///
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
+ public sealed class AspectCoreSourceGeneratedProxyRegistryAttribute : Attribute
+ {
+ public AspectCoreSourceGeneratedProxyRegistryAttribute(Type registryType)
+ {
+ RegistryType = registryType ?? throw new ArgumentNullException(nameof(registryType));
+ }
+
+ public Type RegistryType { get; }
+ }
+}
+
diff --git a/src/AspectCore.Abstractions/DynamicProxy/IAspectConfigurationAccessor.cs b/src/AspectCore.Abstractions/DynamicProxy/IAspectConfigurationAccessor.cs
new file mode 100644
index 00000000..350c429d
--- /dev/null
+++ b/src/AspectCore.Abstractions/DynamicProxy/IAspectConfigurationAccessor.cs
@@ -0,0 +1,14 @@
+using AspectCore.Configuration;
+
+namespace AspectCore.DynamicProxy
+{
+ ///
+ /// 为 Source Generator 代理提供访问 IAspectConfiguration 的能力,用于运行时构建 IAspectValidator,
+ /// 从而对齐 DynamicProxy "是否需要拦截" 的决策(避免无拦截时仍走 AspectActivator 导致异常包装等语义变化)。
+ ///
+ public interface IAspectConfigurationAccessor
+ {
+ IAspectConfiguration AspectConfiguration { get; }
+ }
+}
+
diff --git a/src/AspectCore.Abstractions/DynamicProxy/ISourceGeneratedProxyRegistry.cs b/src/AspectCore.Abstractions/DynamicProxy/ISourceGeneratedProxyRegistry.cs
new file mode 100644
index 00000000..39ecf15a
--- /dev/null
+++ b/src/AspectCore.Abstractions/DynamicProxy/ISourceGeneratedProxyRegistry.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace AspectCore.DynamicProxy
+{
+ ///
+ /// 由 Source Generator 生成的 registry,用于从 (serviceType, implType, kind) 查找 proxy Type。
+ ///
+ public interface ISourceGeneratedProxyRegistry
+ {
+ ///
+ /// implementationType 在 interface proxy 无 target 场景可传 null。
+ ///
+ bool TryGetProxyType(Type serviceType, Type implementationType, SourceGeneratedProxyKind kind, out Type proxyType);
+ }
+}
diff --git a/src/AspectCore.Abstractions/DynamicProxy/ProxyEngine.cs b/src/AspectCore.Abstractions/DynamicProxy/ProxyEngine.cs
new file mode 100644
index 00000000..9c9ab3a6
--- /dev/null
+++ b/src/AspectCore.Abstractions/DynamicProxy/ProxyEngine.cs
@@ -0,0 +1,24 @@
+namespace AspectCore.DynamicProxy
+{
+ ///
+ /// AOP 后端引擎选择。
+ ///
+ public enum ProxyEngine
+ {
+ ///
+ /// 运行时 DynamicProxy Emit(默认)。
+ ///
+ DynamicProxy = 0,
+
+ ///
+ /// 编译期 Source Generator 生成的代理(需要生成物 registry)。
+ ///
+ SourceGenerator = 1,
+
+ ///
+ /// 优先 Source Generator,缺失时可按 AllowRuntimeFallback 策略回退 DynamicProxy。
+ ///
+ Auto = 2,
+ }
+}
+
diff --git a/src/AspectCore.Abstractions/DynamicProxy/ProxyEngineOptions.cs b/src/AspectCore.Abstractions/DynamicProxy/ProxyEngineOptions.cs
new file mode 100644
index 00000000..148ccd7d
--- /dev/null
+++ b/src/AspectCore.Abstractions/DynamicProxy/ProxyEngineOptions.cs
@@ -0,0 +1,27 @@
+namespace AspectCore.DynamicProxy
+{
+ ///
+ /// 控制 AOP 后端引擎(DynamicProxy / Source Generator)选择与回退策略。
+ ///
+ public sealed class ProxyEngineOptions
+ {
+ ///
+ /// 默认 DynamicProxy,保持既有行为。
+ ///
+ public ProxyEngine Engine { get; set; } = ProxyEngine.DynamicProxy;
+
+ ///
+ /// 是否允许在缺失生成物时回退到运行时 DynamicProxy。
+ ///
+ /// - Engine=Auto:默认 true
+ /// - Engine=SourceGenerator:默认 false
+ ///
+ public bool? AllowRuntimeFallback { get; set; }
+
+ ///
+ /// 为 true 时,缺失生成物将抛出异常(用于 CI 强约束覆盖率)。
+ ///
+ public bool Strict { get; set; }
+ }
+}
+
diff --git a/src/AspectCore.Abstractions/DynamicProxy/SourceGeneratedProxyKind.cs b/src/AspectCore.Abstractions/DynamicProxy/SourceGeneratedProxyKind.cs
new file mode 100644
index 00000000..fe720daa
--- /dev/null
+++ b/src/AspectCore.Abstractions/DynamicProxy/SourceGeneratedProxyKind.cs
@@ -0,0 +1,12 @@
+namespace AspectCore.DynamicProxy
+{
+ ///
+ /// Source Generator 生成的代理种类。
+ ///
+ public enum SourceGeneratedProxyKind
+ {
+ Interface = 0,
+ Class = 1,
+ }
+}
+
diff --git a/src/AspectCore.Core/DependencyInjection/ServiceResolver.cs b/src/AspectCore.Core/DependencyInjection/ServiceResolver.cs
index 29921196..744a8958 100644
--- a/src/AspectCore.Core/DependencyInjection/ServiceResolver.cs
+++ b/src/AspectCore.Core/DependencyInjection/ServiceResolver.cs
@@ -17,7 +17,7 @@ internal sealed class ServiceResolver : IServiceResolver,IServiceResolveCallback
public ServiceResolver(IServiceContext serviceContext)
{
- _serviceTable = new ServiceTable(serviceContext.Configuration);
+ _serviceTable = new ServiceTable(serviceContext);
_serviceTable.Populate(serviceContext);
_resolvedScopedServices = new ConcurrentDictionary();
_resolvedSingletonServices = new ConcurrentDictionary();
@@ -117,4 +117,4 @@ public void Dispose()
}
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/src/AspectCore.Core/DependencyInjection/ServiceTable.cs b/src/AspectCore.Core/DependencyInjection/ServiceTable.cs
index 15f4d70b..2dd8e9dc 100644
--- a/src/AspectCore.Core/DependencyInjection/ServiceTable.cs
+++ b/src/AspectCore.Core/DependencyInjection/ServiceTable.cs
@@ -15,15 +15,53 @@ internal class ServiceTable
private readonly IProxyTypeGenerator _proxyTypeGenerator;
private readonly ServiceValidator _serviceValidator;
- public ServiceTable(IAspectConfiguration configuration)
+ public ServiceTable(IServiceContext serviceContext)
{
- var aspectValidatorBuilder = new AspectValidatorBuilder(configuration);
- _proxyTypeGenerator = new ProxyTypeGenerator(aspectValidatorBuilder);
+ if (serviceContext == null)
+ {
+ throw new ArgumentNullException(nameof(serviceContext));
+ }
+
+ var aspectValidatorBuilder = new AspectValidatorBuilder(serviceContext.Configuration);
+ _proxyTypeGenerator = CreateProxyTypeGenerator(serviceContext, aspectValidatorBuilder);
_serviceValidator = new ServiceValidator(aspectValidatorBuilder);
_linkedServiceDefinitions = new ConcurrentDictionary>();
_linkedGenericServiceDefinitions = new ConcurrentDictionary>();
}
+ private static IProxyTypeGenerator CreateProxyTypeGenerator(IServiceContext serviceContext, IAspectValidatorBuilder aspectValidatorBuilder)
+ {
+ // 1) 显式实例注册优先
+ var explicitGenerator = serviceContext
+ .OfType()
+ .FirstOrDefault(x => x.ServiceType == typeof(IProxyTypeGenerator))
+ ?.ImplementationInstance as IProxyTypeGenerator;
+ if (explicitGenerator != null)
+ {
+ return explicitGenerator;
+ }
+
+ // 2) 按 ProxyEngineOptions 选择(若未配置则保持默认 DynamicProxy)
+ var options = serviceContext
+ .OfType()
+ .FirstOrDefault(x => x.ServiceType == typeof(ProxyEngineOptions))
+ ?.ImplementationInstance as ProxyEngineOptions;
+
+ if (options != null && options.Engine != ProxyEngine.DynamicProxy)
+ {
+ var registries = serviceContext
+ .OfType()
+ .Where(x => x.ServiceType == typeof(ISourceGeneratedProxyRegistry))
+ .Select(x => x.ImplementationInstance)
+ .OfType()
+ .ToArray();
+
+ return new SourceGeneratedProxyTypeGenerator(aspectValidatorBuilder, options, registries);
+ }
+
+ return new ProxyTypeGenerator(aspectValidatorBuilder);
+ }
+
internal void Populate(IEnumerable services)
{
Func, IEnumerable> filter = input => input.Where(x => !x.IsManyEnumerable());
@@ -197,4 +235,4 @@ private ServiceDefinition MakProxyService(ServiceDefinition service)
return service;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/AspectCore.Core/DynamicProxy/AspectActivatorFactory.cs b/src/AspectCore.Core/DynamicProxy/AspectActivatorFactory.cs
index c6ab13c9..2128bae4 100644
--- a/src/AspectCore.Core/DynamicProxy/AspectActivatorFactory.cs
+++ b/src/AspectCore.Core/DynamicProxy/AspectActivatorFactory.cs
@@ -4,7 +4,7 @@
namespace AspectCore.DynamicProxy
{
[NonAspect]
- public sealed class AspectActivatorFactory : IAspectActivatorFactory
+ public sealed class AspectActivatorFactory : IAspectActivatorFactory, IAspectConfigurationAccessor
{
private readonly IAspectContextFactory _aspectContextFactory;
private readonly IAspectBuilderFactory _aspectBuilderFactory;
@@ -21,5 +21,7 @@ public IAspectActivator Create()
{
return new AspectActivator(_aspectContextFactory, _aspectBuilderFactory, _aspectConfiguration);
}
+
+ public IAspectConfiguration AspectConfiguration => _aspectConfiguration;
}
-}
\ No newline at end of file
+}
diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitorContext.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitorContext.cs
index bedc7d6e..63a6b647 100644
--- a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitorContext.cs
+++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitorContext.cs
@@ -45,15 +45,18 @@ public MethodConstantTable(TypeBuilder typeBuilder)
public void AddMethod(string name, MethodInfo method)
{
- if (!_fields.ContainsKey(name))
+ if (!_fields.TryGetValue(name, out var field))
{
- var field = _nestedTypeBuilder.DefineField(name, typeof(MethodInfo), FieldAttributes.Static | FieldAttributes.InitOnly | FieldAttributes.Assembly);
+ field = _nestedTypeBuilder.DefineField(name, typeof(MethodInfo), FieldAttributes.Static | FieldAttributes.InitOnly | FieldAttributes.Assembly);
_fields.Add(name, field);
- if (method != null)
- {
- _ilGen.EmitMethod(method);
- _ilGen.Emit(OpCodes.Stsfld, field);
- }
+ }
+
+ // Allow late-binding updates: proxy MethodBuilder is only available during body emit.
+ // It's safe to assign readonly static fields multiple times in the type initializer (last write wins).
+ if (method != null)
+ {
+ _ilGen.EmitMethod(method);
+ _ilGen.Emit(OpCodes.Stsfld, field);
}
}
diff --git a/src/AspectCore.Core/DynamicProxy/SourceGeneratedProxyTypeGenerator.cs b/src/AspectCore.Core/DynamicProxy/SourceGeneratedProxyTypeGenerator.cs
new file mode 100644
index 00000000..79c8b50f
--- /dev/null
+++ b/src/AspectCore.Core/DynamicProxy/SourceGeneratedProxyTypeGenerator.cs
@@ -0,0 +1,252 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace AspectCore.DynamicProxy
+{
+ ///
+ /// 基于 Source Generator 生成物的 proxy type generator。
+ ///
+ /// - 支持 registry discovery(扫描已加载程序集的 )。
+ /// - 支持手动注册 registry(通过 DI 注册 )。
+ /// - 支持引擎选择与回退策略()。
+ ///
+ [NonAspect]
+ public sealed class SourceGeneratedProxyTypeGenerator : IProxyTypeGenerator
+ {
+ private readonly ProxyEngineOptions _options;
+ private readonly IProxyTypeGenerator _dynamicProxy;
+ private readonly IReadOnlyList _manualRegistries;
+
+ private volatile bool _scanned;
+ private readonly object _scanLock = new object();
+ private IReadOnlyList _scannedRegistries = Array.Empty();
+
+ private readonly ConcurrentDictionary _cache = new ConcurrentDictionary();
+
+ public SourceGeneratedProxyTypeGenerator(
+ IAspectValidatorBuilder aspectValidatorBuilder,
+ ProxyEngineOptions options,
+ IEnumerable registries = null)
+ {
+ if (aspectValidatorBuilder == null) throw new ArgumentNullException(nameof(aspectValidatorBuilder));
+ _dynamicProxy = new ProxyTypeGenerator(aspectValidatorBuilder);
+ _options = options ?? throw new ArgumentNullException(nameof(options));
+ _manualRegistries = (registries ?? Array.Empty()).Where(r => r != null).ToArray();
+ }
+
+ public Type CreateInterfaceProxyType(Type serviceType)
+ => CreateInterfaceProxyTypeCore(serviceType, implementationType: null);
+
+ public Type CreateInterfaceProxyType(Type serviceType, Type implementationType)
+ => CreateInterfaceProxyTypeCore(serviceType, implementationType);
+
+ public Type CreateClassProxyType(Type serviceType, Type implementationType)
+ {
+ if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
+ if (implementationType == null) throw new ArgumentNullException(nameof(implementationType));
+
+ return CreateCore(
+ kind: SourceGeneratedProxyKind.Class,
+ serviceType: serviceType,
+ implementationType: implementationType,
+ dynamicFallback: () => _dynamicProxy.CreateClassProxyType(serviceType, implementationType));
+ }
+
+ private Type CreateInterfaceProxyTypeCore(Type serviceType, Type implementationType)
+ {
+ if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
+
+ return CreateCore(
+ kind: SourceGeneratedProxyKind.Interface,
+ serviceType: serviceType,
+ implementationType: implementationType,
+ dynamicFallback: () => implementationType == null
+ ? _dynamicProxy.CreateInterfaceProxyType(serviceType)
+ : _dynamicProxy.CreateInterfaceProxyType(serviceType, implementationType));
+ }
+
+ private Type CreateCore(SourceGeneratedProxyKind kind, Type serviceType, Type implementationType, Func dynamicFallback)
+ {
+ var engine = _options.Engine;
+ if (engine == ProxyEngine.DynamicProxy)
+ {
+ return dynamicFallback();
+ }
+
+ // Interface proxies generated by the current SG node are implementation-type agnostic.
+ // The registry entry uses implementationType == null. Keep the original implementationType
+ // only for diagnostics and runtime fallback; use null for lookup + caching.
+ var lookupImplType = kind == SourceGeneratedProxyKind.Interface ? null : implementationType;
+
+ var key = new CacheKey(serviceType, lookupImplType, kind);
+ if (_cache.TryGetValue(key, out var cached))
+ {
+ return cached;
+ }
+
+ if (TryResolveFromRegistries(serviceType, lookupImplType, kind, out var proxyType))
+ {
+ _cache[key] = proxyType;
+ return proxyType;
+ }
+
+ // miss
+ var allowFallback = GetAllowRuntimeFallback(engine);
+ var mustFail = _options.Strict || !allowFallback || engine == ProxyEngine.SourceGenerator;
+ if (mustFail)
+ {
+ throw CreateMissingProxyException(serviceType, implementationType, kind, engine, allowFallback);
+ }
+
+ return dynamicFallback();
+ }
+
+ private bool GetAllowRuntimeFallback(ProxyEngine engine)
+ {
+ if (_options.AllowRuntimeFallback.HasValue)
+ {
+ return _options.AllowRuntimeFallback.Value;
+ }
+ return engine == ProxyEngine.Auto;
+ }
+
+ private bool TryResolveFromRegistries(Type serviceType, Type implementationType, SourceGeneratedProxyKind kind, out Type proxyType)
+ {
+ // manual registries first
+ foreach (var r in _manualRegistries)
+ {
+ if (r.TryGetProxyType(serviceType, implementationType, kind, out proxyType))
+ {
+ return true;
+ }
+ }
+
+ EnsureScannedRegistries();
+ foreach (var r in _scannedRegistries)
+ {
+ if (r.TryGetProxyType(serviceType, implementationType, kind, out proxyType))
+ {
+ return true;
+ }
+ }
+
+ proxyType = null;
+ return false;
+ }
+
+ private void EnsureScannedRegistries()
+ {
+ if (_scanned) return;
+ lock (_scanLock)
+ {
+ if (_scanned) return;
+ _scannedRegistries = ScanRegistries();
+ _scanned = true;
+ }
+ }
+
+ private static IReadOnlyList ScanRegistries()
+ {
+ var registries = new List();
+ Assembly[] assemblies;
+ try
+ {
+ assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ }
+ catch
+ {
+ return registries;
+ }
+
+ foreach (var asm in assemblies)
+ {
+ try
+ {
+ var attrs = asm.GetCustomAttributes(typeof(AspectCoreSourceGeneratedProxyRegistryAttribute), inherit: false)
+ .Cast();
+ foreach (var attr in attrs)
+ {
+ var t = attr.RegistryType;
+ if (t == null) continue;
+ if (!typeof(ISourceGeneratedProxyRegistry).GetTypeInfo().IsAssignableFrom(t.GetTypeInfo()))
+ {
+ continue;
+ }
+ if (t.GetTypeInfo().DeclaredConstructors.All(c => c.IsStatic || c.GetParameters().Length != 0))
+ {
+ // require parameterless ctor for scan-instantiation
+ continue;
+ }
+ if (Activator.CreateInstance(t) is ISourceGeneratedProxyRegistry registry)
+ {
+ registries.Add(registry);
+ }
+ }
+ }
+ catch
+ {
+ // ignore assemblies that cannot be reflected (dynamic/AOT/partial load)
+ }
+ }
+
+ return registries;
+ }
+
+ private InvalidOperationException CreateMissingProxyException(Type serviceType, Type implementationType, SourceGeneratedProxyKind kind, ProxyEngine engine, bool allowFallback)
+ {
+ var manualCount = _manualRegistries.Count;
+ EnsureScannedRegistries();
+ var scannedCount = _scannedRegistries.Count;
+
+ var implText = implementationType == null ? "" : implementationType.FullName;
+ var msg =
+ "Failed to resolve source-generated proxy type.\n" +
+ $" Engine: {engine}\n" +
+ $" Strict: {_options.Strict}\n" +
+ $" AllowRuntimeFallback: {allowFallback}\n" +
+ $" Kind: {kind}\n" +
+ $" ServiceType: {serviceType.FullName}\n" +
+ $" ImplementationType: {implText}\n" +
+ $" ManualRegistries: {manualCount}\n" +
+ $" ScannedRegistries: {scannedCount}\n" +
+ "Hint: Ensure your project references AspectCore.SourceGenerator and has [AspectCoreGenerateProxy] triggers, " +
+ "and ensure the generated registry is discoverable (assembly attribute) or manually registered via DI.";
+
+ return new InvalidOperationException(msg);
+ }
+
+ private readonly struct CacheKey : IEquatable
+ {
+ private readonly Type _serviceType;
+ private readonly Type _implType;
+ private readonly SourceGeneratedProxyKind _kind;
+
+ public CacheKey(Type serviceType, Type implType, SourceGeneratedProxyKind kind)
+ {
+ _serviceType = serviceType;
+ _implType = implType;
+ _kind = kind;
+ }
+
+ public bool Equals(CacheKey other)
+ => _serviceType == other._serviceType && _implType == other._implType && _kind == other._kind;
+
+ public override bool Equals(object obj)
+ => obj is CacheKey other && Equals(other);
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hash = _serviceType.GetHashCode();
+ hash = (hash * 397) ^ (_implType?.GetHashCode() ?? 0);
+ hash = (hash * 397) ^ (int)_kind;
+ return hash;
+ }
+ }
+ }
+ }
+}
diff --git a/src/AspectCore.Extensions.Autofac/ContainerBuilderExtensions.cs b/src/AspectCore.Extensions.Autofac/ContainerBuilderExtensions.cs
index ab93d86a..987d5d26 100644
--- a/src/AspectCore.Extensions.Autofac/ContainerBuilderExtensions.cs
+++ b/src/AspectCore.Extensions.Autofac/ContainerBuilderExtensions.cs
@@ -67,6 +67,25 @@ public static ContainerBuilder RegisterDynamicProxy(this ContainerBuilder contai
return containerBuilder;
}
+
+ ///
+ /// 显式 opt-in 配置 AOP 后端引擎(DynamicProxy/SourceGenerator/Auto)。
+ /// 调用后会用 替换 注册,
+ /// 但仍可按 options 回退 DynamicProxy。
+ ///
+ public static ContainerBuilder ConfigureDynamicProxyEngine(this ContainerBuilder containerBuilder, Action configure)
+ {
+ if (containerBuilder == null)
+ {
+ throw new ArgumentNullException(nameof(containerBuilder));
+ }
+ var options = new ProxyEngineOptions();
+ configure?.Invoke(options);
+
+ containerBuilder.RegisterInstance(options).SingleInstance();
+ containerBuilder.RegisterType().As().SingleInstance();
+ return containerBuilder;
+ }
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionExtensions.cs
index 5c8e5d8f..ae0656f7 100644
--- a/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionExtensions.cs
+++ b/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionExtensions.cs
@@ -36,6 +36,48 @@ public static IServiceCollection ConfigureDynamicProxy(this IServiceCollection s
return services;
}
+ ///
+ /// 配置 AOP 后端引擎(DynamicProxy / SourceGenerator / Auto)。
+ ///
+ /// 该 API 为显式 opt-in:不影响既有 ConfigureDynamicProxy(...) 的语义,
+ /// 且在未调用时默认行为保持 DynamicProxy。
+ ///
+ public static IServiceCollection ConfigureDynamicProxyEngine(this IServiceCollection services, Action configure)
+ {
+ if (services == null)
+ {
+ throw new ArgumentNullException(nameof(services));
+ }
+
+ var optionsService = services.LastOrDefault(x => x.ServiceType == typeof(ProxyEngineOptions) && x.ImplementationInstance != null);
+ var options = (ProxyEngineOptions)optionsService?.ImplementationInstance ?? new ProxyEngineOptions();
+ configure?.Invoke(options);
+
+ if (optionsService == null)
+ {
+ services.AddSingleton(options);
+ }
+
+ // opt-in 后切换 IProxyTypeGenerator,但仍可按 options 回退 DynamicProxy
+ services.Replace(ServiceDescriptor.Singleton());
+
+ return services;
+ }
+
+ ///
+ /// 手动注册生成的 registry(用于 AOT/trim 场景避免 assembly 扫描不可用)。
+ ///
+ public static IServiceCollection AddSourceGeneratedProxyRegistry(this IServiceCollection services)
+ where TRegistry : class, ISourceGeneratedProxyRegistry
+ {
+ if (services == null)
+ {
+ throw new ArgumentNullException(nameof(services));
+ }
+ services.AddSingleton();
+ return services;
+ }
+
internal static IServiceCollection TryAddDynamicProxyServices(this IServiceCollection services)
{
if (services == null)
@@ -67,4 +109,4 @@ internal static IServiceCollection TryAddDynamicProxyServices(this IServiceColle
return services;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/AspectCore.Extensions.LightInject/ContainerBuilderExtensions.cs b/src/AspectCore.Extensions.LightInject/ContainerBuilderExtensions.cs
index 69a84704..dffb5df6 100644
--- a/src/AspectCore.Extensions.LightInject/ContainerBuilderExtensions.cs
+++ b/src/AspectCore.Extensions.LightInject/ContainerBuilderExtensions.cs
@@ -78,6 +78,25 @@ public static IServiceContainer RegisterDynamicProxy(this IServiceContainer cont
return container;
}
+
+ ///
+ /// 显式 opt-in 配置 AOP 后端引擎(DynamicProxy/SourceGenerator/Auto)。
+ /// 调用后会用 替换 注册。
+ ///
+ public static IServiceContainer ConfigureDynamicProxyEngine(this IServiceContainer container, Action configure)
+ {
+ if (container == null)
+ {
+ throw new ArgumentNullException(nameof(container));
+ }
+
+ var options = new ProxyEngineOptions();
+ configure?.Invoke(options);
+
+ container.AddSingleton(options);
+ container.AddSingleton();
+ return container;
+ }
private static Type GetImplType(this ServiceRegistration registration)
{
diff --git a/src/AspectCore.SourceGenerator/AspectCore.SourceGenerator.csproj b/src/AspectCore.SourceGenerator/AspectCore.SourceGenerator.csproj
new file mode 100644
index 00000000..aba387fe
--- /dev/null
+++ b/src/AspectCore.SourceGenerator/AspectCore.SourceGenerator.csproj
@@ -0,0 +1,29 @@
+
+
+
+ netstandard2.0
+ latest
+ enable
+ true
+
+
+ true
+ false
+ Analyzer
+ false
+
+ AspectCore.SourceGenerator
+ AspectCore.SourceGenerator
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AspectCore.SourceGenerator/AspectCoreProxyGenerator.cs b/src/AspectCore.SourceGenerator/AspectCoreProxyGenerator.cs
new file mode 100644
index 00000000..e4d80d39
--- /dev/null
+++ b/src/AspectCore.SourceGenerator/AspectCoreProxyGenerator.cs
@@ -0,0 +1,296 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace AspectCore.SourceGenerator;
+
+[Generator(LanguageNames.CSharp)]
+public sealed class AspectCoreProxyGenerator : IIncrementalGenerator
+{
+ internal const string GenerateProxyAttributeMetadataName = "AspectCore.DynamicProxy.AspectCoreGenerateProxyAttribute";
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ var candidateTypes = context.SyntaxProvider
+ .CreateSyntaxProvider(
+ static (node, _) => node is TypeDeclarationSyntax tds && tds.AttributeLists.Count > 0,
+ static (ctx, _) => GetCandidate(ctx))
+ .Where(static x => x is not null)
+ .Select(static (x, _) => x!);
+
+ context.RegisterSourceOutput(context.CompilationProvider.Combine(candidateTypes.Collect()), static (spc, input) =>
+ {
+ var (compilation, candidates) = input;
+ Execute(spc, compilation, candidates);
+ });
+ }
+
+ private static INamedTypeSymbol? GetCandidate(GeneratorSyntaxContext ctx)
+ {
+ if (ctx.Node is not TypeDeclarationSyntax tds)
+ {
+ return null;
+ }
+
+ var symbol = ctx.SemanticModel.GetDeclaredSymbol(tds) as INamedTypeSymbol;
+ if (symbol is null)
+ {
+ return null;
+ }
+
+ foreach (var attr in symbol.GetAttributes())
+ {
+ var attrClass = attr.AttributeClass;
+ if (attrClass is null) continue;
+ if (attrClass.ToDisplayString() == GenerateProxyAttributeMetadataName)
+ {
+ return symbol;
+ }
+ }
+
+ return null;
+ }
+
+ private static void Execute(SourceProductionContext context, Compilation compilation, ImmutableArray candidates)
+ {
+ // NOTE: 本节点先实现 Attribute 触发:只处理 type-level [AspectCoreGenerateProxy]。
+ // - class: 默认生成 class proxy (serviceType=implType=该类)
+ // - interface: 默认生成 interface proxy(生成两种 ctor:无 target / 带 target)
+ // assembly-level mapping / 带参数 mapping 在后续节点补齐。
+
+ var attrSymbol = compilation.GetTypeByMetadataName(GenerateProxyAttributeMetadataName);
+ if (attrSymbol is null)
+ {
+ // 用户未引用包含 Attribute 的 runtime 包,直接不输出。
+ return;
+ }
+
+ var entries = new List();
+ foreach (var type in candidates.Distinct(NamedTypeSymbolEqualityComparer.Instance))
+ {
+ var attrData = type.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol));
+ if (attrData is null)
+ {
+ continue;
+ }
+
+ if (type.IsGenericType)
+ {
+ context.ReportDiagnostic(GeneratorDiagnostics.UnsupportedGenericType(type));
+ continue;
+ }
+
+ if (type.ContainingType is not null)
+ {
+ context.ReportDiagnostic(GeneratorDiagnostics.UnsupportedNestedType(type));
+ continue;
+ }
+
+ // P1-1: 检查 sealed 类型(对于 class proxy)
+ if (type.TypeKind == TypeKind.Class && type.IsSealed && !type.IsAbstract)
+ {
+ context.ReportDiagnostic(GeneratorDiagnostics.SealedType(type));
+ continue;
+ }
+
+ // P1-2: 检查类型可见性
+ if (!IsTypeAccessible(type, compilation))
+ {
+ context.ReportDiagnostic(GeneratorDiagnostics.TypeNotAccessible(type));
+ continue;
+ }
+
+ // 从 attribute 中读取实现类型
+ INamedTypeSymbol? implementationType = null;
+ foreach (var namedArg in attrData.NamedArguments)
+ {
+ if (namedArg.Key == "ImplementationType" && namedArg.Value.Value is INamedTypeSymbol implType)
+ {
+ implementationType = implType;
+ break;
+ }
+ }
+
+ // 从构造函数参数中读取实现类型
+ if (implementationType is null && attrData.ConstructorArguments.Length > 0)
+ {
+ // 构造函数参数顺序:serviceType, implementationType, kind
+ // 或者:implementationType (单参数构造函数)
+ foreach (var arg in attrData.ConstructorArguments)
+ {
+ if (arg.Value is INamedTypeSymbol implType)
+ {
+ // 检查这个类型是否是 implementationType(不是 serviceType)
+ // 通过检查 attribute 构造函数的参数顺序来确定
+ if (attrData.ConstructorArguments.Length == 1)
+ {
+ // 单参数构造函数:implementationType
+ implementationType = implType;
+ }
+ else if (attrData.ConstructorArguments.Length >= 2)
+ {
+ // 多参数构造函数:第二个参数是 implementationType
+ var secondArg = attrData.ConstructorArguments[1];
+ if (secondArg.Value is INamedTypeSymbol implType2)
+ {
+ implementationType = implType2;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // 验证实现类型的可见性
+ if (implementationType is not null && !IsTypeAccessible(implementationType, compilation))
+ {
+ context.ReportDiagnostic(GeneratorDiagnostics.TypeNotAccessible(implementationType));
+ continue;
+ }
+
+ switch (type.TypeKind)
+ {
+ case TypeKind.Interface:
+ entries.Add(ProxyEntry.CreateInterface(serviceType: type, implementationType));
+ break;
+ case TypeKind.Class:
+ // P1-3: 检查构造函数可访问性
+ if (!HasAccessibleConstructor(type))
+ {
+ context.ReportDiagnostic(GeneratorDiagnostics.NoAccessibleConstructor(type));
+ continue;
+ }
+ entries.Add(ProxyEntry.CreateClass(serviceType: type, implementationType: type));
+ break;
+ }
+ }
+
+ if (entries.Count == 0)
+ {
+ return;
+ }
+
+ foreach (var entry in entries)
+ {
+ var src = entry.Kind switch
+ {
+ ProxyKind.Interface => ProxyEmitter.EmitInterfaceProxy(compilation, entry, context),
+ ProxyKind.Class => ProxyEmitter.EmitClassProxy(compilation, entry, context),
+ _ => null
+ };
+
+ if (src is not null)
+ {
+ context.AddSource($"{entry.ProxyTypeName}.g.cs", src);
+ }
+ }
+
+ context.AddSource("AspectCoreSourceGeneratedProxyRegistry.g.cs", RegistryEmitter.EmitRegistry(entries));
+ }
+
+ ///
+ /// 检查类型是否对生成器可见(考虑 internal 和 InternalsVisibleTo)
+ ///
+ private static bool IsTypeAccessible(INamedTypeSymbol type, Compilation compilation)
+ {
+ // Public 类型总是可见
+ if (type.DeclaredAccessibility == Accessibility.Public)
+ {
+ // 对于嵌套类型,需要检查所有包含类型的可见性
+ if (type.ContainingType is not null)
+ {
+ return IsTypeAccessible(type.ContainingType, compilation);
+ }
+ return true;
+ }
+
+ // Internal 类型:检查是否有 InternalsVisibleTo
+ if (type.DeclaredAccessibility == Accessibility.Internal)
+ {
+ // 检查生成器所在的程序集是否有权限访问
+ // Source Generator 在编译期间运行,与目标程序集在同一个编译上下文中
+ // 因此 internal 类型应该是可见的
+ return true;
+ }
+
+ // Protected、Private 等类型不可见
+ if (type.DeclaredAccessibility is Accessibility.Protected or Accessibility.ProtectedOrInternal)
+ {
+ // Protected 类型只有在继承场景下可见,对于代理生成通常不可见
+ // 但如果类型在同一个程序集中,可能是可见的
+ return true; // 简化处理,假设在同一个程序集中可见
+ }
+
+ return false;
+ }
+
+ ///
+ /// 检查类型是否有可访问的构造函数(用于 class proxy)
+ ///
+ private static bool HasAccessibleConstructor(INamedTypeSymbol type)
+ {
+ if (type.InstanceConstructors.Length == 0)
+ {
+ return false;
+ }
+
+ // 检查是否有 public 或 protected 构造函数
+ foreach (var ctor in type.InstanceConstructors)
+ {
+ if (ctor.DeclaredAccessibility is Accessibility.Public or Accessibility.Protected or Accessibility.ProtectedOrInternal)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
+
+internal sealed class NamedTypeSymbolEqualityComparer : IEqualityComparer
+{
+ public static readonly NamedTypeSymbolEqualityComparer Instance = new();
+
+ public bool Equals(INamedTypeSymbol? x, INamedTypeSymbol? y)
+ => SymbolEqualityComparer.Default.Equals(x, y);
+
+ public int GetHashCode(INamedTypeSymbol obj)
+ => SymbolEqualityComparer.Default.GetHashCode(obj);
+}
+
+internal enum ProxyKind
+{
+ Interface = 0,
+ Class = 1,
+}
+
+internal sealed class ProxyEntry
+{
+ public ProxyEntry(INamedTypeSymbol serviceType, INamedTypeSymbol? implementationType, ProxyKind kind, string proxyTypeName, string proxyNamespace)
+ {
+ ServiceType = serviceType;
+ ImplementationType = implementationType;
+ Kind = kind;
+ ProxyTypeName = proxyTypeName;
+ ProxyNamespace = proxyNamespace;
+ }
+
+ public INamedTypeSymbol ServiceType { get; }
+ public INamedTypeSymbol? ImplementationType { get; }
+ public ProxyKind Kind { get; }
+ public string ProxyTypeName { get; }
+ public string ProxyNamespace { get; }
+
+ public static ProxyEntry CreateInterface(INamedTypeSymbol serviceType, INamedTypeSymbol? implementationType)
+ => new(serviceType, implementationType, kind: ProxyKind.Interface,
+ proxyTypeName: Naming.GetProxyTypeName(serviceType, implementationType, ProxyKind.Interface),
+ proxyNamespace: Naming.GeneratedProxyNamespace);
+
+ public static ProxyEntry CreateClass(INamedTypeSymbol serviceType, INamedTypeSymbol implementationType)
+ => new(serviceType, implementationType, ProxyKind.Class,
+ Naming.GetProxyTypeName(serviceType, implementationType, ProxyKind.Class),
+ Naming.GeneratedProxyNamespace);
+}
diff --git a/src/AspectCore.SourceGenerator/Emit/GeneratorDiagnostics.cs b/src/AspectCore.SourceGenerator/Emit/GeneratorDiagnostics.cs
new file mode 100644
index 00000000..d4c088e5
--- /dev/null
+++ b/src/AspectCore.SourceGenerator/Emit/GeneratorDiagnostics.cs
@@ -0,0 +1,85 @@
+using System.Linq;
+using Microsoft.CodeAnalysis;
+
+namespace AspectCore.SourceGenerator;
+
+internal static class GeneratorDiagnostics
+{
+ private static readonly DiagnosticDescriptor UnsupportedGenericTypeDescriptor = new(
+ id: "ACSG001",
+ title: "AspectCore SourceGenerator 暂不支持开放泛型类型",
+ messageFormat: "类型 '{0}' 为开放泛型,当前版本的 Source Generator 暂不支持生成代理。",
+ category: "AspectCore.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ private static readonly DiagnosticDescriptor UnsupportedNestedTypeDescriptor = new(
+ id: "ACSG002",
+ title: "AspectCore SourceGenerator 暂不支持嵌套类型",
+ messageFormat: "类型 '{0}' 为嵌套类型,当前版本的 Source Generator 暂不支持生成代理。",
+ category: "AspectCore.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ private static readonly DiagnosticDescriptor UnsupportedEventDescriptor = new(
+ id: "ACSG003",
+ title: "AspectCore SourceGenerator 暂不支持事件成员",
+ messageFormat: "类型 '{0}' 包含事件成员 '{1}',当前版本的 Source Generator 暂不支持生成代理。",
+ category: "AspectCore.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ private static readonly DiagnosticDescriptor UnsupportedGenericMethodDescriptor = new(
+ id: "ACSG004",
+ title: "AspectCore SourceGenerator 暂不支持开放泛型方法",
+ messageFormat: "类型 '{0}' 包含开放泛型方法 '{1}',当前版本的 Source Generator 暂不支持生成代理。",
+ category: "AspectCore.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ private static readonly DiagnosticDescriptor SealedTypeDescriptor = new(
+ id: "ACSG005",
+ title: "无法为 sealed 类型生成代理",
+ messageFormat: "无法为 sealed 类型 '{0}' 生成代理。请移除 sealed 修饰符或使用接口代理。",
+ category: "AspectCore.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Error,
+ isEnabledByDefault: true);
+
+ private static readonly DiagnosticDescriptor TypeNotAccessibleDescriptor = new(
+ id: "ACSG006",
+ title: "类型对 Source Generator 不可见",
+ messageFormat: "类型 '{0}' 对 Source Generator 不可见。请确保类型具有 public 或 internal 可访问性。",
+ category: "AspectCore.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Error,
+ isEnabledByDefault: true);
+
+ private static readonly DiagnosticDescriptor NoAccessibleConstructorDescriptor = new(
+ id: "ACSG007",
+ title: "类型没有可访问的构造函数",
+ messageFormat: "类型 '{0}' 没有可访问的构造函数。类代理要求目标类型具有 public 或 protected 构造函数。",
+ category: "AspectCore.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Error,
+ isEnabledByDefault: true);
+
+ public static Diagnostic UnsupportedGenericType(INamedTypeSymbol symbol)
+ => Diagnostic.Create(UnsupportedGenericTypeDescriptor, symbol.Locations.FirstOrDefault(), symbol.ToDisplayString());
+
+ public static Diagnostic UnsupportedNestedType(INamedTypeSymbol symbol)
+ => Diagnostic.Create(UnsupportedNestedTypeDescriptor, symbol.Locations.FirstOrDefault(), symbol.ToDisplayString());
+
+ public static Diagnostic UnsupportedEvent(INamedTypeSymbol type, IEventSymbol ev)
+ => Diagnostic.Create(UnsupportedEventDescriptor, ev.Locations.FirstOrDefault() ?? type.Locations.FirstOrDefault(), type.ToDisplayString(), ev.Name);
+
+ public static Diagnostic UnsupportedGenericMethod(INamedTypeSymbol type, IMethodSymbol method)
+ => Diagnostic.Create(UnsupportedGenericMethodDescriptor, method.Locations.FirstOrDefault() ?? type.Locations.FirstOrDefault(), type.ToDisplayString(), method.ToDisplayString());
+
+ public static Diagnostic SealedType(INamedTypeSymbol symbol)
+ => Diagnostic.Create(SealedTypeDescriptor, symbol.Locations.FirstOrDefault(), symbol.ToDisplayString());
+
+ public static Diagnostic TypeNotAccessible(INamedTypeSymbol symbol)
+ => Diagnostic.Create(TypeNotAccessibleDescriptor, symbol.Locations.FirstOrDefault(), symbol.ToDisplayString());
+
+ public static Diagnostic NoAccessibleConstructor(INamedTypeSymbol symbol)
+ => Diagnostic.Create(NoAccessibleConstructorDescriptor, symbol.Locations.FirstOrDefault(), symbol.ToDisplayString());
+}
+
diff --git a/src/AspectCore.SourceGenerator/Emit/Naming.cs b/src/AspectCore.SourceGenerator/Emit/Naming.cs
new file mode 100644
index 00000000..4438fcf8
--- /dev/null
+++ b/src/AspectCore.SourceGenerator/Emit/Naming.cs
@@ -0,0 +1,28 @@
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace AspectCore.SourceGenerator;
+
+internal static class Naming
+{
+ public const string GeneratedProxyNamespace = "AspectCore.SourceGenerated.Proxies";
+
+ public static string GetProxyTypeName(INamedTypeSymbol serviceType, INamedTypeSymbol? implType, ProxyKind kind)
+ {
+ // Deterministic + collision-resistant enough for this node.
+ static string Sanitize(string s)
+ {
+ var sb = new StringBuilder(s.Length);
+ foreach (var ch in s)
+ {
+ sb.Append(char.IsLetterOrDigit(ch) ? ch : '_');
+ }
+ return sb.ToString();
+ }
+
+ var serviceId = Sanitize(serviceType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ var implId = implType is null ? "NoTarget" : Sanitize(implType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ return $"{serviceId}__{implId}__{kind}Proxy";
+ }
+}
+
diff --git a/src/AspectCore.SourceGenerator/Emit/ProxyEmitter.cs b/src/AspectCore.SourceGenerator/Emit/ProxyEmitter.cs
new file mode 100644
index 00000000..a1cafcbb
--- /dev/null
+++ b/src/AspectCore.SourceGenerator/Emit/ProxyEmitter.cs
@@ -0,0 +1,838 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace AspectCore.SourceGenerator;
+
+internal static class ProxyEmitter
+{
+ public static string? EmitInterfaceProxy(Compilation compilation, ProxyEntry entry, SourceProductionContext context)
+ {
+ if (entry.ServiceType.TypeKind != TypeKind.Interface)
+ {
+ return null;
+ }
+
+ if (entry.ServiceType.GetMembers().OfType().Any())
+ {
+ var ev = entry.ServiceType.GetMembers().OfType().First();
+ context.ReportDiagnostic(GeneratorDiagnostics.UnsupportedEvent(entry.ServiceType, ev));
+ return null;
+ }
+
+ // Generic methods are supported (proxy method emits generic arity + MakeGenericMethod).
+
+ var sb = new StringBuilder();
+ sb.AppendLine("// ");
+ sb.AppendLine();
+ sb.AppendLine("using System;");
+ sb.AppendLine("using System.Reflection;");
+ sb.AppendLine("using System.Threading.Tasks;");
+ sb.AppendLine();
+ sb.AppendLine($"namespace {entry.ProxyNamespace}");
+ sb.AppendLine("{");
+
+ var ifaceName = entry.ServiceType.ToGlobalName();
+ var proxyName = entry.ProxyTypeName;
+ var implementationType = entry.ImplementationType;
+
+ // stub
+ // NOTE: namespace-level types cannot be private; keep this internal.
+ sb.AppendLine($" internal sealed class {proxyName}__Stub : {ifaceName}");
+ sb.AppendLine(" {");
+ EmitInterfaceStubMembers(sb, entry.ServiceType);
+ sb.AppendLine(" }");
+ sb.AppendLine();
+
+ // proxy
+ sb.AppendLine(" [global::AspectCore.DynamicProxy.NonAspect]");
+ sb.AppendLine(" [global::AspectCore.DynamicProxy.Dynamically]");
+ sb.AppendLine($" public sealed class {proxyName} : {ifaceName}");
+ sb.AppendLine(" {");
+ sb.AppendLine(" private readonly global::AspectCore.DynamicProxy.IAspectActivatorFactory _activatorFactory;");
+ sb.AppendLine($" private readonly {ifaceName} _implementation;");
+ sb.AppendLine(" private global::AspectCore.DynamicProxy.IAspectValidator _validator;");
+ sb.AppendLine();
+
+ // ctors (both required shapes)
+ sb.AppendLine($" public {proxyName}(global::AspectCore.DynamicProxy.IAspectActivatorFactory activatorFactory)");
+ sb.AppendLine(" {");
+ sb.AppendLine(" _activatorFactory = activatorFactory ?? throw new ArgumentNullException(nameof(activatorFactory));");
+ sb.AppendLine($" _implementation = new {proxyName}__Stub();");
+ sb.AppendLine(" }");
+ sb.AppendLine();
+ sb.AppendLine($" public {proxyName}(global::AspectCore.DynamicProxy.IAspectActivatorFactory activatorFactory, {ifaceName} implementation)");
+ sb.AppendLine(" {");
+ sb.AppendLine(" _activatorFactory = activatorFactory ?? throw new ArgumentNullException(nameof(activatorFactory));");
+ sb.AppendLine(" _implementation = implementation; // allow null (matches runtime behavior)");
+ sb.AppendLine(" }");
+ sb.AppendLine();
+
+ EmitValidatorHelpers(sb);
+ sb.AppendLine();
+
+ EmitInterfaceProxyMembers(sb, compilation, entry.ServiceType, implementationType, proxyName, ifaceName);
+
+ sb.AppendLine(" }");
+ sb.AppendLine("}");
+
+ return sb.ToString();
+ }
+
+ public static string? EmitClassProxy(Compilation compilation, ProxyEntry entry, SourceProductionContext context)
+ {
+ if (entry.ServiceType.TypeKind != TypeKind.Class || entry.ImplementationType is null)
+ {
+ return null;
+ }
+
+ if (entry.ServiceType.GetMembers().OfType().Any())
+ {
+ var ev = entry.ServiceType.GetMembers().OfType().First();
+ context.ReportDiagnostic(GeneratorDiagnostics.UnsupportedEvent(entry.ServiceType, ev));
+ return null;
+ }
+
+ // Generic methods are supported (proxy method emits generic arity + MakeGenericMethod).
+
+ var sb = new StringBuilder();
+ sb.AppendLine("// ");
+ sb.AppendLine();
+ sb.AppendLine("using System;");
+ sb.AppendLine("using System.Reflection;");
+ sb.AppendLine("using System.Threading.Tasks;");
+ sb.AppendLine();
+ sb.AppendLine($"namespace {entry.ProxyNamespace}");
+ sb.AppendLine("{");
+
+ var serviceName = entry.ServiceType.ToGlobalName();
+ var implName = entry.ImplementationType.ToGlobalName();
+ var proxyName = entry.ProxyTypeName;
+
+ sb.AppendLine(" [global::AspectCore.DynamicProxy.NonAspect]");
+ sb.AppendLine(" [global::AspectCore.DynamicProxy.Dynamically]");
+ sb.AppendLine($" public sealed class {proxyName} : {implName}");
+ sb.AppendLine(" {");
+ sb.AppendLine(" private readonly global::AspectCore.DynamicProxy.IAspectActivatorFactory _activatorFactory;");
+ sb.AppendLine($" private readonly {serviceName} _implementation;");
+ sb.AppendLine(" private global::AspectCore.DynamicProxy.IAspectValidator _validator;");
+ sb.AppendLine();
+
+ EmitClassConstructors(sb, entry.ImplementationType, proxyName);
+ sb.AppendLine();
+ EmitValidatorHelpers(sb);
+ sb.AppendLine();
+
+ EmitClassProxyMembers(sb, compilation, entry.ServiceType, entry.ImplementationType, proxyName, serviceName);
+
+ sb.AppendLine(" }");
+ sb.AppendLine("}");
+ return sb.ToString();
+ }
+
+ private static void EmitValidatorHelpers(StringBuilder sb)
+ {
+ sb.AppendLine(" private global::AspectCore.DynamicProxy.IAspectValidator GetValidator()");
+ sb.AppendLine(" {");
+ sb.AppendLine(" if (_validator != null) return _validator;");
+ sb.AppendLine(" var accessor = _activatorFactory as global::AspectCore.DynamicProxy.IAspectConfigurationAccessor;");
+ sb.AppendLine(" if (accessor == null) return null;");
+ sb.AppendLine(" _validator = new global::AspectCore.DynamicProxy.AspectValidatorBuilder(accessor.AspectConfiguration).Build();");
+ sb.AppendLine(" return _validator;");
+ sb.AppendLine(" }");
+ sb.AppendLine();
+ sb.AppendLine(" private bool ShouldIntercept(MethodInfo serviceMethod, MethodInfo implementationMethod)");
+ sb.AppendLine(" {");
+ sb.AppendLine(" if (global::AspectCore.DynamicProxy.ReflectionUtils.IsNonAspect(serviceMethod)) return false;");
+ sb.AppendLine(" var v = GetValidator();");
+ sb.AppendLine(" if (v == null) return false;");
+ sb.AppendLine(" return v.Validate(serviceMethod, true) || v.Validate(implementationMethod, false);");
+ sb.AppendLine(" }");
+ }
+
+ private static void EmitInterfaceStubMembers(StringBuilder sb, INamedTypeSymbol iface)
+ {
+ // properties
+ foreach (var prop in iface.GetMembers().OfType())
+ {
+ var typeName = prop.Type.ToGlobalName();
+ if (prop.IsIndexer)
+ {
+ // minimal indexer stub
+ sb.Append(" public ").Append(typeName).Append(" this[");
+ sb.Append(string.Join(", ", prop.Parameters.Select(p => $"{p.Type.ToGlobalName()} {p.Name}")));
+ sb.AppendLine("]");
+ sb.AppendLine(" {");
+ if (prop.GetMethod is not null)
+ sb.AppendLine($" get => default({typeName});");
+ if (prop.SetMethod is not null)
+ sb.AppendLine(" set { }");
+ sb.AppendLine(" }");
+ }
+ else
+ {
+ sb.Append(" public ").Append(typeName).Append(' ').Append(prop.Name).AppendLine();
+ sb.AppendLine(" {");
+ if (prop.GetMethod is not null)
+ sb.AppendLine($" get => default({typeName});");
+ if (prop.SetMethod is not null)
+ sb.AppendLine(" set { }");
+ sb.AppendLine(" }");
+ }
+ sb.AppendLine();
+ }
+
+ // methods
+ // Only emit stubs for abstract members. For default interface methods, omit the member so that
+ // runtime dispatch can use the DIM implementation.
+ foreach (var method in iface.GetMembers().OfType()
+ .Where(m => m.MethodKind == MethodKind.Ordinary)
+ .Where(m => m.IsAbstract))
+ {
+ EmitStubMethod(sb, method);
+ sb.AppendLine();
+ }
+ }
+
+ private static void EmitStubMethod(StringBuilder sb, IMethodSymbol method)
+ {
+ sb.Append(" public ");
+ sb.Append(method.ReturnsVoid ? "void" : method.ReturnType.ToGlobalName());
+ sb.Append(' ').Append(method.Name).Append(EmitGenericParameterList(method)).Append('(');
+ sb.Append(string.Join(", ", method.Parameters.Select(EmitParameterDecl)));
+ sb.AppendLine(")");
+ EmitGenericConstraints(sb, method, indent: " ");
+ sb.AppendLine(" {");
+ foreach (var p in method.Parameters)
+ {
+ if (p.RefKind == RefKind.Out)
+ {
+ sb.AppendLine($" {p.Name} = default({p.Type.ToGlobalName()});");
+ }
+ }
+
+ if (!method.ReturnsVoid)
+ {
+ sb.AppendLine($" return default({method.ReturnType.ToGlobalName()});");
+ }
+ sb.AppendLine(" }");
+ }
+
+ private static void EmitInterfaceProxyMembers(StringBuilder sb, Compilation compilation, INamedTypeSymbol iface, INamedTypeSymbol? implementationType, string proxyName, string ifaceName)
+ {
+ // meta cache
+ sb.AppendLine(" private static class __Meta");
+ sb.AppendLine(" {");
+
+ var methods = iface.GetMembers().OfType().Where(m => m.MethodKind == MethodKind.Ordinary).ToList();
+ var props = iface.GetMembers().OfType().ToList();
+
+ foreach (var m in methods)
+ {
+ EmitMethodMeta(sb, iface, implementationType, proxyName, m);
+ }
+ foreach (var p in props)
+ {
+ if (p.GetMethod is not null) EmitMethodMeta(sb, iface, implementationType, proxyName, p.GetMethod);
+ if (p.SetMethod is not null) EmitMethodMeta(sb, iface, implementationType, proxyName, p.SetMethod);
+ }
+
+ sb.AppendLine(" }");
+ sb.AppendLine();
+
+ // properties
+ foreach (var prop in props)
+ {
+ EmitProxyProperty(sb, iface, implementationType, proxyName, ifaceName, prop);
+ sb.AppendLine();
+ }
+
+ // methods
+ foreach (var method in methods)
+ {
+ EmitProxyMethod(sb, iface, implementationType, proxyName, ifaceName, method, callTargetExpr: $"_implementation.{method.Name}");
+ sb.AppendLine();
+ }
+ }
+
+ private static void EmitClassConstructors(StringBuilder sb, INamedTypeSymbol implType, string proxyName)
+ {
+ foreach (var ctor in implType.InstanceConstructors.Where(c => c.DeclaredAccessibility is Accessibility.Public or Accessibility.Protected or Accessibility.ProtectedOrInternal or Accessibility.ProtectedAndInternal))
+ {
+ sb.Append(" public ").Append(proxyName).Append("(global::AspectCore.DynamicProxy.IAspectActivatorFactory activatorFactory");
+ if (ctor.Parameters.Length > 0)
+ {
+ sb.Append(", ");
+ sb.Append(string.Join(", ", ctor.Parameters.Select(p => $"{p.Type.ToGlobalName()} {p.Name}")));
+ }
+ sb.Append(')');
+ sb.Append(" : base(");
+ sb.Append(string.Join(", ", ctor.Parameters.Select(p => p.Name)));
+ sb.AppendLine(")");
+ sb.AppendLine(" {");
+ sb.AppendLine(" _activatorFactory = activatorFactory ?? throw new ArgumentNullException(nameof(activatorFactory));");
+ sb.AppendLine(" _implementation = this;");
+ sb.AppendLine(" }");
+ sb.AppendLine();
+ }
+ }
+
+ private static void EmitClassProxyMembers(StringBuilder sb, Compilation compilation, INamedTypeSymbol serviceType, INamedTypeSymbol implType, string proxyName, string serviceName)
+ {
+ var methods = serviceType.GetMembers().OfType()
+ .Where(m => m.MethodKind == MethodKind.Ordinary)
+ .Where(IsOverridable)
+ .ToList();
+ var props = serviceType.GetMembers().OfType()
+ .Where(p => (p.GetMethod is not null && IsOverridable(p.GetMethod)) || (p.SetMethod is not null && IsOverridable(p.SetMethod)))
+ .ToList();
+
+ sb.AppendLine(" private static class __Meta");
+ sb.AppendLine(" {");
+ foreach (var m in methods)
+ {
+ EmitMethodMeta(sb, serviceType, implType, proxyName, m);
+ }
+ foreach (var p in props)
+ {
+ if (p.GetMethod is not null) EmitMethodMeta(sb, serviceType, implType, proxyName, p.GetMethod);
+ if (p.SetMethod is not null) EmitMethodMeta(sb, serviceType, implType, proxyName, p.SetMethod);
+ }
+ sb.AppendLine(" }");
+ sb.AppendLine();
+
+ foreach (var prop in props)
+ {
+ EmitProxyProperty(sb, serviceType, implType, proxyName, serviceName, prop, isOverride: true);
+ sb.AppendLine();
+ }
+
+ foreach (var method in methods)
+ {
+ EmitProxyMethod(sb, serviceType, implType, proxyName, serviceName, method, callTargetExpr: $"base.{method.Name}", isOverride: true);
+ sb.AppendLine();
+ }
+ }
+
+ private static bool IsOverridable(IMethodSymbol method)
+ {
+ if (method.IsStatic) return false;
+ if (!method.IsVirtual) return false;
+ if (method.IsSealed) return false;
+ if (method.DeclaredAccessibility is not (Accessibility.Public or Accessibility.Protected or Accessibility.ProtectedOrInternal or Accessibility.ProtectedAndInternal))
+ return false;
+ if (method.Name == "Finalize") return false;
+ return true;
+ }
+
+ private static void EmitMethodMeta(StringBuilder sb, INamedTypeSymbol serviceType, INamedTypeSymbol? implementationType, string proxyTypeName, IMethodSymbol method)
+ {
+ var suffix = GetMethodId(method);
+ var serviceMethodField = $"Service_{suffix}";
+ var implMethodField = $"Impl_{suffix}";
+ var proxyMethodField = $"Proxy_{suffix}";
+
+ var serviceTypeExpr = $"typeof({serviceType.ToGlobalName()})";
+ var proxyTypeExpr = $"typeof(global::{Naming.GeneratedProxyNamespace}.{proxyTypeName})";
+
+ if (!method.IsGenericMethod)
+ {
+ var paramTypesExpr = EmitParameterTypesArray(method);
+ sb.AppendLine($" internal static readonly MethodInfo {serviceMethodField} = {serviceTypeExpr}.GetMethod(\"{method.Name}\", global::System.Reflection.BindingFlags.Instance | global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic, binder: null, types: {paramTypesExpr}, modifiers: null);");
+ }
+ else
+ {
+ var findName = $"FindService_{suffix}";
+ sb.AppendLine($" internal static readonly MethodInfo {serviceMethodField} = {findName}();");
+ sb.AppendLine($" private static MethodInfo {findName}()");
+ sb.AppendLine(" {");
+ sb.AppendLine($" foreach (var m in {serviceTypeExpr}.GetMethods(global::System.Reflection.BindingFlags.Instance | global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic))");
+ sb.AppendLine(" {");
+ sb.AppendLine($" if (m.Name != \"{method.Name}\") continue;");
+ sb.AppendLine(" if (!m.IsGenericMethodDefinition) continue;");
+ sb.AppendLine($" if (m.GetGenericArguments().Length != {method.TypeParameters.Length}) continue;");
+ sb.AppendLine(" var ps = m.GetParameters();");
+ sb.AppendLine($" if (ps.Length != {method.Parameters.Length}) continue;");
+
+ for (var i = 0; i < method.Parameters.Length; i++)
+ {
+ var p = method.Parameters[i];
+ var wantsByRef = p.RefKind is RefKind.Ref or RefKind.Out or RefKind.In;
+ var expectsGenericParam = p.Type is ITypeParameterSymbol;
+ var expectedTypeExpr = expectsGenericParam ? null : $"typeof({p.Type.ToGlobalName()})";
+
+ sb.AppendLine($" var p{i} = ps[{i}].ParameterType;");
+ if (wantsByRef)
+ {
+ sb.AppendLine($" if (!p{i}.IsByRef) continue;");
+ sb.AppendLine($" p{i} = p{i}.GetElementType();");
+ }
+ else
+ {
+ sb.AppendLine($" if (p{i}.IsByRef) continue;");
+ }
+
+ if (expectsGenericParam)
+ {
+ sb.AppendLine($" if (p{i} == null || !p{i}.IsGenericParameter) continue;");
+ }
+ else
+ {
+ sb.AppendLine($" if (p{i} != {expectedTypeExpr}) continue;");
+ }
+ }
+
+ sb.AppendLine(" return m;");
+ sb.AppendLine(" }");
+ sb.AppendLine($" throw new MissingMethodException({serviceTypeExpr}.FullName, \"{method.Name}\");");
+ sb.AppendLine(" }");
+ }
+
+ if (implementationType is null)
+ {
+ // no target: use service method itself
+ sb.AppendLine($" internal static readonly MethodInfo {implMethodField} = {serviceMethodField};");
+ }
+ else
+ {
+ sb.AppendLine($" internal static readonly MethodInfo {implMethodField} = global::AspectCore.DynamicProxy.ReflectionUtils.GetMethodBySignature(typeof({implementationType.ToGlobalName()}).GetTypeInfo(), {serviceMethodField});");
+ }
+
+ if (!method.IsGenericMethod)
+ {
+ var paramTypesExpr = EmitParameterTypesArray(method);
+ sb.AppendLine($" internal static readonly MethodInfo {proxyMethodField} = {proxyTypeExpr}.GetMethod(\"{method.Name}\", global::System.Reflection.BindingFlags.Instance | global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic, binder: null, types: {paramTypesExpr}, modifiers: null);");
+ }
+ else
+ {
+ var findName = $"FindProxy_{suffix}";
+ sb.AppendLine($" internal static readonly MethodInfo {proxyMethodField} = {findName}();");
+ sb.AppendLine($" private static MethodInfo {findName}()");
+ sb.AppendLine(" {");
+ sb.AppendLine($" foreach (var m in {proxyTypeExpr}.GetMethods(global::System.Reflection.BindingFlags.Instance | global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic))");
+ sb.AppendLine(" {");
+ sb.AppendLine($" if (m.Name != \"{method.Name}\") continue;");
+ sb.AppendLine(" if (!m.IsGenericMethodDefinition) continue;");
+ sb.AppendLine($" if (m.GetGenericArguments().Length != {method.TypeParameters.Length}) continue;");
+ sb.AppendLine(" var ps = m.GetParameters();");
+ sb.AppendLine($" if (ps.Length != {method.Parameters.Length}) continue;");
+
+ for (var i = 0; i < method.Parameters.Length; i++)
+ {
+ var p = method.Parameters[i];
+ var wantsByRef = p.RefKind is RefKind.Ref or RefKind.Out or RefKind.In;
+ var expectsGenericParam = p.Type is ITypeParameterSymbol;
+ var expectedTypeExpr = expectsGenericParam ? null : $"typeof({p.Type.ToGlobalName()})";
+
+ sb.AppendLine($" var p{i} = ps[{i}].ParameterType;");
+ if (wantsByRef)
+ {
+ sb.AppendLine($" if (!p{i}.IsByRef) continue;");
+ sb.AppendLine($" p{i} = p{i}.GetElementType();");
+ }
+ else
+ {
+ sb.AppendLine($" if (p{i}.IsByRef) continue;");
+ }
+
+ if (expectsGenericParam)
+ {
+ sb.AppendLine($" if (p{i} == null || !p{i}.IsGenericParameter) continue;");
+ }
+ else
+ {
+ sb.AppendLine($" if (p{i} != {expectedTypeExpr}) continue;");
+ }
+ }
+
+ sb.AppendLine(" return m;");
+ sb.AppendLine(" }");
+ sb.AppendLine($" throw new MissingMethodException({proxyTypeExpr}.FullName, \"{method.Name}\");");
+ sb.AppendLine(" }");
+ }
+ sb.AppendLine();
+ }
+
+ private static void EmitProxyProperty(StringBuilder sb, INamedTypeSymbol serviceType, INamedTypeSymbol? implementationType, string proxyTypeName, string declaredTypeName, IPropertySymbol prop, bool isOverride = false)
+ {
+ var propTypeName = prop.Type.ToGlobalName();
+
+ if (prop.IsIndexer)
+ {
+ sb.Append(" public ");
+ if (isOverride) sb.Append("override ");
+ sb.Append(propTypeName).Append(" this[");
+ sb.Append(string.Join(", ", prop.Parameters.Select(p => $"{p.Type.ToGlobalName()} {p.Name}")));
+ sb.AppendLine("]");
+ }
+ else
+ {
+ sb.Append(" public ");
+ if (isOverride) sb.Append("override ");
+ sb.Append(propTypeName).Append(' ').Append(prop.Name).AppendLine();
+ }
+
+ sb.AppendLine(" {");
+ if (prop.GetMethod is not null)
+ {
+ EmitAccessorBody(sb, serviceType, implementationType, proxyTypeName, prop.GetMethod, declaredTypeName, prop, accessorKind: "get", callExpr: prop.IsIndexer ? "base" : "base", isOverride: isOverride);
+ }
+ if (prop.SetMethod is not null)
+ {
+ EmitAccessorBody(sb, serviceType, implementationType, proxyTypeName, prop.SetMethod, declaredTypeName, prop, accessorKind: "set", callExpr: prop.IsIndexer ? "base" : "base", isOverride: isOverride);
+ }
+ sb.AppendLine(" }");
+ }
+
+ private static void EmitAccessorBody(StringBuilder sb, INamedTypeSymbol serviceType, INamedTypeSymbol? implementationType, string proxyTypeName, IMethodSymbol accessor, string declaredTypeName, IPropertySymbol prop, string accessorKind, string callExpr, bool isOverride)
+ {
+ // Use same emission as method, but inside property block.
+ // We'll generate a local function body as statement block.
+
+ var suffix = GetMethodId(accessor);
+ var serviceMethodField = $"__Meta.Service_{suffix}";
+ var implMethodField = $"__Meta.Impl_{suffix}";
+ var proxyMethodField = $"__Meta.Proxy_{suffix}";
+
+ if (accessorKind == "get")
+ {
+ sb.AppendLine(" get");
+ sb.AppendLine(" {");
+ EmitProxyInvokeBody(
+ sb,
+ accessor,
+ serviceMethodField,
+ implMethodField,
+ proxyMethodField,
+ directCall: prop.IsIndexer
+ ? $"{(isOverride ? "base" : "_implementation")}[{string.Join(", ", prop.Parameters.Select(p => p.Name))}]"
+ : $"{(isOverride ? "base" : "_implementation")}.{prop.Name}",
+ resolveImplementationMethodByInstance: implementationType is null,
+ interfaceStubTypeName: implementationType is null ? $"{proxyTypeName}__Stub" : null);
+ sb.AppendLine(" }");
+ }
+ else
+ {
+ sb.AppendLine(" set");
+ sb.AppendLine(" {");
+ EmitProxyInvokeBody(
+ sb,
+ accessor,
+ serviceMethodField,
+ implMethodField,
+ proxyMethodField,
+ directCall: prop.IsIndexer
+ ? $"{(isOverride ? "base" : "_implementation")}[{string.Join(", ", prop.Parameters.Select(p => p.Name))}] = value"
+ : $"{(isOverride ? "base" : "_implementation")}.{prop.Name} = value",
+ resolveImplementationMethodByInstance: implementationType is null,
+ interfaceStubTypeName: implementationType is null ? $"{proxyTypeName}__Stub" : null);
+ sb.AppendLine(" }");
+ }
+ }
+
+ private static void EmitProxyMethod(StringBuilder sb, INamedTypeSymbol serviceType, INamedTypeSymbol? implementationType, string proxyTypeName, string declaredTypeName, IMethodSymbol method, string callTargetExpr, bool isOverride = false)
+ {
+ sb.Append(" public ");
+ if (isOverride) sb.Append("override ");
+ sb.Append(method.ReturnsVoid ? "void" : method.ReturnType.ToGlobalName());
+ sb.Append(' ').Append(method.Name).Append(EmitGenericParameterList(method)).Append('(');
+ sb.Append(string.Join(", ", method.Parameters.Select(EmitParameterDecl)));
+ sb.AppendLine(")");
+ EmitGenericConstraints(sb, method, indent: " ");
+ sb.AppendLine(" {");
+
+ var suffix = GetMethodId(method);
+ EmitProxyInvokeBody(
+ sb,
+ method,
+ serviceMethodField: $"__Meta.Service_{suffix}",
+ implMethodField: $"__Meta.Impl_{suffix}",
+ proxyMethodField: $"__Meta.Proxy_{suffix}",
+ directCall: $"{callTargetExpr}{EmitGenericTypeArgumentList(method)}({string.Join(", ", method.Parameters.Select(EmitArgument))})",
+ resolveImplementationMethodByInstance: implementationType is null,
+ interfaceStubTypeName: implementationType is null ? $"{proxyTypeName}__Stub" : null);
+
+ sb.AppendLine(" }");
+ }
+
+ private static void EmitProxyInvokeBody(
+ StringBuilder sb,
+ IMethodSymbol method,
+ string serviceMethodField,
+ string implMethodField,
+ string proxyMethodField,
+ string directCall,
+ bool resolveImplementationMethodByInstance,
+ string? interfaceStubTypeName)
+ {
+ var argCount = method.Parameters.Length;
+ sb.AppendLine($" var __serviceMethod = {serviceMethodField};");
+ sb.AppendLine($" var __implMethod = {implMethodField};");
+ sb.AppendLine($" var __proxyMethod = {proxyMethodField};");
+
+ if (resolveImplementationMethodByInstance)
+ {
+ sb.AppendLine($" if (_implementation is not null && !(_implementation is {interfaceStubTypeName}))");
+ sb.AppendLine(" {");
+ sb.AppendLine(" try");
+ sb.AppendLine(" {");
+ sb.AppendLine(" __implMethod = global::AspectCore.DynamicProxy.ReflectionUtils.GetMethodBySignature(_implementation.GetType().GetTypeInfo(), __serviceMethod);");
+ sb.AppendLine(" }");
+ sb.AppendLine(" catch");
+ sb.AppendLine(" {");
+ sb.AppendLine(" // ignore and fall back to service method\n");
+ sb.AppendLine(" }");
+ sb.AppendLine(" }");
+ sb.AppendLine();
+ }
+
+ if (method.IsGenericMethod)
+ {
+ sb.AppendLine($" var __genericArgs = new[] {{ {string.Join(", ", method.TypeParameters.Select(tp => $"typeof({tp.Name})"))} }};");
+ sb.AppendLine(" if (__serviceMethod.IsGenericMethodDefinition) __serviceMethod = __serviceMethod.MakeGenericMethod(__genericArgs);");
+ sb.AppendLine(" if (__implMethod.IsGenericMethodDefinition) __implMethod = __implMethod.MakeGenericMethod(__genericArgs);");
+ sb.AppendLine(" if (__proxyMethod.IsGenericMethodDefinition) __proxyMethod = __proxyMethod.MakeGenericMethod(__genericArgs);");
+ sb.AppendLine();
+ }
+ sb.AppendLine(" if (!ShouldIntercept(__serviceMethod, __implMethod))");
+ sb.AppendLine(" {");
+ if (method.ReturnsVoid)
+ {
+ sb.AppendLine($" {directCall};");
+ sb.AppendLine(" return;");
+ }
+ else
+ {
+ sb.AppendLine($" return {directCall};");
+ }
+ sb.AppendLine(" }");
+ sb.AppendLine();
+ sb.AppendLine($" var __args = new object[{argCount}];");
+ for (var i = 0; i < argCount; i++)
+ {
+ var p = method.Parameters[i];
+ if (p.RefKind == RefKind.Out)
+ {
+ sb.AppendLine($" __args[{i}] = default({p.Type.ToGlobalName()});");
+ }
+ else
+ {
+ sb.AppendLine($" __args[{i}] = {p.Name};");
+ }
+ }
+ sb.AppendLine();
+ sb.AppendLine(" var __ctx = new global::AspectCore.DynamicProxy.AspectActivatorContext(");
+ sb.AppendLine(" __serviceMethod,");
+ sb.AppendLine(" __implMethod,");
+ sb.AppendLine(" __proxyMethod,");
+ sb.AppendLine(" _implementation,");
+ sb.AppendLine(" this,");
+ sb.AppendLine(" __args);");
+ sb.AppendLine();
+ sb.AppendLine(" var __activator = _activatorFactory.Create();");
+
+ var rk = ReturnKind.Determine(method);
+ switch (rk)
+ {
+ case ReturnKindKind.Void:
+ sb.AppendLine(" __activator.Invoke