├── .github └── workflows │ ├── Publish_NuGet_Manual.yml │ └── continuous.yml ├── .gitignore ├── .nuke ├── build.schema.json └── parameters.json ├── LICENSE ├── LuYao.LightRpc.Demo.sln ├── LuYao.LightRpc.Test.sln ├── LuYao.LightRpc.sln ├── README.md ├── build.cmd ├── build.ps1 ├── build.sh ├── build ├── .editorconfig ├── Build.Paths.cs ├── Build.Publish.cs ├── Build.Test.cs ├── Build.cs ├── Configuration.cs ├── Directory.Build.props ├── Directory.Build.targets ├── _build.csproj └── _build.csproj.DotSettings ├── demo ├── LuYao.LightRpc.Demo.Client │ ├── LuYao.LightRpc.Demo.Client.csproj │ ├── Program.cs │ ├── RpcClient.cs │ ├── RpcTunnel.cs │ └── rd.xml ├── LuYao.LightRpc.Demo.MinimalApi │ ├── .config │ │ └── dotnet-tools.json │ ├── LuYao.LightRpc.Demo.MinimalApi.csproj │ ├── MainServer.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── RpcHttpResult.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── LuYao.LightRpc.Demo │ ├── LuYao.LightRpc.Demo.csproj │ ├── MainServer.cs │ └── TestController.cs └── fc │ ├── LuYao.LightRpc.Demo.Functions.InAliyun │ ├── LuYao.LightRpc.Demo.Functions.InAliyun.csproj │ ├── MainServer.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── RpcHttpResult.cs │ ├── appsettings.Development.json │ └── appsettings.json │ └── LuYao.LightRpc.Demo.Functions.InAzure │ ├── .gitignore │ ├── Handler.cs │ ├── LuYao.LightRpc.Demo.Functions.InAzure.csproj │ ├── MainServer.cs │ ├── Program.cs │ ├── Properties │ ├── launchSettings.json │ ├── serviceDependencies.json │ └── serviceDependencies.local.json │ └── host.json ├── src ├── LuYao.LightRpc.Newtonsoft │ ├── FodyWeavers.xml │ ├── JObjectDataPackage.cs │ ├── LuYao.LightRpc.Newtonsoft.csproj │ └── NewtonsoftDataConverter.cs ├── LuYao.LightRpc.SourceGenerators │ ├── CSharpStringBuilder.cs │ ├── ControllerDescriptorGenerator.cs │ ├── DisposeAction.cs │ ├── FodyWeavers.xml │ ├── Helpers.cs │ ├── LuYao.LightRpc.SourceGenerators.csproj │ ├── Models │ │ ├── BuildControllerDescriptor │ │ │ ├── ActionModel.cs │ │ │ ├── ActionParameterModel.cs │ │ │ └── ControllerModel.cs │ │ └── BuildRpcClientAgent │ │ │ ├── ActionModel.cs │ │ │ ├── ActionParameterModel.cs │ │ │ ├── ActionTypeParameterModel.cs │ │ │ └── ClientModel.cs │ ├── Renders │ │ ├── BuildControllerDescriptor │ │ │ └── ControllerDescriptorRender.cs │ │ └── BuildRpcClientAgent │ │ │ └── RpcClientAgentRender.cs │ └── RpcClientAgentGenerator.cs └── LuYao.LightRpc │ ├── Attributes │ ├── ControllerAttribute.cs │ ├── ControllerDescriptorAttribute.cs │ ├── IgnoreAttribute.cs │ ├── RemoteActionAttribute.cs │ └── RpcClientAgentAttribute.cs │ ├── Descriptors │ ├── ActionDescriptor.cs │ ├── IActionDescriptor.cs │ └── IControllerDescriptor.cs │ ├── EmptyDataPackage.cs │ ├── Http │ └── HttpJsonRpcTunnel.cs │ ├── IDataConverter.cs │ ├── IDataPackage.cs │ ├── IRpcTunnel.cs │ ├── InvokeContext.cs │ ├── LuYao.LightRpc.csproj │ ├── RpcClient.cs │ ├── RpcException.cs │ ├── RpcResult.cs │ ├── RpcResultCode.cs │ ├── RpcServer.cs │ └── RpcService.cs └── tests ├── LuYao.LightRpc.Test.Client ├── BaseClass.cs ├── LuYao.LightRpc.Test.Client.csproj ├── MainClient.cs ├── MainTunnel.cs └── Program.cs ├── LuYao.LightRpc.Test.MinimalApi ├── LuYao.LightRpc.Test.MinimalApi.csproj ├── MainServer.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── RpcHttpResult.cs ├── appsettings.Development.json └── appsettings.json ├── LuYao.LightRpc.Test ├── LuYao.LightRpc.Test.csproj ├── MainServer.cs └── TestController.cs └── LuYao.LightRpc.UnitTests └── LuYao.LightRpc.UnitTests.csproj /.github/workflows/Publish_NuGet_Manual.yml: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # 3 | # 4 | # This code was generated. 5 | # 6 | # - To turn off auto-generation set: 7 | # 8 | # [GitHubActions (AutoGenerate = false)] 9 | # 10 | # - To trigger manual generation invoke: 11 | # 12 | # nuke --generate-configuration GitHubActions_Publish_NuGet_Manual --host GitHubActions 13 | # 14 | # 15 | # ------------------------------------------------------------------------------ 16 | 17 | name: Publish_NuGet_Manual 18 | 19 | on: [workflow_dispatch] 20 | 21 | jobs: 22 | windows-2022: 23 | name: windows-2022 24 | runs-on: windows-2022 25 | steps: 26 | - uses: actions/checkout@v4 27 | with: 28 | fetch-depth: 0 29 | - name: 'Cache: .nuke/temp, ~/.nuget/packages' 30 | uses: actions/cache@v4 31 | with: 32 | path: | 33 | .nuke/temp 34 | ~/.nuget/packages 35 | key: ${{ runner.os }}-${{ hashFiles('**/global.json', '**/*.csproj', '**/Directory.Packages.props') }} 36 | - name: 'Run: Push' 37 | run: ./build.cmd Push 38 | env: 39 | NuGetApiKey: ${{ secrets.NUGET_API_KEY }} 40 | - name: 'Publish: packages' 41 | uses: actions/upload-artifact@v4 42 | with: 43 | name: packages 44 | path: output/packages 45 | -------------------------------------------------------------------------------- /.github/workflows/continuous.yml: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # 3 | # 4 | # This code was generated. 5 | # 6 | # - To turn off auto-generation set: 7 | # 8 | # [GitHubActions (AutoGenerate = false)] 9 | # 10 | # - To trigger manual generation invoke: 11 | # 12 | # nuke --generate-configuration GitHubActions_continuous --host GitHubActions 13 | # 14 | # 15 | # ------------------------------------------------------------------------------ 16 | 17 | name: continuous 18 | 19 | on: [push] 20 | 21 | jobs: 22 | windows-2022: 23 | name: windows-2022 24 | runs-on: windows-2022 25 | steps: 26 | - uses: actions/checkout@v4 27 | with: 28 | fetch-depth: 0 29 | - name: 'Cache: .nuke/temp, ~/.nuget/packages' 30 | uses: actions/cache@v4 31 | with: 32 | path: | 33 | .nuke/temp 34 | ~/.nuget/packages 35 | key: ${{ runner.os }}-${{ hashFiles('**/global.json', '**/*.csproj', '**/Directory.Packages.props') }} 36 | - name: 'Run: Compile' 37 | run: ./build.cmd Compile 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | -------------------------------------------------------------------------------- /.nuke/build.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "properties": { 4 | "Configuration": { 5 | "type": "string", 6 | "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", 7 | "enum": [ 8 | "Debug", 9 | "Release" 10 | ] 11 | }, 12 | "NuGetApiKey": { 13 | "type": "string", 14 | "description": "Api key to use when pushing the package" 15 | }, 16 | "PackageSource": { 17 | "type": "string", 18 | "description": "NuGet artifact target uri - Defaults to https://api.nuget.org/v3/index.json" 19 | }, 20 | "Solution": { 21 | "type": "string", 22 | "description": "Path to a solution file that is automatically loaded" 23 | } 24 | }, 25 | "definitions": { 26 | "Host": { 27 | "type": "string", 28 | "enum": [ 29 | "AppVeyor", 30 | "AzurePipelines", 31 | "Bamboo", 32 | "Bitbucket", 33 | "Bitrise", 34 | "GitHubActions", 35 | "GitLab", 36 | "Jenkins", 37 | "Rider", 38 | "SpaceAutomation", 39 | "TeamCity", 40 | "Terminal", 41 | "TravisCI", 42 | "VisualStudio", 43 | "VSCode" 44 | ] 45 | }, 46 | "ExecutableTarget": { 47 | "type": "string", 48 | "enum": [ 49 | "Clean", 50 | "Compile", 51 | "Pack", 52 | "Print", 53 | "Push", 54 | "Restore" 55 | ] 56 | }, 57 | "Verbosity": { 58 | "type": "string", 59 | "description": "", 60 | "enum": [ 61 | "Verbose", 62 | "Normal", 63 | "Minimal", 64 | "Quiet" 65 | ] 66 | }, 67 | "NukeBuild": { 68 | "properties": { 69 | "Continue": { 70 | "type": "boolean", 71 | "description": "Indicates to continue a previously failed build attempt" 72 | }, 73 | "Help": { 74 | "type": "boolean", 75 | "description": "Shows the help text for this build assembly" 76 | }, 77 | "Host": { 78 | "description": "Host for execution. Default is 'automatic'", 79 | "$ref": "#/definitions/Host" 80 | }, 81 | "NoLogo": { 82 | "type": "boolean", 83 | "description": "Disables displaying the NUKE logo" 84 | }, 85 | "Partition": { 86 | "type": "string", 87 | "description": "Partition to use on CI" 88 | }, 89 | "Plan": { 90 | "type": "boolean", 91 | "description": "Shows the execution plan (HTML)" 92 | }, 93 | "Profile": { 94 | "type": "array", 95 | "description": "Defines the profiles to load", 96 | "items": { 97 | "type": "string" 98 | } 99 | }, 100 | "Root": { 101 | "type": "string", 102 | "description": "Root directory during build execution" 103 | }, 104 | "Skip": { 105 | "type": "array", 106 | "description": "List of targets to be skipped. Empty list skips all dependencies", 107 | "items": { 108 | "$ref": "#/definitions/ExecutableTarget" 109 | } 110 | }, 111 | "Target": { 112 | "type": "array", 113 | "description": "List of targets to be invoked. Default is '{default_target}'", 114 | "items": { 115 | "$ref": "#/definitions/ExecutableTarget" 116 | } 117 | }, 118 | "Verbosity": { 119 | "description": "Logging verbosity during build execution. Default is 'Normal'", 120 | "$ref": "#/definitions/Verbosity" 121 | } 122 | } 123 | } 124 | }, 125 | "$ref": "#/definitions/NukeBuild" 126 | } 127 | -------------------------------------------------------------------------------- /.nuke/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "build.schema.json", 3 | "Solution": "LuYao.LightRpc.sln" 4 | } 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 码农很忙 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LuYao.LightRpc.Demo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.11.35312.102 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LuYao.LightRpc.Demo", "demo\LuYao.LightRpc.Demo\LuYao.LightRpc.Demo.csproj", "{FC06BAE2-28EA-47AA-B688-B065A461CC60}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "functions", "functions", "{8ED26275-E307-4D36-875E-EC09D652A629}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LuYao.LightRpc.Demo.Functions.InAzure", "demo\fc\LuYao.LightRpc.Demo.Functions.InAzure\LuYao.LightRpc.Demo.Functions.InAzure.csproj", "{BEC53184-0817-4915-A2F5-4F2C62754ABB}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LuYao.LightRpc.Demo.Functions.InAliyun", "demo\fc\LuYao.LightRpc.Demo.Functions.InAliyun\LuYao.LightRpc.Demo.Functions.InAliyun.csproj", "{858FB197-CC4C-4A4A-BA3C-11186101797F}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LuYao.LightRpc.Demo.Client", "demo\LuYao.LightRpc.Demo.Client\LuYao.LightRpc.Demo.Client.csproj", "{7D193906-C35B-4E75-8270-66A37498E85A}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuYao.LightRpc.Demo.MinimalApi", "demo\LuYao.LightRpc.Demo.MinimalApi\LuYao.LightRpc.Demo.MinimalApi.csproj", "{59B320CC-49D5-4AA9-9D8C-17528C0A155A}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {FC06BAE2-28EA-47AA-B688-B065A461CC60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {FC06BAE2-28EA-47AA-B688-B065A461CC60}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {FC06BAE2-28EA-47AA-B688-B065A461CC60}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {FC06BAE2-28EA-47AA-B688-B065A461CC60}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {BEC53184-0817-4915-A2F5-4F2C62754ABB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {BEC53184-0817-4915-A2F5-4F2C62754ABB}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {BEC53184-0817-4915-A2F5-4F2C62754ABB}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {BEC53184-0817-4915-A2F5-4F2C62754ABB}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {858FB197-CC4C-4A4A-BA3C-11186101797F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {858FB197-CC4C-4A4A-BA3C-11186101797F}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {858FB197-CC4C-4A4A-BA3C-11186101797F}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {858FB197-CC4C-4A4A-BA3C-11186101797F}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {7D193906-C35B-4E75-8270-66A37498E85A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {7D193906-C35B-4E75-8270-66A37498E85A}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {7D193906-C35B-4E75-8270-66A37498E85A}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {7D193906-C35B-4E75-8270-66A37498E85A}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {59B320CC-49D5-4AA9-9D8C-17528C0A155A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {59B320CC-49D5-4AA9-9D8C-17528C0A155A}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {59B320CC-49D5-4AA9-9D8C-17528C0A155A}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {59B320CC-49D5-4AA9-9D8C-17528C0A155A}.Release|Any CPU.Build.0 = Release|Any CPU 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(NestedProjects) = preSolution 49 | {BEC53184-0817-4915-A2F5-4F2C62754ABB} = {8ED26275-E307-4D36-875E-EC09D652A629} 50 | {858FB197-CC4C-4A4A-BA3C-11186101797F} = {8ED26275-E307-4D36-875E-EC09D652A629} 51 | EndGlobalSection 52 | GlobalSection(ExtensibilityGlobals) = postSolution 53 | SolutionGuid = {3D869020-D838-4F7B-9CC3-A537443D7B65} 54 | EndGlobalSection 55 | EndGlobal 56 | -------------------------------------------------------------------------------- /LuYao.LightRpc.Test.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31912.275 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LuYao.LightRpc", "src\LuYao.LightRpc\LuYao.LightRpc.csproj", "{1029D930-4556-4FE6-BA64-F9E9F9B87A55}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E65A796D-A9BE-4C66-9A17-C9A8E2BD8347}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LuYao.LightRpc.Newtonsoft", "src\LuYao.LightRpc.Newtonsoft\LuYao.LightRpc.Newtonsoft.csproj", "{F593163C-1AAB-4A15-9C55-F8DE467B2146}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LuYao.LightRpc.SourceGenerators", "src\LuYao.LightRpc.SourceGenerators\LuYao.LightRpc.SourceGenerators.csproj", "{01643FC8-921C-4460-9E46-0BDAFCA420E5}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LuYao.LightRpc.Test", "tests\LuYao.LightRpc.Test\LuYao.LightRpc.Test.csproj", "{F1D431B8-5DBA-45B3-9648-8B2B0CD01F63}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuYao.LightRpc.Test.MinimalApi", "tests\LuYao.LightRpc.Test.MinimalApi\LuYao.LightRpc.Test.MinimalApi.csproj", "{1AEC67DD-0562-4800-94CF-152CCF179F3D}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuYao.LightRpc.Test.Client", "tests\LuYao.LightRpc.Test.Client\LuYao.LightRpc.Test.Client.csproj", "{E4F2787B-B5F8-4339-9227-EE8FD6C4AF34}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {1029D930-4556-4FE6-BA64-F9E9F9B87A55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {1029D930-4556-4FE6-BA64-F9E9F9B87A55}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {1029D930-4556-4FE6-BA64-F9E9F9B87A55}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {1029D930-4556-4FE6-BA64-F9E9F9B87A55}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {F593163C-1AAB-4A15-9C55-F8DE467B2146}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {F593163C-1AAB-4A15-9C55-F8DE467B2146}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {F593163C-1AAB-4A15-9C55-F8DE467B2146}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {F593163C-1AAB-4A15-9C55-F8DE467B2146}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {01643FC8-921C-4460-9E46-0BDAFCA420E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {01643FC8-921C-4460-9E46-0BDAFCA420E5}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {01643FC8-921C-4460-9E46-0BDAFCA420E5}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {01643FC8-921C-4460-9E46-0BDAFCA420E5}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {F1D431B8-5DBA-45B3-9648-8B2B0CD01F63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {F1D431B8-5DBA-45B3-9648-8B2B0CD01F63}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {F1D431B8-5DBA-45B3-9648-8B2B0CD01F63}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {F1D431B8-5DBA-45B3-9648-8B2B0CD01F63}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {1AEC67DD-0562-4800-94CF-152CCF179F3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {1AEC67DD-0562-4800-94CF-152CCF179F3D}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {1AEC67DD-0562-4800-94CF-152CCF179F3D}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {1AEC67DD-0562-4800-94CF-152CCF179F3D}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {E4F2787B-B5F8-4339-9227-EE8FD6C4AF34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {E4F2787B-B5F8-4339-9227-EE8FD6C4AF34}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {E4F2787B-B5F8-4339-9227-EE8FD6C4AF34}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {E4F2787B-B5F8-4339-9227-EE8FD6C4AF34}.Release|Any CPU.Build.0 = Release|Any CPU 50 | EndGlobalSection 51 | GlobalSection(SolutionProperties) = preSolution 52 | HideSolutionNode = FALSE 53 | EndGlobalSection 54 | GlobalSection(NestedProjects) = preSolution 55 | {1029D930-4556-4FE6-BA64-F9E9F9B87A55} = {E65A796D-A9BE-4C66-9A17-C9A8E2BD8347} 56 | {F593163C-1AAB-4A15-9C55-F8DE467B2146} = {E65A796D-A9BE-4C66-9A17-C9A8E2BD8347} 57 | {01643FC8-921C-4460-9E46-0BDAFCA420E5} = {E65A796D-A9BE-4C66-9A17-C9A8E2BD8347} 58 | EndGlobalSection 59 | GlobalSection(ExtensibilityGlobals) = postSolution 60 | SolutionGuid = {DA69B1C3-F6B9-4AF9-859B-D680E03B79F2} 61 | EndGlobalSection 62 | EndGlobal 63 | -------------------------------------------------------------------------------- /LuYao.LightRpc.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31912.275 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{52B999D2-BA6F-46D9-AAEB-895D6756C0C7}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuYao.LightRpc", "src\LuYao.LightRpc\LuYao.LightRpc.csproj", "{1029D930-4556-4FE6-BA64-F9E9F9B87A55}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E65A796D-A9BE-4C66-9A17-C9A8E2BD8347}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{60037AE3-ACEB-4609-94C1-39B22A64EF73}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuYao.LightRpc.UnitTests", "tests\LuYao.LightRpc.UnitTests\LuYao.LightRpc.UnitTests.csproj", "{153BF677-B274-46EE-B45F-8AAD1AD0ADFE}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LuYao.LightRpc.Newtonsoft", "src\LuYao.LightRpc.Newtonsoft\LuYao.LightRpc.Newtonsoft.csproj", "{F593163C-1AAB-4A15-9C55-F8DE467B2146}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LuYao.LightRpc.SourceGenerators", "src\LuYao.LightRpc.SourceGenerators\LuYao.LightRpc.SourceGenerators.csproj", "{01643FC8-921C-4460-9E46-0BDAFCA420E5}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {52B999D2-BA6F-46D9-AAEB-895D6756C0C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {52B999D2-BA6F-46D9-AAEB-895D6756C0C7}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {1029D930-4556-4FE6-BA64-F9E9F9B87A55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {1029D930-4556-4FE6-BA64-F9E9F9B87A55}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {1029D930-4556-4FE6-BA64-F9E9F9B87A55}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {1029D930-4556-4FE6-BA64-F9E9F9B87A55}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {153BF677-B274-46EE-B45F-8AAD1AD0ADFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {153BF677-B274-46EE-B45F-8AAD1AD0ADFE}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {153BF677-B274-46EE-B45F-8AAD1AD0ADFE}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {153BF677-B274-46EE-B45F-8AAD1AD0ADFE}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {F593163C-1AAB-4A15-9C55-F8DE467B2146}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {F593163C-1AAB-4A15-9C55-F8DE467B2146}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {F593163C-1AAB-4A15-9C55-F8DE467B2146}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {F593163C-1AAB-4A15-9C55-F8DE467B2146}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {01643FC8-921C-4460-9E46-0BDAFCA420E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {01643FC8-921C-4460-9E46-0BDAFCA420E5}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {01643FC8-921C-4460-9E46-0BDAFCA420E5}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {01643FC8-921C-4460-9E46-0BDAFCA420E5}.Release|Any CPU.Build.0 = Release|Any CPU 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(NestedProjects) = preSolution 49 | {1029D930-4556-4FE6-BA64-F9E9F9B87A55} = {E65A796D-A9BE-4C66-9A17-C9A8E2BD8347} 50 | {153BF677-B274-46EE-B45F-8AAD1AD0ADFE} = {60037AE3-ACEB-4609-94C1-39B22A64EF73} 51 | {F593163C-1AAB-4A15-9C55-F8DE467B2146} = {E65A796D-A9BE-4C66-9A17-C9A8E2BD8347} 52 | {01643FC8-921C-4460-9E46-0BDAFCA420E5} = {E65A796D-A9BE-4C66-9A17-C9A8E2BD8347} 53 | EndGlobalSection 54 | GlobalSection(ExtensibilityGlobals) = postSolution 55 | SolutionGuid = {DA69B1C3-F6B9-4AF9-859B-D680E03B79F2} 56 | EndGlobalSection 57 | EndGlobal 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # luyao-light-rpc 2 | A lightweight RPC framework that supports AOT. 3 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | :; set -eo pipefail 2 | :; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 3 | :; ${SCRIPT_DIR}/build.sh "$@" 4 | :; exit $? 5 | 6 | @ECHO OFF 7 | powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* 8 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param( 3 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] 4 | [string[]]$BuildArguments 5 | ) 6 | 7 | Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" 8 | 9 | Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } 10 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 11 | 12 | ########################################################################### 13 | # CONFIGURATION 14 | ########################################################################### 15 | 16 | $BuildProjectFile = "$PSScriptRoot\build\_build.csproj" 17 | $TempDirectory = "$PSScriptRoot\\.nuke\temp" 18 | 19 | $DotNetGlobalFile = "$PSScriptRoot\\global.json" 20 | $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" 21 | $DotNetChannel = "STS" 22 | 23 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 24 | $env:DOTNET_NOLOGO = 1 25 | 26 | ########################################################################### 27 | # EXECUTION 28 | ########################################################################### 29 | 30 | function ExecSafe([scriptblock] $cmd) { 31 | & $cmd 32 | if ($LASTEXITCODE) { exit $LASTEXITCODE } 33 | } 34 | 35 | # If dotnet CLI is installed globally and it matches requested version, use for execution 36 | if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` 37 | $(dotnet --version) -and $LASTEXITCODE -eq 0) { 38 | $env:DOTNET_EXE = (Get-Command "dotnet").Path 39 | } 40 | else { 41 | # Download install script 42 | $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" 43 | New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null 44 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 45 | (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) 46 | 47 | # If global.json exists, load expected version 48 | if (Test-Path $DotNetGlobalFile) { 49 | $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) 50 | if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { 51 | $DotNetVersion = $DotNetGlobal.sdk.version 52 | } 53 | } 54 | 55 | # Install by channel or version 56 | $DotNetDirectory = "$TempDirectory\dotnet-win" 57 | if (!(Test-Path variable:DotNetVersion)) { 58 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } 59 | } else { 60 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } 61 | } 62 | $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" 63 | $env:PATH = "$DotNetDirectory;$env:PATH" 64 | } 65 | 66 | Write-Output "Microsoft (R) .NET SDK version $(& $env:DOTNET_EXE --version)" 67 | 68 | if (Test-Path env:NUKE_ENTERPRISE_TOKEN) { 69 | & $env:DOTNET_EXE nuget remove source "nuke-enterprise" > $null 70 | & $env:DOTNET_EXE nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password $env:NUKE_ENTERPRISE_TOKEN > $null 71 | } 72 | 73 | ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } 74 | ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } 75 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bash --version 2>&1 | head -n 1 4 | 5 | set -eo pipefail 6 | SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 7 | 8 | ########################################################################### 9 | # CONFIGURATION 10 | ########################################################################### 11 | 12 | BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj" 13 | TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" 14 | 15 | DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" 16 | DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" 17 | DOTNET_CHANNEL="STS" 18 | 19 | export DOTNET_CLI_TELEMETRY_OPTOUT=1 20 | export DOTNET_NOLOGO=1 21 | 22 | ########################################################################### 23 | # EXECUTION 24 | ########################################################################### 25 | 26 | function FirstJsonValue { 27 | perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" 28 | } 29 | 30 | # If dotnet CLI is installed globally and it matches requested version, use for execution 31 | if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then 32 | export DOTNET_EXE="$(command -v dotnet)" 33 | else 34 | # Download install script 35 | DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" 36 | mkdir -p "$TEMP_DIRECTORY" 37 | curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" 38 | chmod +x "$DOTNET_INSTALL_FILE" 39 | 40 | # If global.json exists, load expected version 41 | if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then 42 | DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") 43 | if [[ "$DOTNET_VERSION" == "" ]]; then 44 | unset DOTNET_VERSION 45 | fi 46 | fi 47 | 48 | # Install by channel or version 49 | DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" 50 | if [[ -z ${DOTNET_VERSION+x} ]]; then 51 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path 52 | else 53 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path 54 | fi 55 | export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" 56 | export PATH="$DOTNET_DIRECTORY:$PATH" 57 | fi 58 | 59 | echo "Microsoft (R) .NET SDK version $("$DOTNET_EXE" --version)" 60 | 61 | if [[ ! -z ${NUKE_ENTERPRISE_TOKEN+x} && "$NUKE_ENTERPRISE_TOKEN" != "" ]]; then 62 | "$DOTNET_EXE" nuget remove source "nuke-enterprise" &>/dev/null || true 63 | "$DOTNET_EXE" nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password "$NUKE_ENTERPRISE_TOKEN" --store-password-in-clear-text &>/dev/null || true 64 | fi 65 | 66 | "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet 67 | "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" 68 | -------------------------------------------------------------------------------- /build/.editorconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderbusy/luyao-light-rpc/bc6f99ddc7ac63562d5e04eee6a8dbed966614c6/build/.editorconfig -------------------------------------------------------------------------------- /build/Build.Paths.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common.IO; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | partial class Build 9 | { 10 | AbsolutePath SourceDirectory => RootDirectory / "src"; 11 | AbsolutePath TestDirectory => RootDirectory / "tests"; 12 | AbsolutePath OutputDirectory => RootDirectory / "output"; 13 | AbsolutePath PackageDirectory => OutputDirectory / "packages"; 14 | } 15 | -------------------------------------------------------------------------------- /build/Build.Publish.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common; 2 | using Nuke.Common.CI.GitHubActions; 3 | using Nuke.Common.Tools.DotNet; 4 | using Nuke.Common.Tools.GitVersion; 5 | 6 | [GitHubActions("Publish NuGet Manual", 7 | GitHubActionsImage.WindowsServer2022, 8 | On = new[] { GitHubActionsTrigger.WorkflowDispatch }, 9 | PublishArtifacts = true, 10 | ImportSecrets = new[] { nameof(NuGetApiKey) }, 11 | InvokedTargets = new[] { nameof(Push) }, 12 | FetchDepth = 0 13 | )] 14 | partial class Build 15 | { 16 | [Parameter("Api key to use when pushing the package")] 17 | readonly string NuGetApiKey = string.Empty; 18 | 19 | [Parameter("NuGet artifact target uri - Defaults to https://api.nuget.org/v3/index.json")] 20 | readonly string PackageSource = "https://api.nuget.org/v3/index.json"; 21 | 22 | Target Pack => _ => _ 23 | .DependsOn(Clean, Compile) 24 | .Produces(PackageDirectory / "*nupkg") 25 | .Executes(() => 26 | { 27 | DotNetTasks.DotNetPack(s => s 28 | .SetProject(Solution) 29 | .SetTreatWarningsAsErrors(true) 30 | .EnableNoBuild() 31 | .SetConfiguration(Configuration) 32 | .SetVersion(GitVersion.FullSemVer) 33 | .SetOutputDirectory(PackageDirectory)); 34 | }); 35 | 36 | Target Push => _ => _ 37 | .Consumes(Pack) 38 | .DependsOn(Pack) 39 | .Requires(() => NuGetApiKey) 40 | .Requires(() => PackageSource) 41 | .Executes(() => 42 | { 43 | DotNetTasks.DotNetNuGetPush(s => s 44 | .SetTargetPath(PackageDirectory / $"*.nupkg") 45 | .SetApiKey(NuGetApiKey) 46 | .SetSource(PackageSource)); 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /build/Build.Test.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common; 2 | using Serilog; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | partial class Build 10 | { 11 | Target Print => _ => _ 12 | .Executes(() => 13 | { 14 | Log.Information("GitVersion.Major = {Value}", GitVersion.Major); 15 | Log.Information("GitVersion.Minor = {Value}", GitVersion.Minor); 16 | Log.Information("GitVersion.Patch = {Value}", GitVersion.Patch); 17 | Log.Information("GitVersion.PreReleaseTag = {Value}", GitVersion.PreReleaseTag); 18 | Log.Information("GitVersion.PreReleaseTagWithDash = {Value}", GitVersion.PreReleaseTagWithDash); 19 | Log.Information("GitVersion.PreReleaseLabel = {Value}", GitVersion.PreReleaseLabel); 20 | Log.Information("GitVersion.PreReleaseLabelWithDash = {Value}", GitVersion.PreReleaseLabelWithDash); 21 | Log.Information("GitVersion.PreReleaseNumber = {Value}", GitVersion.PreReleaseNumber); 22 | Log.Information("GitVersion.WeightedPreReleaseNumber = {Value}", GitVersion.WeightedPreReleaseNumber); 23 | Log.Information("GitVersion.BuildMetaData = {Value}", GitVersion.BuildMetaData); 24 | Log.Information("GitVersion.BuildMetaDataPadded = {Value}", GitVersion.BuildMetaDataPadded); 25 | Log.Information("GitVersion.FullBuildMetaData = {Value}", GitVersion.FullBuildMetaData); 26 | Log.Information("GitVersion.MajorMinorPatch = {Value}", GitVersion.MajorMinorPatch); 27 | Log.Information("GitVersion.SemVer = {Value}", GitVersion.SemVer); 28 | Log.Information("GitVersion.LegacySemVer = {Value}", GitVersion.LegacySemVer); 29 | Log.Information("GitVersion.LegacySemVerPadded = {Value}", GitVersion.LegacySemVerPadded); 30 | Log.Information("GitVersion.AssemblySemVer = {Value}", GitVersion.AssemblySemVer); 31 | Log.Information("GitVersion.AssemblySemFileVer = {Value}", GitVersion.AssemblySemFileVer); 32 | Log.Information("GitVersion.FullSemVer = {Value}", GitVersion.FullSemVer); 33 | Log.Information("GitVersion.InformationalVersion = {Value}", GitVersion.InformationalVersion); 34 | Log.Information("GitVersion.BranchName = {Value}", GitVersion.BranchName); 35 | Log.Information("GitVersion.EscapedBranchName = {Value}", GitVersion.EscapedBranchName); 36 | Log.Information("GitVersion.Sha = {Value}", GitVersion.Sha); 37 | Log.Information("GitVersion.ShortSha = {Value}", GitVersion.ShortSha); 38 | Log.Information("GitVersion.NuGetVersionV2 = {Value}", GitVersion.NuGetVersionV2); 39 | Log.Information("GitVersion.NuGetVersion = {Value}", GitVersion.NuGetVersion); 40 | Log.Information("GitVersion.NuGetPreReleaseTagV2 = {Value}", GitVersion.NuGetPreReleaseTagV2); 41 | Log.Information("GitVersion.NuGetPreReleaseTag = {Value}", GitVersion.NuGetPreReleaseTag); 42 | Log.Information("GitVersion.VersionSourceSha = {Value}", GitVersion.VersionSourceSha); 43 | Log.Information("GitVersion.CommitsSinceVersionSource = {Value}", GitVersion.CommitsSinceVersionSource); 44 | Log.Information("GitVersion.CommitsSinceVersionSourcePadded = {Value}", GitVersion.CommitsSinceVersionSourcePadded); 45 | Log.Information("GitVersion.UncommittedChanges = {Value}", GitVersion.UncommittedChanges); 46 | Log.Information("GitVersion.CommitDate = {Value}", GitVersion.CommitDate); 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /build/Build.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Nuke.Common; 4 | using Nuke.Common.CI; 5 | using Nuke.Common.CI.GitHubActions; 6 | using Nuke.Common.Execution; 7 | using Nuke.Common.IO; 8 | using Nuke.Common.ProjectModel; 9 | using Nuke.Common.Tooling; 10 | using Nuke.Common.Tools.DotNet; 11 | using Nuke.Common.Tools.GitVersion; 12 | using Nuke.Common.Utilities.Collections; 13 | using static Nuke.Common.EnvironmentInfo; 14 | using static Nuke.Common.IO.FileSystemTasks; 15 | using static Nuke.Common.IO.PathConstruction; 16 | using static Nuke.Common.Tools.DotNet.DotNetTasks; 17 | 18 | [ShutdownDotNetAfterServerBuild] 19 | [GitHubActions( 20 | "continuous", 21 | GitHubActionsImage.WindowsServer2022, 22 | On = new[] { GitHubActionsTrigger.Push }, 23 | FetchDepth = 0, 24 | InvokedTargets = new[] { nameof(Compile) })] 25 | partial class Build : NukeBuild 26 | { 27 | /// Support plugins are available for: 28 | /// - JetBrains ReSharper https://nuke.build/resharper 29 | /// - JetBrains Rider https://nuke.build/rider 30 | /// - Microsoft VisualStudio https://nuke.build/visualstudio 31 | /// - Microsoft VSCode https://nuke.build/vscode 32 | 33 | public static int Main() => Execute(x => x.Compile); 34 | 35 | [Solution] readonly Solution Solution; 36 | 37 | [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] 38 | readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; 39 | 40 | [GitVersion(NoFetch = true)] readonly GitVersion GitVersion; 41 | GitHubActions GitHubActions => GitHubActions.Instance; 42 | Target Clean => _ => _ 43 | .Before(Restore) 44 | .Executes(() => 45 | { 46 | SourceDirectory.GlobDirectories("**/bin", "**/obj").ForEach(dir => dir.DeleteDirectory()); 47 | TestDirectory.GlobDirectories("**/bin", "**/obj").ForEach(dir => dir.DeleteDirectory()); 48 | OutputDirectory.CreateOrCleanDirectory(); 49 | }); 50 | 51 | Target Restore => _ => _ 52 | .Executes(() => 53 | { 54 | DotNetRestore(dotNetRestoreSettings => dotNetRestoreSettings 55 | .SetProjectFile(Solution)); 56 | }); 57 | 58 | Target Compile => _ => _ 59 | .DependsOn(Restore) 60 | .Executes(() => 61 | { 62 | DotNetBuild(dotNetBuildSettings => dotNetBuildSettings 63 | .SetProjectFile(Solution) 64 | .SetConfiguration(Configuration) 65 | .SetVersion(GitVersion.FullSemVer) 66 | .EnableNoRestore()); 67 | }); 68 | } 69 | -------------------------------------------------------------------------------- /build/Configuration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | using Nuke.Common.Tooling; 5 | 6 | [TypeConverter(typeof(TypeConverter))] 7 | public class Configuration : Enumeration 8 | { 9 | public static Configuration Debug = new Configuration { Value = nameof(Debug) }; 10 | public static Configuration Release = new Configuration { Value = nameof(Release) }; 11 | 12 | public static implicit operator string(Configuration configuration) 13 | { 14 | return configuration.Value; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /build/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build/_build.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 7 | CS0649;CS0169;CA1050;CA1822;CA2211;IDE1006 8 | .. 9 | .. 10 | 1 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /build/_build.csproj.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | DO_NOT_SHOW 3 | DO_NOT_SHOW 4 | DO_NOT_SHOW 5 | DO_NOT_SHOW 6 | DO_NOT_SHOW 7 | Implicit 8 | Implicit 9 | ExpressionBody 10 | 0 11 | NEXT_LINE 12 | True 13 | False 14 | 120 15 | IF_OWNER_IS_SINGLE_LINE 16 | WRAP_IF_LONG 17 | False 18 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> 19 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> 20 | True 21 | True 22 | True 23 | True 24 | True 25 | True 26 | True 27 | True 28 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.Client/LuYao.LightRpc.Demo.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.Client/Program.cs: -------------------------------------------------------------------------------- 1 | // See https://aka.ms/new-console-template for more information 2 | 3 | using LuYao.LightRpc.Demo.Client; 4 | using System.Diagnostics; 5 | 6 | //var endpoint = "http://localhost:5297/";// in aliyun 7 | //var endpoint = "http://localhost:5094/";//minimal api 8 | var endpoint = "http://localhost:5000/";//release 9 | var client = new RpcClient(endpoint); 10 | var sum = await client.SumAsync(1, 2); 11 | Console.WriteLine($"sum is : {sum}"); 12 | var st = new Stopwatch(); 13 | st.Start(); 14 | for (int i = 0; i < 1_0000; i++) 15 | { 16 | await client.SumAsync(1, 2); 17 | } 18 | st.Stop(); 19 | Console.WriteLine($"10k times sum cost {st.ElapsedMilliseconds} ms"); 20 | Console.ReadLine(); -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.Client/RpcClient.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Attributes; 2 | 3 | namespace LuYao.LightRpc.Demo.Client; 4 | 5 | [RpcClientAgent] 6 | public partial class RpcClient : LuYao.LightRpc.RpcClient 7 | { 8 | public RpcClient(string endpoint) : base(new RpcTunnel(endpoint)) 9 | { 10 | 11 | } 12 | 13 | [RemoteAction("test/sum")] 14 | public partial Task SumAsync(int a, int b); 15 | } 16 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.Client/RpcTunnel.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Http; 2 | 3 | namespace LuYao.LightRpc.Demo.Client; 4 | 5 | public class RpcTunnel : HttpJsonRpcTunnel 6 | { 7 | private static readonly SocketsHttpHandler _handler = new SocketsHttpHandler(); 8 | public RpcTunnel(string endpoint) : base(endpoint, new NewtonsoftDataConverter()) 9 | { 10 | } 11 | 12 | protected override HttpClient CreateHttpClient() 13 | { 14 | return new HttpClient(_handler, false); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.Client/rd.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.MinimalApi/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-ef": { 6 | "version": "8.0.8", 7 | "commands": [ 8 | "dotnet-ef" 9 | ], 10 | "rollForward": false 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.MinimalApi/LuYao.LightRpc.Demo.MinimalApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.MinimalApi/MainServer.cs: -------------------------------------------------------------------------------- 1 | namespace LuYao.LightRpc.Demo.MinimalApi; 2 | 3 | public class MainServer : MainServer 4 | { 5 | public MainServer() : base(new NewtonsoftDataConverter()) 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.MinimalApi/Program.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc; 2 | using LuYao.LightRpc.Demo; 3 | using LuYao.LightRpc.Demo.MinimalApi; 4 | using Microsoft.AspNetCore.Mvc; 5 | 6 | var builder = WebApplication.CreateSlimBuilder(args); 7 | 8 | // Add services to the container. 9 | builder.Services.AddSingleton(); 10 | 11 | var app = builder.Build(); 12 | 13 | app.Map("/", async ( 14 | HttpContext context, 15 | [FromQuery(Name = "cmd")] string? action, 16 | [FromServices] MainServer server 17 | ) => 18 | { 19 | if (action is null) action = string.Empty; 20 | string json = string.Empty; 21 | var request = context.Request; 22 | using (var reader = new StreamReader(request.Body)) 23 | { 24 | json = await reader.ReadToEndAsync(); 25 | } 26 | var input = server.DataConverter.Deserialize(json) ?? EmptyDataPackage.Instance; 27 | var result = await server.InvokeAsync(action, input); 28 | var ret = new RpcHttpResult(result); 29 | return ret; 30 | }); 31 | 32 | app.Run(); 33 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.MinimalApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "http": { 5 | "commandName": "Project", 6 | "dotnetRunMessages": true, 7 | "launchBrowser": true, 8 | "applicationUrl": "http://localhost:5094", 9 | "environmentVariables": { 10 | "ASPNETCORE_ENVIRONMENT": "Development" 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.MinimalApi/RpcHttpResult.cs: -------------------------------------------------------------------------------- 1 | namespace LuYao.LightRpc.Demo.MinimalApi; 2 | 3 | public class RpcHttpResult : IResult 4 | { 5 | public RpcResult RpcResult { get; } 6 | 7 | public RpcHttpResult(RpcResult rpcResult) 8 | { 9 | RpcResult = rpcResult; 10 | } 11 | 12 | public async Task ExecuteAsync(HttpContext httpContext) 13 | { 14 | var response = httpContext.Response; 15 | response.StatusCode = (int)this.RpcResult.Code; 16 | if (this.RpcResult.Code == RpcResultCode.Ok) 17 | { 18 | if (this.RpcResult.Data is not null) 19 | { 20 | var server = httpContext.RequestServices.GetRequiredService(); 21 | var output = server.DataConverter.Serialize(this.RpcResult.Data); 22 | await response.WriteAsync(output); 23 | } 24 | } 25 | else 26 | { 27 | if (!string.IsNullOrWhiteSpace(this.RpcResult.Message)) 28 | { 29 | await response.WriteAsync(this.RpcResult.Message); 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.MinimalApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo.MinimalApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo/LuYao.LightRpc.Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 10.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo/MainServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LuYao.LightRpc.Demo; 6 | 7 | public class MainServer : RpcServer 8 | { 9 | public MainServer(IDataConverter dataConverter) : base(dataConverter) 10 | { 11 | this.Register(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /demo/LuYao.LightRpc.Demo/TestController.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Attributes; 2 | using System; 3 | 4 | namespace LuYao.LightRpc.Demo; 5 | 6 | [Controller] 7 | public partial class TestController 8 | { 9 | public int Sum(int a, int b) 10 | { 11 | return a + b; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAliyun/LuYao.LightRpc.Demo.Functions.InAliyun.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAliyun/MainServer.cs: -------------------------------------------------------------------------------- 1 | namespace LuYao.LightRpc.Demo.Functions.InAliyun; 2 | 3 | public class MainServer : MainServer 4 | { 5 | public MainServer() : base(new NewtonsoftDataConverter()) 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAliyun/Program.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc; 2 | using LuYao.LightRpc.Demo; 3 | using LuYao.LightRpc.Demo.Functions.InAliyun; 4 | using Microsoft.AspNetCore.Mvc; 5 | 6 | var builder = WebApplication.CreateBuilder(args); 7 | 8 | // Add services to the container. 9 | builder.Services.AddSingleton(); 10 | 11 | var app = builder.Build(); 12 | 13 | app.MapPost("/initialize", () => 14 | { 15 | return "initialize succ"; 16 | }); 17 | 18 | app.MapPost("/invoke", ([FromHeader(Name = "x-fc-request-id")] string requestId) => 19 | { 20 | return "get reqeuestId from header:" + requestId; 21 | }); 22 | 23 | app.Map("/", async ( 24 | [FromQuery(Name = "cmd")] string? action, 25 | [FromBody] dynamic? body, 26 | [FromServices] MainServer server 27 | ) => 28 | { 29 | if (action is null) action = string.Empty; 30 | string json = body is not null ? body.ToString() : string.Empty; 31 | var input = server.DataConverter.Deserialize(json) ?? EmptyDataPackage.Instance; 32 | var result = await server.InvokeAsync(action, input); 33 | var ret = new RpcHttpResult(result); 34 | return ret; 35 | }); 36 | 37 | app.Run(); 38 | -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAliyun/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:38408", 8 | "sslPort": 0 9 | } 10 | }, 11 | "profiles": { 12 | "LuYao.LightRpc.Demo.Functions.InAliyun": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "applicationUrl": "http://localhost:5297", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAliyun/RpcHttpResult.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace LuYao.LightRpc.Demo.Functions.InAliyun; 4 | 5 | public class RpcHttpResult : IResult 6 | { 7 | public RpcResult RpcResult { get; } 8 | 9 | public RpcHttpResult(RpcResult rpcResult) 10 | { 11 | RpcResult = rpcResult; 12 | } 13 | 14 | public async Task ExecuteAsync(HttpContext httpContext) 15 | { 16 | var response = httpContext.Response; 17 | response.StatusCode = (int)this.RpcResult.Code; 18 | if (this.RpcResult.Code == RpcResultCode.Ok) 19 | { 20 | if (this.RpcResult.Data is not null) 21 | { 22 | var server = httpContext.RequestServices.GetRequiredService(); 23 | var output = server.DataConverter.Serialize(this.RpcResult.Data); 24 | await response.WriteAsync(output); 25 | } 26 | } 27 | else 28 | { 29 | if (!string.IsNullOrWhiteSpace(this.RpcResult.Message)) 30 | { 31 | await response.WriteAsync(this.RpcResult.Message); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAliyun/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAliyun/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAzure/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAzure/Handler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.Functions.Worker; 2 | using Microsoft.Azure.Functions.Worker.Http; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace LuYao.LightRpc.Demo.Functions.InAzure; 6 | 7 | public class Handler 8 | { 9 | private readonly ILogger _logger; 10 | private readonly MainServer _mainServer; 11 | 12 | public Handler(ILogger logger, MainServer mainServer) 13 | { 14 | _logger = logger; 15 | _mainServer = mainServer; 16 | } 17 | 18 | [Function("Handler")] 19 | public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req) 20 | { 21 | string action = req.Query["cmd"]?.ToString() ?? String.Empty; 22 | string json = await req.ReadAsStringAsync() ?? string.Empty; 23 | var response = req.CreateResponse(); 24 | response.Headers.Add("Content-Type", "application/json"); 25 | var input = this._mainServer.DataConverter.Deserialize(json) ?? EmptyDataPackage.Instance; 26 | var result = await this._mainServer.InvokeAsync(action, input); 27 | response.StatusCode = System.Net.HttpStatusCode.OK; 28 | if (result != null) 29 | { 30 | response.StatusCode = (System.Net.HttpStatusCode)result.Code; 31 | if (result.Code == RpcResultCode.Ok) 32 | { 33 | if (result.Data != null) 34 | { 35 | var output = _mainServer.DataConverter.Serialize(result.Data); 36 | await response.WriteStringAsync(output); 37 | } 38 | } 39 | else 40 | { 41 | if (!string.IsNullOrWhiteSpace(result.Message)) 42 | { 43 | await response.WriteStringAsync(result.Message); 44 | } 45 | } 46 | } 47 | 48 | return response; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAzure/LuYao.LightRpc.Demo.Functions.InAzure.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | v4 5 | Exe 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | PreserveNewest 26 | Never 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAzure/MainServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace LuYao.LightRpc.Demo.Functions.InAzure; 8 | 9 | public class MainServer : MainServer 10 | { 11 | public MainServer() : base(new NewtonsoftDataConverter()) 12 | { 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAzure/Program.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Demo; 2 | using LuYao.LightRpc.Demo.Functions.InAzure; 3 | using Microsoft.Azure.Functions.Worker; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | 7 | var host = new HostBuilder() 8 | .ConfigureFunctionsWorkerDefaults() 9 | .ConfigureServices(services => 10 | { 11 | services.AddApplicationInsightsTelemetryWorkerService(); 12 | services.ConfigureFunctionsApplicationInsights(); 13 | services.AddSingleton(); 14 | }) 15 | .Build(); 16 | 17 | host.Run(); 18 | -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAzure/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "LuYao.LightRpc.Demo.Functions.InAzure": { 4 | "commandName": "Project", 5 | "commandLineArgs": "--port 7073", 6 | "launchBrowser": false 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAzure/Properties/serviceDependencies.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "appInsights1": { 4 | "type": "appInsights" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAzure/Properties/serviceDependencies.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "appInsights1": { 4 | "type": "appInsights.sdk" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /demo/fc/LuYao.LightRpc.Demo.Functions.InAzure/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "excludedTypes": "Request" 8 | }, 9 | "enableLiveMetricsFilters": true 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/LuYao.LightRpc.Newtonsoft/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.Newtonsoft/JObjectDataPackage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System.Collections.Generic; 3 | 4 | namespace LuYao.LightRpc; 5 | 6 | internal class JObjectDataPackage : SortedDictionary, IDataPackage 7 | { 8 | public void Set(string key, T? value) 9 | { 10 | if (value is null) 11 | { 12 | this.Remove(key); 13 | } 14 | else 15 | { 16 | this[key] = JToken.FromObject(value); 17 | } 18 | } 19 | 20 | public bool TryGetValue(string key, out T? value) 21 | { 22 | if (this.TryGetValue(key, out var token)) 23 | { 24 | value = token.ToObject(); 25 | return true; 26 | } 27 | value = default; 28 | return false; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.Newtonsoft/LuYao.LightRpc.Newtonsoft.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net45;net461;netstandard2.0;netstandard2.1;net6.0;net8.0 4 | enable 5 | latest 6 | LuYao.LightRpc 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.Newtonsoft/NewtonsoftDataConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace LuYao.LightRpc; 5 | 6 | public class NewtonsoftDataConverter : IDataConverter 7 | { 8 | public IDataPackage CreatePackage() => new JObjectDataPackage(); 9 | 10 | public IDataPackage? Deserialize(string? data) 11 | { 12 | if (string.IsNullOrWhiteSpace(data)) return EmptyDataPackage.Instance; 13 | return JsonConvert.DeserializeObject(data!); 14 | } 15 | 16 | public string Serialize(IDataPackage data) => JsonConvert.SerializeObject(data); 17 | } 18 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/CSharpStringBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LuYao.LightRpc; 6 | 7 | public class CSharpStringBuilder 8 | { 9 | private readonly StringBuilder _builder = new StringBuilder(); 10 | 11 | public override string ToString() => _builder.ToString(); 12 | public void AppendLine(string value) 13 | { 14 | _builder.Append(this._tabString); 15 | _builder.AppendLine(value); 16 | } 17 | public void AppendLine() => _builder.AppendLine(); 18 | private string _tabString = string.Empty; 19 | private int _tabs; 20 | private void SetTabs() 21 | { 22 | if (_tabs > 0) 23 | { 24 | _tabString = new string(' ', _tabs); 25 | } 26 | else 27 | { 28 | _tabString = string.Empty; 29 | } 30 | } 31 | 32 | private void AddTab() 33 | { 34 | _tabs += 4; 35 | SetTabs(); 36 | } 37 | private void RemoveTab() 38 | { 39 | _tabs -= 4; 40 | if (_tabs < 0) _tabs = 0; 41 | SetTabs(); 42 | } 43 | public IDisposable Tab() 44 | { 45 | this.AddTab(); 46 | return new DisposeAction(() => this.RemoveTab()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/ControllerDescriptorGenerator.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Models.BuildControllerDescriptor; 2 | using LuYao.LightRpc.Renders.BuildControllerDescriptor; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp.Syntax; 5 | using Microsoft.CodeAnalysis.Text; 6 | using System.Collections.Immutable; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | 11 | namespace LuYao.LightRpc; 12 | 13 | [Generator] 14 | public class ControllerDescriptorGenerator : IIncrementalGenerator 15 | { 16 | public void Initialize(IncrementalGeneratorInitializationContext context) 17 | { 18 | var classDeclarations = context.SyntaxProvider.ForAttributeWithMetadataName( 19 | "LuYao.LightRpc.Attributes.ControllerAttribute", 20 | predicate: static (node, cancel) => node is ClassDeclarationSyntax, 21 | transform: static (ctx, cancel) => GetSemanticTargetForGeneration(ctx, cancel) 22 | ).Where(m => m.Item1 is not null && m.Item2 is not null); 23 | 24 | 25 | var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect()); 26 | context.RegisterSourceOutput(compilationAndClasses, Execute); 27 | } 28 | 29 | private static (ClassDeclarationSyntax, AttributeData) GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken ct) 30 | { 31 | if (context.TargetNode is not ClassDeclarationSyntax classDeclaration) 32 | { 33 | return (null, null); 34 | } 35 | AttributeData attribute = context.Attributes.FirstOrDefault(a => a.AttributeClass?.Name == "ControllerAttribute"); 36 | 37 | return (classDeclaration, attribute); 38 | } 39 | 40 | private void Execute(SourceProductionContext context, (Compilation Left, ImmutableArray<(ClassDeclarationSyntax, AttributeData)> Right) args) 41 | { 42 | if (!args.Right.Any()) return; 43 | var compilation = args.Left; 44 | foreach ((ClassDeclarationSyntax Syntax, AttributeData Attr) item in args.Right) 45 | { 46 | if (item.Syntax is null || item.Attr is null) continue; 47 | 48 | 49 | string className = item.Syntax.Identifier.ValueText; 50 | string ns = Helpers.GetNamespace(item.Syntax); 51 | 52 | var semanticModel = compilation.GetSemanticModel(item.Syntax.SyntaxTree); 53 | var model = new ControllerModel 54 | { 55 | Name = className, 56 | FullName = $"{ns}.{className}", 57 | Namespace = ns, 58 | }; 59 | 60 | BuildModel(context, item, semanticModel, model); 61 | 62 | var render = new ControllerDescriptorRender(model); 63 | var code = render.Render(); 64 | 65 | var sourceText = SourceText.From(code, Encoding.UTF8); 66 | context.AddSource($"{ns}.{className}.g.cs", sourceText); 67 | } 68 | } 69 | 70 | private static void BuildModel(SourceProductionContext context, (ClassDeclarationSyntax Syntax, AttributeData Attr) item, SemanticModel semanticModel, ControllerModel model) 71 | { 72 | foreach (var n in item.Attr.NamedArguments) 73 | { 74 | //switch (n.Key) 75 | //{ 76 | // case "Controller": 77 | // model.Controller = Convert.ToString(n.Value.Value); 78 | // break; 79 | // case "Area": 80 | // model.Area = Convert.ToString(n.Value.Value); 81 | // break; 82 | //} 83 | } 84 | 85 | var methods = item.Syntax.DescendantNodes().OfType().ToList(); 86 | if (methods.Count > 0) 87 | { 88 | foreach (var m in methods) 89 | { 90 | var methodSymbol = semanticModel.GetDeclaredSymbol(m) as IMethodSymbol; 91 | if (IsIgnore(methodSymbol)) continue; 92 | var action = new ActionModel 93 | { 94 | Name = methodSymbol.Name, 95 | ReturnsVoid = methodSymbol.ReturnsVoid, 96 | HasReturnValue = !methodSymbol.ReturnsVoid 97 | }; 98 | model.Actions.Add(action); 99 | // 获取方法的返回类型 100 | var returnType = methodSymbol.ReturnType; 101 | // 判断返回类型是否是 Task 或 Task<> 102 | bool returnsTask = returnType.Name == "Task" && (returnType.ContainingNamespace.ToString() == "System.Threading.Tasks"); 103 | 104 | // 这个方法是否可以被 await 105 | action.IsAwaitable = returnsTask; 106 | 107 | if (action.IsAwaitable) 108 | { 109 | // 检查返回类型是否是 Task 110 | bool returnsTaskWithoutResult = returnType.Name == "Task" && 111 | returnType.ContainingNamespace.ToString() == "System.Threading.Tasks"; 112 | 113 | // 检查返回类型是否是 Task 114 | bool returnsTaskWithResult = returnType is INamedTypeSymbol namedTypeSymbol && 115 | namedTypeSymbol.Name == "Task" && 116 | namedTypeSymbol.TypeArguments.Length == 1 && 117 | returnType.ContainingNamespace.ToString() == "System.Threading.Tasks"; 118 | 119 | // 检查自定义类型是否有 GetAwaiter 并且 GetAwaiter().GetResult() 有返回值 120 | bool customAwaitableReturnsValue = false; 121 | var getAwaiterMethod = returnType.GetMembers("GetAwaiter").OfType().FirstOrDefault(); 122 | 123 | if (getAwaiterMethod != null && getAwaiterMethod.Parameters.Length == 0) 124 | { 125 | var awaiterReturnType = getAwaiterMethod.ReturnType; 126 | var getResultMethod = awaiterReturnType.GetMembers("GetResult").OfType().FirstOrDefault(); 127 | 128 | if (getResultMethod != null) 129 | { 130 | // 检查 GetResult() 是否有返回值 131 | customAwaitableReturnsValue = !getResultMethod.ReturnsVoid; 132 | } 133 | } 134 | 135 | // 判断 await 后是否有返回值 136 | action.HasReturnValue = returnsTaskWithResult || customAwaitableReturnsValue; 137 | } 138 | 139 | var paras = m.ParameterList.Parameters; 140 | foreach (var p in paras) 141 | { 142 | if (p.Type is null) continue; 143 | var typeInfo = semanticModel.GetTypeInfo(p.Type); 144 | var ap = new ActionParameterModel 145 | { 146 | Name = p.Identifier.ValueText, 147 | Type = typeInfo.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) 148 | }; 149 | action.Parameters.Add(ap); 150 | } 151 | } 152 | } 153 | } 154 | 155 | private static bool IsIgnore(IMethodSymbol methodSymbol) 156 | { 157 | if (methodSymbol is null) return true; 158 | var accessibility = methodSymbol.DeclaredAccessibility; 159 | if (accessibility != Accessibility.Public) return true; 160 | if (methodSymbol.IsStatic) return true; 161 | if (methodSymbol.IsOverride) return true; 162 | foreach (var attr in methodSymbol.GetAttributes()) 163 | { 164 | if (attr.AttributeClass.Name == "IgnoreAttribute") 165 | { 166 | return true; 167 | } 168 | } 169 | return false; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/DisposeAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LuYao.LightRpc; 4 | 5 | /// 6 | /// This class can be used to provide an action when 7 | /// Dipose method is called. 8 | /// 9 | public class DisposeAction : IDisposable 10 | { 11 | private readonly Action _action; 12 | 13 | /// 14 | /// Creates a new object. 15 | /// 16 | /// Action to be executed when this object is disposed. 17 | public DisposeAction(Action action) 18 | { 19 | if (action == null) 20 | throw new ArgumentNullException(nameof(action)); 21 | 22 | _action = action; 23 | } 24 | 25 | public void Dispose() 26 | { 27 | _action(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/Helpers.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace LuYao.LightRpc; 5 | 6 | internal static class Helpers 7 | { 8 | public static string GetNamespace(BaseTypeDeclarationSyntax syntax) 9 | { 10 | // If we don't have a namespace at all we'll return an empty string 11 | // This accounts for the "default namespace" case 12 | string nameSpace = string.Empty; 13 | 14 | // Get the containing syntax node for the type declaration 15 | // (could be a nested type, for example) 16 | SyntaxNode potentialNamespaceParent = syntax.Parent; 17 | 18 | // Keep moving "out" of nested classes etc until we get to a namespace 19 | // or until we run out of parents 20 | while (potentialNamespaceParent != null && 21 | potentialNamespaceParent is not NamespaceDeclarationSyntax 22 | && potentialNamespaceParent is not FileScopedNamespaceDeclarationSyntax) 23 | { 24 | potentialNamespaceParent = potentialNamespaceParent.Parent; 25 | } 26 | 27 | // Build up the final namespace by looping until we no longer have a namespace declaration 28 | if (potentialNamespaceParent is BaseNamespaceDeclarationSyntax namespaceParent) 29 | { 30 | // We have a namespace. Use that as the type 31 | nameSpace = namespaceParent.Name.ToString(); 32 | 33 | // Keep moving "out" of the namespace declarations until we 34 | // run out of nested namespace declarations 35 | while (true) 36 | { 37 | if (namespaceParent.Parent is not NamespaceDeclarationSyntax parent) 38 | { 39 | break; 40 | } 41 | 42 | // Add the outer namespace as a prefix to the final namespace 43 | nameSpace = $"{namespaceParent.Name}.{nameSpace}"; 44 | namespaceParent = parent; 45 | } 46 | } 47 | 48 | // return the final namespace 49 | return nameSpace; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/LuYao.LightRpc.SourceGenerators.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | LuYao.LightRpc 6 | 10.0 7 | true 8 | false 9 | $(NoWarn);NU5128 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/Models/BuildControllerDescriptor/ActionModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LuYao.LightRpc.Models.BuildControllerDescriptor; 6 | 7 | public class ActionModel 8 | { 9 | public string Name { get; set; } 10 | public string Path { get; set; } 11 | public bool ReturnsVoid { get; set; } 12 | public bool IsAwaitable { get; set; } 13 | public bool HasReturnValue { get; set; } 14 | public List Parameters { get; } = new List(); 15 | } 16 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/Models/BuildControllerDescriptor/ActionParameterModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LuYao.LightRpc.Models.BuildControllerDescriptor; 6 | 7 | public class ActionParameterModel 8 | { 9 | public string Name { get; set; } 10 | public string Type { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/Models/BuildControllerDescriptor/ControllerModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LuYao.LightRpc.Models.BuildControllerDescriptor; 6 | 7 | public class ControllerModel 8 | { 9 | public string Namespace { get; set; } 10 | public string Name { get; set; } 11 | public string FullName { get; set; } 12 | 13 | public List Actions { get; } = new List(); 14 | 15 | public string GetActionPath(ActionModel action) 16 | { 17 | var parts = new List(); 18 | var n = this.Name; 19 | 20 | if (n.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)) 21 | { 22 | n = n.Substring(0, n.Length - "Controller".Length); 23 | } 24 | if (n.EndsWith("Service", StringComparison.OrdinalIgnoreCase)) 25 | { 26 | n = n.Substring(0, n.Length - "Service".Length); 27 | } 28 | parts.Add(n); 29 | 30 | n = action.Name; 31 | if (n.EndsWith("Async", StringComparison.OrdinalIgnoreCase)) 32 | { 33 | n = n.Substring(0, n.Length - "Async".Length); 34 | } 35 | parts.Add(n); 36 | 37 | return string.Join("/", parts).ToLowerInvariant(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/Models/BuildRpcClientAgent/ActionModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace LuYao.LightRpc.Models.BuildRpcClientAgent; 7 | 8 | public class ActionModel 9 | { 10 | public string Name { get; set; } 11 | public string Command { get; set; } 12 | public bool ReturnsVoid { get; set; } 13 | public bool IsAwaitable { get; set; } 14 | public bool HasReturnValue { get; set; } 15 | public string ReturnType { get; set; } 16 | public string ReturnDataType { get; set; } 17 | public List Parameters { get; } = new List(); 18 | public List TypeParameters { get; } = new List(); 19 | public bool IsGenericMethod => this.TypeParameters.Any(); 20 | public string Accessibility { get; set; } 21 | } 22 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/Models/BuildRpcClientAgent/ActionParameterModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LuYao.LightRpc.Models.BuildRpcClientAgent; 6 | 7 | public class ActionParameterModel 8 | { 9 | public string Name { get; set; } 10 | public string Type { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/Models/BuildRpcClientAgent/ActionTypeParameterModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LuYao.LightRpc.Models.BuildRpcClientAgent; 6 | 7 | public class ActionTypeParameterModel : ActionParameterModel 8 | { 9 | public List ConstraintTypes { get; } = new List(); 10 | public bool HasReferenceTypeConstraint { get; set; } 11 | public bool HasValueTypeConstraint { get; set; } 12 | public bool HasConstructorConstraint { get; set; } 13 | public bool HasUnmanagedTypeConstraint { get; set; } 14 | public bool HasConstraint => ConstraintTypes.Count > 0 || HasReferenceTypeConstraint || HasValueTypeConstraint || HasConstructorConstraint || HasUnmanagedTypeConstraint; 15 | } 16 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/Models/BuildRpcClientAgent/ClientModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LuYao.LightRpc.Models.BuildRpcClientAgent; 6 | 7 | public class ClientModel 8 | { 9 | public string Namespace { get; set; } 10 | public string Name { get; set; } 11 | public string FullName { get; set; } 12 | public List Type { get; } = new List(); 13 | public List Actions { get; } = new List(); 14 | } 15 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/Renders/BuildControllerDescriptor/ControllerDescriptorRender.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Models.BuildControllerDescriptor; 2 | using System; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Xml.Serialization; 7 | 8 | namespace LuYao.LightRpc.Renders.BuildControllerDescriptor; 9 | 10 | public class ControllerDescriptorRender 11 | { 12 | public ControllerModel Controller { get; } 13 | 14 | public ControllerDescriptorRender(ControllerModel controller) 15 | { 16 | Controller = controller; 17 | } 18 | public string Render() 19 | { 20 | var sb = new CSharpStringBuilder(); 21 | sb.AppendLine("using System;"); 22 | sb.AppendLine("using System.Collections.Generic;"); 23 | sb.AppendLine("using System.Threading.Tasks;"); 24 | sb.AppendLine("using LuYao.LightRpc;"); 25 | sb.AppendLine("using LuYao.LightRpc.Attributes;"); 26 | sb.AppendLine("using LuYao.LightRpc.Descriptors;"); 27 | sb.AppendLine(); 28 | sb.AppendLine($"namespace {this.Controller.Namespace}"); 29 | sb.AppendLine("{"); 30 | using (sb.Tab()) BuildClass(sb); 31 | sb.AppendLine("}"); 32 | return sb.ToString(); 33 | } 34 | 35 | private void BuildClass(CSharpStringBuilder sb) 36 | { 37 | sb.AppendLine($"[{this.Controller.Name}.{this.Controller.Name}DescriptorAttribute]"); 38 | sb.AppendLine($"partial class {this.Controller.Name}"); 39 | sb.AppendLine("{"); 40 | using (sb.Tab()) 41 | { 42 | this.WriteDescriptorAttribute(sb); 43 | //WriteOutput 44 | this.WriteDebugOutput(sb); 45 | } 46 | sb.AppendLine("}"); 47 | } 48 | 49 | private void WriteDescriptorAttribute(CSharpStringBuilder sb) 50 | { 51 | sb.AppendLine($"internal class {this.Controller.Name}DescriptorAttribute : ControllerDescriptorAttribute"); 52 | sb.AppendLine("{"); 53 | using (sb.Tab()) 54 | { 55 | sb.AppendLine($"public override IEnumerable GetActionDescriptors()"); 56 | sb.AppendLine("{"); 57 | using (sb.Tab()) 58 | { 59 | if (this.Controller.Actions.Count > 0) 60 | { 61 | foreach (var action in this.Controller.Actions) 62 | { 63 | sb.AppendLine($"yield return new ActionDescriptor<{this.Controller.Name}>(\"{this.Controller.GetActionPath(action)}\",{action.Name});"); 64 | } 65 | } 66 | else 67 | { 68 | sb.AppendLine("yield break;"); 69 | } 70 | } 71 | sb.AppendLine("}"); 72 | 73 | if (this.Controller.Actions.Count > 0) 74 | { 75 | foreach (var action in this.Controller.Actions) 76 | { 77 | sb.AppendLine($"private static {(action.IsAwaitable ? "async " : "")}Task {action.Name}({this.Controller.Name} controller, InvokeContext context)"); 78 | sb.AppendLine("{"); 79 | using (sb.Tab()) 80 | { 81 | if (action.Parameters.Count > 0) 82 | { 83 | //拷贝变量 84 | sb.AppendLine($"var dict = context.Params;"); 85 | //解析参数 86 | foreach (var parameter in action.Parameters) 87 | { 88 | sb.AppendLine($"{parameter.Type} _{parameter.Name} = default;"); 89 | sb.AppendLine($"if (dict.TryGetValue<{parameter.Type}>(\"{parameter.Name}\", out var v_{parameter.Name}))"); 90 | sb.AppendLine("{"); 91 | using (sb.Tab()) sb.AppendLine($"_{parameter.Name} = v_{parameter.Name};"); 92 | sb.AppendLine("}"); 93 | } 94 | } 95 | var args = string.Join(", ", action.Parameters.Select(p => $"_{p.Name}")); 96 | 97 | //函数调用 98 | if (action.IsAwaitable) 99 | { 100 | if (action.HasReturnValue) 101 | { 102 | sb.AppendLine($"var result = await controller.{action.Name}({args});"); 103 | sb.AppendLine($"context.SetResult(result);"); 104 | } 105 | else 106 | { 107 | sb.AppendLine($"await controller.{action.Name}({args});"); 108 | } 109 | } 110 | else 111 | { 112 | if (action.HasReturnValue) 113 | { 114 | sb.AppendLine($"var result = controller.{action.Name}({args});"); 115 | sb.AppendLine($"context.SetResult(result);"); 116 | } 117 | else 118 | { 119 | sb.AppendLine($"controller.{action.Name}({args});"); 120 | } 121 | } 122 | if (!action.IsAwaitable) 123 | { 124 | sb.AppendLine($"return Task.CompletedTask;"); 125 | } 126 | } 127 | sb.AppendLine("}"); 128 | } 129 | } 130 | } 131 | sb.AppendLine("}"); 132 | } 133 | 134 | private void WriteDebugOutput(CSharpStringBuilder sb) 135 | { 136 | #if DEBUG 137 | var xmlSerializer = new XmlSerializer(typeof(ControllerModel)); 138 | string output = string.Empty; 139 | using (var stringWriter = new StringWriter()) 140 | { 141 | xmlSerializer.Serialize(stringWriter, this.Controller); 142 | string xmlString = stringWriter.ToString(); 143 | var bytes = Encoding.UTF8.GetBytes(xmlString); 144 | output = Convert.ToBase64String(bytes); 145 | } 146 | sb.AppendLine("public static String Output()"); 147 | sb.AppendLine("{"); 148 | using (sb.Tab()) sb.AppendLine($"return \"{output}\";"); 149 | sb.AppendLine("}"); 150 | #endif 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/Renders/BuildRpcClientAgent/RpcClientAgentRender.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Models.BuildControllerDescriptor; 2 | using LuYao.LightRpc.Models.BuildRpcClientAgent; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Xml.Serialization; 9 | 10 | namespace LuYao.LightRpc.Renders.BuildRpcClientAgent; 11 | 12 | public class RpcClientAgentRender 13 | { 14 | public ClientModel Client { get; } 15 | 16 | public RpcClientAgentRender(ClientModel client) 17 | { 18 | Client = client; 19 | } 20 | public string Render() 21 | { 22 | var sb = new CSharpStringBuilder(); 23 | sb.AppendLine("using System;"); 24 | sb.AppendLine("using System.Collections.Generic;"); 25 | sb.AppendLine("using System.Threading.Tasks;"); 26 | sb.AppendLine("using LuYao.LightRpc;"); 27 | sb.AppendLine("using LuYao.LightRpc.Attributes;"); 28 | sb.AppendLine("using LuYao.LightRpc.Descriptors;"); 29 | sb.AppendLine(); 30 | sb.AppendLine($"namespace {this.Client.Namespace}"); 31 | sb.AppendLine("{"); 32 | using (sb.Tab()) BuildClass(sb); 33 | sb.AppendLine("}"); 34 | return sb.ToString(); 35 | } 36 | 37 | private void BuildClass(CSharpStringBuilder sb) 38 | { 39 | if (this.Client.Type.Count == 0) 40 | { 41 | sb.AppendLine($"partial class {this.Client.Name}"); 42 | } 43 | else 44 | { 45 | sb.AppendLine($"partial class {this.Client.Name}<{string.Join(",", this.Client.Type)}>"); 46 | } 47 | sb.AppendLine("{"); 48 | using (sb.Tab()) 49 | { 50 | this.WriteAgent(sb); 51 | this.WriteDebugOutput(sb); 52 | } 53 | sb.AppendLine("}"); 54 | } 55 | 56 | private void WriteAgent(CSharpStringBuilder sb) 57 | { 58 | foreach (var action in this.Client.Actions) 59 | { 60 | sb.AppendLine($"{action.Accessibility} partial {(action.IsAwaitable ? "async " : "")}{action.ReturnType} {action.Name}{BuildTypeArgs(action)}({BuildArgs(action)}){BuildTypeArgsConstraint(action)}"); 61 | sb.AppendLine("{"); 62 | using (sb.Tab()) 63 | { 64 | sb.AppendLine("var _pkg_ = this.CreateDataPackage();"); 65 | foreach (var arg in action.Parameters) 66 | { 67 | sb.AppendLine($"_pkg_.Set(\"{arg.Name}\", {arg.Name});"); 68 | } 69 | if (action.HasReturnValue) 70 | { 71 | if (action.IsAwaitable) 72 | { 73 | sb.AppendLine($"var result = await this.InvokeAsync<{action.ReturnDataType}>(\"{action.Command}\", _pkg_);"); 74 | sb.AppendLine("return result;"); 75 | } 76 | else 77 | { 78 | sb.AppendLine($"var result = this.Invoke<{action.ReturnDataType}>(\"{action.Command}\", _pkg_);"); 79 | sb.AppendLine("return result;"); 80 | } 81 | } 82 | else 83 | { 84 | if (action.IsAwaitable) 85 | { 86 | sb.AppendLine($"await this.InvokeAsync(\"{action.Command}\", _pkg_);"); 87 | } 88 | else 89 | { 90 | sb.AppendLine($"this.Invoke(\"{action.Command}\", _pkg_);"); 91 | } 92 | } 93 | } 94 | sb.AppendLine("}"); 95 | } 96 | } 97 | 98 | private object BuildTypeArgsConstraint(Models.BuildRpcClientAgent.ActionModel action) 99 | { 100 | if (action.IsGenericMethod == false) return string.Empty; 101 | var sb = new StringBuilder(); 102 | var args = new List(); 103 | foreach (var item in action.TypeParameters) 104 | { 105 | args.Clear(); 106 | if (!item.HasConstraint) continue; 107 | if (item.ConstraintTypes.Any()) args.AddRange(item.ConstraintTypes); 108 | if (item.HasReferenceTypeConstraint) args.Add("class"); 109 | if (item.HasConstructorConstraint) args.Add("new()"); 110 | if (item.HasUnmanagedTypeConstraint) 111 | { 112 | args.Add("unmanaged"); 113 | } 114 | else if (item.HasValueTypeConstraint) 115 | { 116 | args.Add("struct"); 117 | } 118 | if (args.Count > 0) 119 | { 120 | sb.AppendLine($" where {item.Name} : {string.Join(", ", args)}"); 121 | } 122 | } 123 | return sb.ToString(); 124 | } 125 | 126 | private string BuildTypeArgs(Models.BuildRpcClientAgent.ActionModel action) 127 | { 128 | if (action.IsGenericMethod == false) return string.Empty; 129 | return $"<{string.Join(",", action.TypeParameters.Select(p => p.Type))}>"; 130 | } 131 | 132 | private object BuildArgs(Models.BuildRpcClientAgent.ActionModel action) 133 | { 134 | if (action.Parameters.Count == 0) return string.Empty; 135 | return string.Join(", ", action.Parameters.Select(p => $"{p.Type} {p.Name}")); 136 | } 137 | 138 | private void WriteDebugOutput(CSharpStringBuilder sb) 139 | { 140 | #if DEBUG 141 | var xmlSerializer = new XmlSerializer(this.Client.GetType()); 142 | string output = string.Empty; 143 | using (var stringWriter = new StringWriter()) 144 | { 145 | xmlSerializer.Serialize(stringWriter, this.Client); 146 | string xmlString = stringWriter.ToString(); 147 | var bytes = Encoding.UTF8.GetBytes(xmlString); 148 | output = Convert.ToBase64String(bytes); 149 | } 150 | sb.AppendLine("public static String Output()"); 151 | sb.AppendLine("{"); 152 | using (sb.Tab()) sb.AppendLine($"return \"{output}\";"); 153 | sb.AppendLine("}"); 154 | #endif 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc.SourceGenerators/RpcClientAgentGenerator.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Models.BuildRpcClientAgent; 2 | using LuYao.LightRpc.Renders.BuildRpcClientAgent; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp.Syntax; 5 | using Microsoft.CodeAnalysis.Text; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Collections.Immutable; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading; 12 | 13 | namespace LuYao.LightRpc; 14 | 15 | [Generator] 16 | public class RpcClientAgentGenerator : IIncrementalGenerator 17 | { 18 | public void Initialize(IncrementalGeneratorInitializationContext context) 19 | { 20 | 21 | var classDeclarations = context.SyntaxProvider.ForAttributeWithMetadataName( 22 | "LuYao.LightRpc.Attributes.RpcClientAgentAttribute", 23 | predicate: static (node, cancel) => node is ClassDeclarationSyntax, 24 | transform: static (ctx, cancel) => GetSemanticTargetForGeneration(ctx, cancel) 25 | ).Where(m => m.Item1 is not null && m.Item2 is not null); 26 | 27 | 28 | var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect()); 29 | context.RegisterSourceOutput(compilationAndClasses, Execute); 30 | } 31 | private static (ClassDeclarationSyntax, AttributeData) GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken ct) 32 | { 33 | if (context.TargetNode is not ClassDeclarationSyntax classDeclaration) 34 | { 35 | return (null, null); 36 | } 37 | AttributeData attribute = context.Attributes.FirstOrDefault(a => a.AttributeClass?.Name == "RpcClientAgentAttribute"); 38 | 39 | return (classDeclaration, attribute); 40 | } 41 | 42 | private void Execute(SourceProductionContext context, (Compilation Left, ImmutableArray<(ClassDeclarationSyntax, AttributeData)> Right) args) 43 | { 44 | if (!args.Right.Any()) return; 45 | var compilation = args.Left; 46 | foreach ((ClassDeclarationSyntax Syntax, AttributeData Attr) item in args.Right) 47 | { 48 | if (item.Syntax is null || item.Attr is null) continue; 49 | 50 | string className = item.Syntax.Identifier.ValueText; 51 | string ns = Helpers.GetNamespace(item.Syntax); 52 | 53 | var semanticModel = compilation.GetSemanticModel(item.Syntax.SyntaxTree); 54 | 55 | var model = new ClientModel 56 | { 57 | Name = className, 58 | FullName = $"{ns}.{className}", 59 | Namespace = ns, 60 | }; 61 | 62 | if (item.Syntax.TypeParameterList != null) 63 | { 64 | var typeParameters = item.Syntax.TypeParameterList.ChildNodes().ToList(); 65 | foreach (var t in typeParameters) 66 | { 67 | model.Type.Add(t.ToFullString()); 68 | } 69 | } 70 | 71 | BuildModel(context, item, semanticModel, model); 72 | 73 | var render = new RpcClientAgentRender(model); 74 | var code = render.Render(); 75 | 76 | var sourceText = SourceText.From(code, Encoding.UTF8); 77 | context.AddSource($"{ns}.{className}.g.cs", sourceText); 78 | } 79 | } 80 | private static void BuildModel(SourceProductionContext context, (ClassDeclarationSyntax Syntax, AttributeData Attr) item, SemanticModel semanticModel, ClientModel model) 81 | { 82 | foreach (var n in item.Attr.NamedArguments) 83 | { 84 | //switch (n.Key) 85 | //{ 86 | // case "Controller": 87 | // model.Controller = Convert.ToString(n.Value.Value); 88 | // break; 89 | // case "Area": 90 | // model.Area = Convert.ToString(n.Value.Value); 91 | // break; 92 | //} 93 | } 94 | 95 | var methods = item.Syntax.DescendantNodes().OfType().ToList(); 96 | if (methods.Count > 0) 97 | { 98 | foreach (var m in methods) 99 | { 100 | var methodSymbol = semanticModel.GetDeclaredSymbol(m) as IMethodSymbol; 101 | if (IsIgnore(methodSymbol, out var command)) continue; 102 | var action = new ActionModel 103 | { 104 | Name = methodSymbol.Name, 105 | Command = command, 106 | ReturnsVoid = methodSymbol.ReturnsVoid, 107 | HasReturnValue = !methodSymbol.ReturnsVoid, 108 | Accessibility = methodSymbol.DeclaredAccessibility.ToString().ToLower() 109 | }; 110 | if (methodSymbol.IsGenericMethod) 111 | { 112 | foreach (var t in methodSymbol.TypeParameters) 113 | { 114 | var mp = new ActionTypeParameterModel 115 | { 116 | Name = t.Name, 117 | Type = t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), 118 | HasReferenceTypeConstraint = t.HasReferenceTypeConstraint, 119 | HasValueTypeConstraint = t.HasValueTypeConstraint, 120 | HasConstructorConstraint = t.HasConstructorConstraint, 121 | HasUnmanagedTypeConstraint = t.HasUnmanagedTypeConstraint 122 | }; 123 | action.TypeParameters.Add(mp); 124 | foreach (var type in t.ConstraintTypes) 125 | { 126 | mp.ConstraintTypes.Add(type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); 127 | } 128 | } 129 | } 130 | model.Actions.Add(action); 131 | // 获取方法的返回类型 132 | var returnType = methodSymbol.ReturnType; 133 | action.ReturnType = returnType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); 134 | // 判断返回类型是否是 Task 或 Task<> 135 | bool returnsTask = returnType.Name == "Task" && (returnType.ContainingNamespace.ToString() == "System.Threading.Tasks"); 136 | 137 | action.ReturnDataType = action.ReturnType; 138 | // 这个方法是否可以被 await 139 | action.IsAwaitable = returnsTask; 140 | if (returnsTask) 141 | { 142 | if (returnType is INamedTypeSymbol named && named.TypeArguments.Any()) 143 | { 144 | action.ReturnDataType = named.TypeArguments[0].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); 145 | } 146 | } 147 | 148 | if (action.IsAwaitable) 149 | { 150 | // 检查返回类型是否是 Task 151 | bool returnsTaskWithoutResult = returnType.Name == "Task" && 152 | returnType.ContainingNamespace.ToString() == "System.Threading.Tasks"; 153 | 154 | // 检查返回类型是否是 Task 155 | bool returnsTaskWithResult = returnType is INamedTypeSymbol namedTypeSymbol && 156 | namedTypeSymbol.Name == "Task" && 157 | namedTypeSymbol.TypeArguments.Length == 1 && 158 | returnType.ContainingNamespace.ToString() == "System.Threading.Tasks"; 159 | 160 | // 检查自定义类型是否有 GetAwaiter 并且 GetAwaiter().GetResult() 有返回值 161 | bool customAwaitableReturnsValue = false; 162 | var getAwaiterMethod = returnType.GetMembers("GetAwaiter").OfType().FirstOrDefault(); 163 | 164 | if (getAwaiterMethod != null && getAwaiterMethod.Parameters.Length == 0) 165 | { 166 | var awaiterReturnType = getAwaiterMethod.ReturnType; 167 | var getResultMethod = awaiterReturnType.GetMembers("GetResult").OfType().FirstOrDefault(); 168 | 169 | if (getResultMethod != null) 170 | { 171 | // 检查 GetResult() 是否有返回值 172 | customAwaitableReturnsValue = !getResultMethod.ReturnsVoid; 173 | } 174 | } 175 | 176 | // 判断 await 后是否有返回值 177 | action.HasReturnValue = returnsTaskWithResult || customAwaitableReturnsValue; 178 | } 179 | 180 | var paras = m.ParameterList.Parameters; 181 | foreach (var p in paras) 182 | { 183 | if (p.Type is null) continue; 184 | var typeInfo = semanticModel.GetTypeInfo(p.Type); 185 | var ap = new ActionParameterModel 186 | { 187 | Name = p.Identifier.ValueText, 188 | Type = typeInfo.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) 189 | }; 190 | action.Parameters.Add(ap); 191 | } 192 | } 193 | } 194 | } 195 | 196 | private static bool IsIgnore(IMethodSymbol methodSymbol, out string command) 197 | { 198 | command = string.Empty; 199 | if (methodSymbol is null) return true; 200 | if (methodSymbol.IsStatic) return true; 201 | if (methodSymbol.IsOverride) return true; 202 | foreach (var attr in methodSymbol.GetAttributes()) 203 | { 204 | if (attr.AttributeClass.Name == "RemoteActionAttribute" && attr.ConstructorArguments.Count() > 0) 205 | { 206 | command = Convert.ToString(attr.ConstructorArguments[0].Value); 207 | return false; 208 | } 209 | } 210 | return true; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/Attributes/ControllerAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LuYao.LightRpc.Attributes; 4 | 5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] 6 | public class ControllerAttribute : Attribute 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/Attributes/ControllerDescriptorAttribute.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Descriptors; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace LuYao.LightRpc.Attributes; 6 | 7 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] 8 | public abstract class ControllerDescriptorAttribute : Attribute, IControllerDescriptor 9 | { 10 | public abstract IEnumerable GetActionDescriptors(); 11 | } 12 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/Attributes/IgnoreAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LuYao.LightRpc.Attributes; 4 | 5 | [AttributeUsage(AttributeTargets.Method)] 6 | public class IgnoreAttribute : Attribute 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/Attributes/RemoteActionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LuYao.LightRpc.Attributes; 4 | 5 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 6 | public class RemoteActionAttribute : Attribute 7 | { 8 | public RemoteActionAttribute(string command) 9 | { 10 | this.Command = command; 11 | } 12 | public string Command { get; } 13 | } -------------------------------------------------------------------------------- /src/LuYao.LightRpc/Attributes/RpcClientAgentAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LuYao.LightRpc.Attributes; 4 | 5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] 6 | public class RpcClientAgentAttribute : Attribute 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/Descriptors/ActionDescriptor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace LuYao.LightRpc.Descriptors; 5 | 6 | public abstract class ActionDescriptor : IActionDescriptor 7 | { 8 | protected ActionDescriptor(string path) 9 | { 10 | if (string.IsNullOrWhiteSpace(path)) throw new System.ArgumentNullException(nameof(path)); 11 | Path = path; 12 | } 13 | 14 | public string Path { get; } 15 | public abstract Task Invoke(object controller, InvokeContext context); 16 | } 17 | 18 | public class ActionDescriptor : ActionDescriptor 19 | { 20 | private readonly Func _invoke; 21 | 22 | public ActionDescriptor(string path, Func invoke) : base(path) 23 | { 24 | _invoke = invoke ?? throw new ArgumentNullException(nameof(invoke)); 25 | } 26 | 27 | public override Task Invoke(object controller, InvokeContext context) 28 | { 29 | return _invoke((T)controller, context); 30 | } 31 | } -------------------------------------------------------------------------------- /src/LuYao.LightRpc/Descriptors/IActionDescriptor.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace LuYao.LightRpc.Descriptors; 4 | 5 | public interface IActionDescriptor 6 | { 7 | string Path { get; } 8 | Task Invoke(object controller, InvokeContext context); 9 | } 10 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/Descriptors/IControllerDescriptor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace LuYao.LightRpc.Descriptors; 4 | 5 | public interface IControllerDescriptor 6 | { 7 | IEnumerable GetActionDescriptors(); 8 | } 9 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/EmptyDataPackage.cs: -------------------------------------------------------------------------------- 1 | namespace LuYao.LightRpc; 2 | 3 | public class EmptyDataPackage : IDataPackage 4 | { 5 | public static EmptyDataPackage Instance { get; } = new EmptyDataPackage(); 6 | private EmptyDataPackage() { } 7 | public bool TryGetValue(string key, out T? value) 8 | { 9 | value = default(T); 10 | return false; 11 | } 12 | 13 | public void Set(string key, T? value) 14 | { 15 | throw new System.NotSupportedException(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/Http/HttpJsonRpcTunnel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net.Http; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace LuYao.LightRpc.Http; 8 | 9 | public abstract class HttpJsonRpcTunnel : IRpcTunnel 10 | { 11 | protected HttpJsonRpcTunnel(string endpoint, IDataConverter jsonConverter) 12 | { 13 | if (string.IsNullOrWhiteSpace(endpoint)) throw new ArgumentNullException(nameof(endpoint)); 14 | if (jsonConverter == null) throw new ArgumentNullException(nameof(jsonConverter)); 15 | this.Endpoint = endpoint; 16 | JsonConverter = jsonConverter; 17 | } 18 | public string Endpoint { get; } 19 | public IDataConverter JsonConverter { get; } 20 | 21 | public IDataConverter DataConverter => this.JsonConverter; 22 | protected virtual string MatchEndpoint(String action) => this.Endpoint; 23 | protected abstract HttpClient CreateHttpClient(); 24 | 25 | public virtual RpcResult Send(string action, IDataPackage data) 26 | { 27 | #if NET5_0_OR_GREATER 28 | using var http = this.CreateHttpClient(); 29 | using HttpRequestMessage request = CreateHttpRequestMessage(action, data); 30 | using var response = http.Send(request); string body = string.Empty; 31 | using (var ms = response.Content.ReadAsStream()) 32 | using (var sr = new StreamReader(ms)) 33 | { 34 | body = sr.ReadToEnd(); 35 | } 36 | return CreateResult(response, body); 37 | #else 38 | return this.SendAsync(action, data).GetAwaiter().GetResult(); 39 | #endif 40 | } 41 | 42 | public virtual async Task SendAsync(string action, IDataPackage data) 43 | { 44 | using var http = this.CreateHttpClient(); 45 | using HttpRequestMessage request = CreateHttpRequestMessage(action, data); 46 | using var response = await http.SendAsync(request); 47 | string body = await response.Content.ReadAsStringAsync(); 48 | return CreateResult(response, body); 49 | } 50 | 51 | protected virtual RpcResult CreateResult(HttpResponseMessage response, string body) 52 | { 53 | if (response.IsSuccessStatusCode) 54 | { 55 | return new RpcResult 56 | { 57 | Code = RpcResultCode.Ok, 58 | Data = this.JsonConverter.Deserialize(body) 59 | }; 60 | } 61 | else 62 | { 63 | return new RpcResult 64 | { 65 | Code = (RpcResultCode)response.StatusCode, 66 | Message = body 67 | }; 68 | } 69 | } 70 | 71 | protected virtual HttpRequestMessage CreateHttpRequestMessage(string action, IDataPackage data) 72 | { 73 | var builder = new UriBuilder(this.MatchEndpoint(action)); 74 | var nv = System.Web.HttpUtility.ParseQueryString(builder.Query); 75 | nv["cmd"] = action; 76 | builder.Query = nv.ToString(); 77 | var url = builder.ToString(); 78 | var json = this.JsonConverter.Serialize(data); 79 | var request = new HttpRequestMessage(HttpMethod.Post, url); 80 | request.Content = new StringContent(json, Encoding.UTF8, "application/json"); 81 | request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); 82 | return request; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/IDataConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace LuYao.LightRpc; 4 | 5 | public interface IDataConverter 6 | { 7 | IDataPackage CreatePackage(); 8 | } 9 | 10 | public interface IDataConverter : IDataConverter 11 | { 12 | T Serialize(IDataPackage data); 13 | IDataPackage? Deserialize(T? data); 14 | } -------------------------------------------------------------------------------- /src/LuYao.LightRpc/IDataPackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LuYao.LightRpc; 4 | 5 | public interface IDataPackage 6 | { 7 | void Set(String key, T? value); 8 | bool TryGetValue(string key, out T? value); 9 | } 10 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/IRpcTunnel.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace LuYao.LightRpc; 4 | 5 | public interface IRpcTunnel 6 | { 7 | IDataConverter DataConverter { get; } 8 | Task SendAsync(string action, IDataPackage data); 9 | RpcResult Send(string action, IDataPackage data); 10 | } -------------------------------------------------------------------------------- /src/LuYao.LightRpc/InvokeContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LuYao.LightRpc; 4 | 5 | public class InvokeContext 6 | { 7 | public IDataPackage Params { get; } 8 | public IDataPackage Response { get; } 9 | 10 | public InvokeContext(IDataPackage @params, IDataPackage response) 11 | { 12 | this.Params = @params; 13 | this.Response = response; 14 | } 15 | 16 | 17 | public void SetResult(T result) 18 | { 19 | this.Response.Set("data", result); 20 | } 21 | } -------------------------------------------------------------------------------- /src/LuYao.LightRpc/LuYao.LightRpc.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net45;net461;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 4 | enable 5 | latest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/RpcClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace LuYao.LightRpc; 5 | 6 | public abstract class RpcClient 7 | { 8 | public abstract IDataPackage CreateDataPackage(); 9 | public abstract Task InvokeAsync(String action, IDataPackage package); 10 | public abstract Task InvokeAsync(String action, IDataPackage package); 11 | public abstract TResult Invoke(String action, IDataPackage package); 12 | public abstract void Invoke(String action, IDataPackage package); 13 | } 14 | 15 | public class RpcClient : RpcClient 16 | { 17 | public RpcClient(IRpcTunnel tunnel) 18 | { 19 | Tunnel = tunnel ?? throw new ArgumentNullException(nameof(tunnel)); 20 | } 21 | 22 | public IRpcTunnel Tunnel { get; } 23 | 24 | public override IDataPackage CreateDataPackage() => this.Tunnel.DataConverter.CreatePackage(); 25 | 26 | public override TResult Invoke(string action, IDataPackage package) 27 | { 28 | var result = this.Tunnel.Send(action, package); 29 | if (result.Code != RpcResultCode.Ok) throw new RpcException(result.Code, result.Message!); 30 | var output = result.Data ?? EmptyDataPackage.Instance; 31 | TResult data = default!; 32 | if (output.TryGetValue("data", out var v_ret)) data = v_ret!; 33 | return data; 34 | } 35 | 36 | public override void Invoke(string action, IDataPackage package) 37 | { 38 | var result = this.Tunnel.Send(action, package); 39 | if (result.Code != RpcResultCode.Ok) throw new RpcException(result.Code, result.Message!); 40 | } 41 | 42 | public override async Task InvokeAsync(String action, IDataPackage package) 43 | { 44 | var result = await this.Tunnel.SendAsync(action, package); 45 | if (result.Code != RpcResultCode.Ok) throw new RpcException(result.Code, result.Message!); 46 | } 47 | 48 | public override async Task InvokeAsync(string action, IDataPackage package) 49 | { 50 | var result = await this.Tunnel.SendAsync(action, package); 51 | if (result.Code != RpcResultCode.Ok) throw new RpcException(result.Code, result.Message!); 52 | var output = result.Data ?? EmptyDataPackage.Instance; 53 | TResult data = default!; 54 | if (output.TryGetValue("data", out var v_ret)) data = v_ret!; 55 | return data; 56 | } 57 | } -------------------------------------------------------------------------------- /src/LuYao.LightRpc/RpcException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LuYao.LightRpc; 4 | 5 | public class RpcException : Exception 6 | { 7 | /// 代码 8 | public Int32 Code { get; set; } 9 | 10 | /// 实例化远程调用异常 11 | /// 12 | /// 13 | public RpcException(RpcResultCode code, String message) : base(message) => Code = (int)code; 14 | /// 实例化远程调用异常 15 | /// 16 | /// 17 | public RpcException(Int32 code, String message) : base(message) => Code = code; 18 | 19 | /// 实例化远程调用异常 20 | /// 21 | /// 22 | public RpcException(Int32 code, Exception ex) : base(ex.Message, ex) => Code = code; 23 | } 24 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/RpcResult.cs: -------------------------------------------------------------------------------- 1 | namespace LuYao.LightRpc; 2 | 3 | public class RpcResult 4 | { 5 | public RpcResultCode Code { get; set; } 6 | public string? Message { get; set; } 7 | public IDataPackage? Data { get; set; } 8 | } -------------------------------------------------------------------------------- /src/LuYao.LightRpc/RpcResultCode.cs: -------------------------------------------------------------------------------- 1 | namespace LuYao.LightRpc; 2 | 3 | public enum RpcResultCode 4 | { 5 | Ok = 200, 6 | Unauthorized = 401, 7 | Forbidden = 403, 8 | NotFound = 404, 9 | InternalServerError = 500 10 | } 11 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/RpcServer.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Attributes; 2 | using LuYao.LightRpc.Descriptors; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Threading.Tasks; 8 | 9 | namespace LuYao.LightRpc; 10 | 11 | public class RpcServer 12 | { 13 | public IDataConverter DataConverter { get; } 14 | 15 | public RpcServer(IDataConverter dataConverter) 16 | { 17 | DataConverter = dataConverter; 18 | } 19 | 20 | private IDictionary _services = new Dictionary(StringComparer.OrdinalIgnoreCase); 21 | public IDictionary Services => _services; 22 | 23 | public void Register() where TController : class, new() 24 | { 25 | var type = typeof(TController); 26 | var attribute = type.GetCustomAttributes(false).FirstOrDefault(); 27 | if (attribute == null) throw new InvalidOperationException($"The controller {type.FullName} is not decorated with RpcControllerDescriptorAttribute."); 28 | 29 | lock (this) 30 | { 31 | var controller = new TController(); 32 | var temp = new Dictionary(this._services, StringComparer.OrdinalIgnoreCase); 33 | foreach (var action in attribute.GetActionDescriptors()) 34 | { 35 | temp[action.Path] = new RpcService(controller, action); 36 | } 37 | this._services = temp; 38 | } 39 | } 40 | 41 | public async Task InvokeAsync(string action, IDataPackage input) 42 | { 43 | if (action == null) throw new ArgumentNullException(nameof(action)); 44 | var result = new RpcResult 45 | { 46 | Code = RpcResultCode.Ok, 47 | Data = this.DataConverter.CreatePackage() 48 | }; 49 | if (!this.TryFindService(action, out var service)) 50 | { 51 | result.Code = RpcResultCode.NotFound; 52 | return result; 53 | } 54 | var context = new InvokeContext(input, result.Data); 55 | try 56 | { 57 | await service!.Descriptor.Invoke(service.Controller, context); 58 | } 59 | catch (Exception e) 60 | { 61 | result.Code = RpcResultCode.InternalServerError; 62 | result.Message = e.Message; 63 | } 64 | return result; 65 | } 66 | 67 | private bool TryFindService(string action, out RpcService? service) 68 | { 69 | if (this.Services.TryGetValue(action, out service)) 70 | { 71 | return true; 72 | } 73 | // 局部模糊匹配 74 | var p = action.IndexOf('/'); 75 | if (p >= 0) 76 | { 77 | var ctrl = action.Substring(0, p); 78 | if (this.Services.TryGetValue(ctrl + "/*", out service)) return true; 79 | } 80 | // 全局模糊匹配 81 | if (this.Services.TryGetValue("*", out service)) return true; 82 | return false; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/LuYao.LightRpc/RpcService.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Descriptors; 2 | using System; 3 | 4 | namespace LuYao.LightRpc; 5 | 6 | public class RpcService 7 | { 8 | public RpcService(object controller, IActionDescriptor descriptor) 9 | { 10 | Controller = controller; 11 | Descriptor = descriptor; 12 | } 13 | public Object Controller { get; } 14 | public IActionDescriptor Descriptor { get; } 15 | } 16 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test.Client/BaseClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace LuYao.LightRpc.Test.Client; 8 | 9 | public class BaseClass 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test.Client/LuYao.LightRpc.Test.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test.Client/MainClient.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Attributes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace LuYao.LightRpc.Test.Client; 9 | 10 | [RpcClientAgent] 11 | public partial class MainClient : RpcClient 12 | { 13 | public MainClient(string endpoint) : base(new MainTunnel(endpoint)) 14 | { 15 | 16 | } 17 | [RemoteAction("Test/Sum")] 18 | public partial Task Sum(int a, int b); 19 | 20 | [RemoteAction("Test/Sum")] 21 | public partial Task Sum1(T1 a, T2 b, T3 c) 22 | where T : struct 23 | where T1 : class 24 | where T2 : unmanaged 25 | where T3 : BaseClass, new() 26 | ; 27 | } 28 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test.Client/MainTunnel.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Http; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace LuYao.LightRpc.Test.Client; 9 | 10 | public class MainTunnel : HttpJsonRpcTunnel 11 | { 12 | private static readonly SocketsHttpHandler _handler = new SocketsHttpHandler(); 13 | 14 | public MainTunnel(string endpoint) : base(endpoint, new NewtonsoftDataConverter()) 15 | { 16 | 17 | } 18 | 19 | protected override HttpClient CreateHttpClient() 20 | { 21 | return new HttpClient(_handler, false); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test.Client/Program.cs: -------------------------------------------------------------------------------- 1 | // See https://aka.ms/new-console-template for more information 2 | using LuYao.LightRpc.Test.Client; 3 | 4 | var endpoint = "http://localhost:5287/"; 5 | var client = new MainClient(endpoint); 6 | var sum = await client.Sum(1, 2); 7 | Console.WriteLine(sum); 8 | Console.ReadLine(); -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test.MinimalApi/LuYao.LightRpc.Test.MinimalApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test.MinimalApi/MainServer.cs: -------------------------------------------------------------------------------- 1 | namespace LuYao.LightRpc.Test.MinimalApi; 2 | 3 | public class MainServer : MainServer 4 | { 5 | public MainServer() : base(new NewtonsoftDataConverter()) 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test.MinimalApi/Program.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc; 2 | using LuYao.LightRpc.Test.MinimalApi; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | var builder = WebApplication.CreateBuilder(args); 6 | // Add services to the container. 7 | builder.Services.AddSingleton(); 8 | 9 | var app = builder.Build(); 10 | 11 | app.Map("/", async ( 12 | HttpContext context, 13 | [FromQuery(Name = "cmd")] string? action, 14 | [FromServices] MainServer server 15 | ) => 16 | { 17 | if (action is null) action = string.Empty; 18 | string json = string.Empty; 19 | var request = context.Request; 20 | using (var reader = new StreamReader(request.Body)) 21 | { 22 | json = await reader.ReadToEndAsync(); 23 | } 24 | var input = server.DataConverter.Deserialize(json) ?? EmptyDataPackage.Instance; 25 | var result = await server.InvokeAsync(action, input); 26 | var ret = new RpcHttpResult(result); 27 | return ret; 28 | }); 29 | 30 | app.Run(); 31 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test.MinimalApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:36880", 8 | "sslPort": 0 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "applicationUrl": "http://localhost:5287", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test.MinimalApi/RpcHttpResult.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace LuYao.LightRpc.Test.MinimalApi; 3 | 4 | public class RpcHttpResult : IResult 5 | { 6 | public RpcResult RpcResult { get; } 7 | 8 | public RpcHttpResult(RpcResult rpcResult) 9 | { 10 | RpcResult = rpcResult; 11 | } 12 | public async Task ExecuteAsync(HttpContext httpContext) 13 | { 14 | var response = httpContext.Response; 15 | response.StatusCode = (int)this.RpcResult.Code; 16 | if (this.RpcResult.Code == RpcResultCode.Ok) 17 | { 18 | if (this.RpcResult.Data is not null) 19 | { 20 | var server = httpContext.RequestServices.GetRequiredService(); 21 | var output = server.DataConverter.Serialize(this.RpcResult.Data); 22 | await response.WriteAsync(output); 23 | } 24 | } 25 | else 26 | { 27 | if (!string.IsNullOrWhiteSpace(this.RpcResult.Message)) 28 | { 29 | await response.WriteAsync(this.RpcResult.Message); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test.MinimalApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test.MinimalApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test/LuYao.LightRpc.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test/MainServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace LuYao.LightRpc.Test; 8 | 9 | public class MainServer : RpcServer 10 | { 11 | public MainServer(IDataConverter dataConverter) : base(dataConverter) 12 | { 13 | this.Register(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.Test/TestController.cs: -------------------------------------------------------------------------------- 1 | using LuYao.LightRpc.Attributes; 2 | 3 | namespace LuYao.LightRpc.Test; 4 | 5 | [Controller] 6 | public partial class TestController 7 | { 8 | public int Sum(int a, int b) 9 | { 10 | return a + b; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/LuYao.LightRpc.UnitTests/LuYao.LightRpc.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net472;net6.0;net7.0;net8.0 5 | 6 | false 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | --------------------------------------------------------------------------------