├── .gitignore
├── Directory.Build.props
├── DotNetCoreRpc.sln
├── README.md
├── demo
├── Test.Client
│ ├── NacosDiscoveryDelegatingHandler.cs
│ ├── Program.cs
│ ├── RpcClientBenchTest.cs
│ ├── Test.Client.csproj
│ └── appsettings.json
├── Test.DAL
│ ├── PersonDal.cs
│ ├── ProductDal.cs
│ └── Test.DAL.csproj
├── Test.IDAL
│ ├── IPersonDal.cs
│ ├── IProductDal.cs
│ └── Test.IDAL.csproj
├── Test.IService
│ ├── Class1.cs
│ ├── IPersonService.cs
│ ├── IProductService.cs
│ └── Test.IService.csproj
├── Test.Model
│ ├── PersonModel.cs
│ ├── ProductDto.cs
│ └── Test.Model.csproj
├── Test.Server
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Startup.cs
│ ├── Test.Server.csproj
│ ├── appsettings.Development.json
│ └── appsettings.json
├── Test.Server6
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Test.Server6.csproj
│ ├── appsettings.Development.json
│ └── appsettings.json
└── Test.Service
│ ├── Class1.cs
│ ├── Configs
│ ├── ElasticSearchConfig.cs
│ └── RedisConfig.cs
│ ├── Filters
│ ├── CacheFilter.cs
│ └── LoggerFilter.cs
│ ├── PersonService.cs
│ ├── ProductService.cs
│ └── Test.Service.csproj
├── docs
├── 1.1.3及之前版本.md
└── 1.1.3更高版本.md
└── src
├── DotNetCoreRpc.Client
├── ClientOptions.cs
├── DotNetCoreRpc.Client.csproj
├── HttpClientFactoryHelper.cs
├── HttpRequestInterceptor.cs
├── RequestHandler.cs
├── RpcClient.cs
└── ServiceCollectionExtensions.cs
├── DotNetCoreRpc.Core
├── DotNetCoreRpc.Core.csproj
├── JsonExtensions.cs
├── RequestModel.cs
├── ResponseModel.cs
├── TaskUtils.cs
└── TypeExtensions.cs
└── DotNetCoreRpc.Server
├── DncRpcEndpointRouteBuilderExtensions.cs
├── DncRpcIApplicationBuilderExtensions.cs
├── DotNetCoreRpc.Server.csproj
├── DotNetCoreRpcMiddleware.cs
├── RpcBuilder
├── AspectPiplineBuilder.cs
├── FromServicesAttribute.cs
├── RpcContext.cs
├── RpcFilterAttribute.cs
├── RpcFilterUtils.cs
└── RpcRequestDelegate.cs
├── RpcServerOptions.cs
├── ServerServiceCollectionExtensions.cs
└── ServiceTypeCollection.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | # globs
2 | Makefile.in
3 | *.userprefs
4 | *.usertasks
5 | config.make
6 | config.status
7 | aclocal.m4
8 | install-sh
9 | autom4te.cache/
10 | *.tar.gz
11 | tarballs/
12 | test-results/
13 |
14 | # Mac bundle stuff
15 | *.dmg
16 | *.app
17 |
18 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
19 | # General
20 | .DS_Store
21 | .AppleDouble
22 | .LSOverride
23 |
24 | # Icon must end with two \r
25 | Icon
26 |
27 |
28 | # Thumbnails
29 | ._*
30 |
31 | # Files that might appear in the root of a volume
32 | .DocumentRevisions-V100
33 | .fseventsd
34 | .Spotlight-V100
35 | .TemporaryItems
36 | .Trashes
37 | .VolumeIcon.icns
38 | .com.apple.timemachine.donotpresent
39 |
40 | # Directories potentially created on remote AFP share
41 | .AppleDB
42 | .AppleDesktop
43 | Network Trash Folder
44 | Temporary Items
45 | .apdisk
46 |
47 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
48 | # Windows thumbnail cache files
49 | Thumbs.db
50 | ehthumbs.db
51 | ehthumbs_vista.db
52 |
53 | # Dump file
54 | *.stackdump
55 |
56 | # Folder config file
57 | [Dd]esktop.ini
58 |
59 | # Recycle Bin used on file shares
60 | $RECYCLE.BIN/
61 |
62 | # Windows Installer files
63 | *.cab
64 | *.msi
65 | *.msix
66 | *.msm
67 | *.msp
68 |
69 | # Windows shortcuts
70 | *.lnk
71 |
72 | # content below from: https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
73 | ## Ignore Visual Studio temporary files, build results, and
74 | ## files generated by popular Visual Studio add-ons.
75 | ##
76 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
77 |
78 | # User-specific files
79 | *.suo
80 | *.user
81 | *.userosscache
82 | *.sln.docstates
83 |
84 | # User-specific files (MonoDevelop/Xamarin Studio)
85 | *.userprefs
86 |
87 | # Build results
88 | [Dd]ebug/
89 | [Dd]ebugPublic/
90 | [Rr]elease/
91 | [Rr]eleases/
92 | x64/
93 | x86/
94 | bld/
95 | [Bb]in/
96 | [Oo]bj/
97 | [Ll]og/
98 |
99 | # Visual Studio 2015/2017 cache/options directory
100 | .vs/
101 | # Uncomment if you have tasks that create the project's static files in wwwroot
102 | #wwwroot/
103 |
104 | # Visual Studio 2017 auto generated files
105 | Generated\ Files/
106 |
107 | # MSTest test Results
108 | [Tt]est[Rr]esult*/
109 | [Bb]uild[Ll]og.*
110 |
111 | # NUNIT
112 | *.VisualState.xml
113 | TestResult.xml
114 |
115 | # Build Results of an ATL Project
116 | [Dd]ebugPS/
117 | [Rr]eleasePS/
118 | dlldata.c
119 |
120 | # Benchmark Results
121 | BenchmarkDotNet.Artifacts/
122 |
123 | # .NET Core
124 | project.lock.json
125 | project.fragment.lock.json
126 | artifacts/
127 |
128 | # StyleCop
129 | StyleCopReport.xml
130 |
131 | # Files built by Visual Studio
132 | *_i.c
133 | *_p.c
134 | *_h.h
135 | *.ilk
136 | *.meta
137 | *.obj
138 | *.iobj
139 | *.pch
140 | *.pdb
141 | *.ipdb
142 | *.pgc
143 | *.pgd
144 | *.rsp
145 | *.sbr
146 | *.tlb
147 | *.tli
148 | *.tlh
149 | *.tmp
150 | *.tmp_proj
151 | *_wpftmp.csproj
152 | *.log
153 | *.vspscc
154 | *.vssscc
155 | .builds
156 | *.pidb
157 | *.svclog
158 | *.scc
159 |
160 | # Chutzpah Test files
161 | _Chutzpah*
162 |
163 | # Visual C++ cache files
164 | ipch/
165 | *.aps
166 | *.ncb
167 | *.opendb
168 | *.opensdf
169 | *.sdf
170 | *.cachefile
171 | *.VC.db
172 | *.VC.VC.opendb
173 |
174 | # Visual Studio profiler
175 | *.psess
176 | *.vsp
177 | *.vspx
178 | *.sap
179 |
180 | # Visual Studio Trace Files
181 | *.e2e
182 |
183 | # TFS 2012 Local Workspace
184 | $tf/
185 |
186 | # Guidance Automation Toolkit
187 | *.gpState
188 |
189 | # ReSharper is a .NET coding add-in
190 | _ReSharper*/
191 | *.[Rr]e[Ss]harper
192 | *.DotSettings.user
193 |
194 | # JustCode is a .NET coding add-in
195 | .JustCode
196 |
197 | # TeamCity is a build add-in
198 | _TeamCity*
199 |
200 | # DotCover is a Code Coverage Tool
201 | *.dotCover
202 |
203 | # AxoCover is a Code Coverage Tool
204 | .axoCover/*
205 | !.axoCover/settings.json
206 |
207 | # Visual Studio code coverage results
208 | *.coverage
209 | *.coveragexml
210 |
211 | # NCrunch
212 | _NCrunch_*
213 | .*crunch*.local.xml
214 | nCrunchTemp_*
215 |
216 | # MightyMoose
217 | *.mm.*
218 | AutoTest.Net/
219 |
220 | # Web workbench (sass)
221 | .sass-cache/
222 |
223 | # Installshield output folder
224 | [Ee]xpress/
225 |
226 | # DocProject is a documentation generator add-in
227 | DocProject/buildhelp/
228 | DocProject/Help/*.HxT
229 | DocProject/Help/*.HxC
230 | DocProject/Help/*.hhc
231 | DocProject/Help/*.hhk
232 | DocProject/Help/*.hhp
233 | DocProject/Help/Html2
234 | DocProject/Help/html
235 |
236 | # Click-Once directory
237 | publish/
238 |
239 | # Publish Web Output
240 | *.[Pp]ublish.xml
241 | *.azurePubxml
242 | # Note: Comment the next line if you want to checkin your web deploy settings,
243 | # but database connection strings (with potential passwords) will be unencrypted
244 | *.pubxml
245 | *.publishproj
246 |
247 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
248 | # checkin your Azure Web App publish settings, but sensitive information contained
249 | # in these scripts will be unencrypted
250 | PublishScripts/
251 |
252 | # NuGet Packages
253 | *.nupkg
254 | # The packages folder can be ignored because of Package Restore
255 | **/[Pp]ackages/*
256 | # except build/, which is used as an MSBuild target.
257 | !**/[Pp]ackages/build/
258 | # Uncomment if necessary however generally it will be regenerated when needed
259 | #!**/[Pp]ackages/repositories.config
260 | # NuGet v3's project.json files produces more ignorable files
261 | *.nuget.props
262 | *.nuget.targets
263 |
264 | # Microsoft Azure Build Output
265 | csx/
266 | *.build.csdef
267 |
268 | # Microsoft Azure Emulator
269 | ecf/
270 | rcf/
271 |
272 | # Windows Store app package directories and files
273 | AppPackages/
274 | BundleArtifacts/
275 | Package.StoreAssociation.xml
276 | _pkginfo.txt
277 | *.appx
278 |
279 | # Visual Studio cache files
280 | # files ending in .cache can be ignored
281 | *.[Cc]ache
282 | # but keep track of directories ending in .cache
283 | !*.[Cc]ache/
284 |
285 | # Others
286 | ClientBin/
287 | ~$*
288 | *~
289 | *.dbmdl
290 | *.dbproj.schemaview
291 | *.jfm
292 | *.pfx
293 | *.publishsettings
294 | orleans.codegen.cs
295 |
296 | # Including strong name files can present a security risk
297 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
298 | #*.snk
299 |
300 | # Since there are multiple workflows, uncomment next line to ignore bower_components
301 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
302 | #bower_components/
303 |
304 | # RIA/Silverlight projects
305 | Generated_Code/
306 |
307 | # Backup & report files from converting an old project file
308 | # to a newer Visual Studio version. Backup files are not needed,
309 | # because we have git ;-)
310 | _UpgradeReport_Files/
311 | Backup*/
312 | UpgradeLog*.XML
313 | UpgradeLog*.htm
314 | ServiceFabricBackup/
315 | *.rptproj.bak
316 |
317 | # SQL Server files
318 | *.mdf
319 | *.ldf
320 | *.ndf
321 |
322 | # Business Intelligence projects
323 | *.rdl.data
324 | *.bim.layout
325 | *.bim_*.settings
326 | *.rptproj.rsuser
327 |
328 | # Microsoft Fakes
329 | FakesAssemblies/
330 |
331 | # GhostDoc plugin setting file
332 | *.GhostDoc.xml
333 |
334 | # Node.js Tools for Visual Studio
335 | .ntvs_analysis.dat
336 | node_modules/
337 |
338 | # Visual Studio 6 build log
339 | *.plg
340 |
341 | # Visual Studio 6 workspace options file
342 | *.opt
343 |
344 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
345 | *.vbw
346 |
347 | # Visual Studio LightSwitch build output
348 | **/*.HTMLClient/GeneratedArtifacts
349 | **/*.DesktopClient/GeneratedArtifacts
350 | **/*.DesktopClient/ModelManifest.xml
351 | **/*.Server/GeneratedArtifacts
352 | **/*.Server/ModelManifest.xml
353 | _Pvt_Extensions
354 |
355 | # Paket dependency manager
356 | .paket/paket.exe
357 | paket-files/
358 |
359 | # FAKE - F# Make
360 | .fake/
361 |
362 | # JetBrains Rider
363 | .idea/
364 | *.sln.iml
365 |
366 | # CodeRush personal settings
367 | .cr/personal
368 |
369 | # Python Tools for Visual Studio (PTVS)
370 | __pycache__/
371 | *.pyc
372 |
373 | # Cake - Uncomment if you are using it
374 | # tools/**
375 | # !tools/packages.config
376 |
377 | # Tabs Studio
378 | *.tss
379 |
380 | # Telerik's JustMock configuration file
381 | *.jmconfig
382 |
383 | # BizTalk build output
384 | *.btp.cs
385 | *.btm.cs
386 | *.odx.cs
387 | *.xsd.cs
388 |
389 | # OpenCover UI analysis results
390 | OpenCover/
391 |
392 | # Azure Stream Analytics local run output
393 | ASALocalRun/
394 |
395 | # MSBuild Binary and Structured Log
396 | *.binlog
397 |
398 | # NVidia Nsight GPU debugger configuration file
399 | *.nvuser
400 |
401 | # MFractors (Xamarin productivity tool) working folder
402 | .mfractor/
403 |
404 | # Local History for Visual Studio
405 | .localhistory/
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | softlgl
4 | softlgl
5 | softlgl
6 | https://github.com/softlgl/DotNetCoreRpc
7 | 1.3.0
8 | 1.3.0
9 |
10 |
11 |
--------------------------------------------------------------------------------
/DotNetCoreRpc.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio Version 17
3 | VisualStudioVersion = 17.0.31912.275
4 | MinimumVisualStudioVersion = 10.0.40219.1
5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{96A4B550-34BF-42E5-8616-9417AC211E06}"
6 | EndProject
7 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreRpc.Client", "src\DotNetCoreRpc.Client\DotNetCoreRpc.Client.csproj", "{24C6EB77-6E2F-4CAD-BA1F-FF5020AF1817}"
8 | EndProject
9 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "demo", "demo", "{D2541A66-D747-491B-A2D5-449097B9E199}"
10 | EndProject
11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreRpc.Core", "src\DotNetCoreRpc.Core\DotNetCoreRpc.Core.csproj", "{56E87951-234F-42DF-9000-FE8075E86E48}"
12 | EndProject
13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreRpc.Server", "src\DotNetCoreRpc.Server\DotNetCoreRpc.Server.csproj", "{0955C355-CCDB-4423-B769-57B50481A037}"
14 | EndProject
15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.IDAL", "demo\Test.IDAL\Test.IDAL.csproj", "{B94943EB-4EED-450A-B602-8185135F1507}"
16 | EndProject
17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.DAL", "demo\Test.DAL\Test.DAL.csproj", "{1A48DCED-CBBB-4F3D-AA30-49C85CCF6A42}"
18 | EndProject
19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.Model", "demo\Test.Model\Test.Model.csproj", "{44C5D95F-7F18-4EDA-9C50-4780AA07EC36}"
20 | EndProject
21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.IService", "demo\Test.IService\Test.IService.csproj", "{C0605A57-C2C6-48ED-ABB3-473FB89ADA71}"
22 | EndProject
23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.Service", "demo\Test.Service\Test.Service.csproj", "{077A1845-8E2D-4035-B2F9-3375A00AD6E7}"
24 | EndProject
25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.Server", "demo\Test.Server\Test.Server.csproj", "{02A7DC84-9707-48DF-B420-CD9F8CADDAEF}"
26 | EndProject
27 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.Client", "demo\Test.Client\Test.Client.csproj", "{69FA7DCE-C9F7-4EE0-87BA-F718E5DEEFC8}"
28 | EndProject
29 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.Server6", "demo\Test.Server6\Test.Server6.csproj", "{37EDF9A9-16E5-418C-BC5B-25A0FF30A1D9}"
30 | EndProject
31 | Global
32 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
33 | Debug|Any CPU = Debug|Any CPU
34 | Release|Any CPU = Release|Any CPU
35 | EndGlobalSection
36 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
37 | {24C6EB77-6E2F-4CAD-BA1F-FF5020AF1817}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 | {24C6EB77-6E2F-4CAD-BA1F-FF5020AF1817}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 | {24C6EB77-6E2F-4CAD-BA1F-FF5020AF1817}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 | {24C6EB77-6E2F-4CAD-BA1F-FF5020AF1817}.Release|Any CPU.Build.0 = Release|Any CPU
41 | {56E87951-234F-42DF-9000-FE8075E86E48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 | {56E87951-234F-42DF-9000-FE8075E86E48}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 | {56E87951-234F-42DF-9000-FE8075E86E48}.Release|Any CPU.ActiveCfg = Release|Any CPU
44 | {56E87951-234F-42DF-9000-FE8075E86E48}.Release|Any CPU.Build.0 = Release|Any CPU
45 | {0955C355-CCDB-4423-B769-57B50481A037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46 | {0955C355-CCDB-4423-B769-57B50481A037}.Debug|Any CPU.Build.0 = Debug|Any CPU
47 | {0955C355-CCDB-4423-B769-57B50481A037}.Release|Any CPU.ActiveCfg = Release|Any CPU
48 | {0955C355-CCDB-4423-B769-57B50481A037}.Release|Any CPU.Build.0 = Release|Any CPU
49 | {B94943EB-4EED-450A-B602-8185135F1507}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 | {B94943EB-4EED-450A-B602-8185135F1507}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 | {B94943EB-4EED-450A-B602-8185135F1507}.Release|Any CPU.ActiveCfg = Release|Any CPU
52 | {B94943EB-4EED-450A-B602-8185135F1507}.Release|Any CPU.Build.0 = Release|Any CPU
53 | {1A48DCED-CBBB-4F3D-AA30-49C85CCF6A42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54 | {1A48DCED-CBBB-4F3D-AA30-49C85CCF6A42}.Debug|Any CPU.Build.0 = Debug|Any CPU
55 | {1A48DCED-CBBB-4F3D-AA30-49C85CCF6A42}.Release|Any CPU.ActiveCfg = Release|Any CPU
56 | {1A48DCED-CBBB-4F3D-AA30-49C85CCF6A42}.Release|Any CPU.Build.0 = Release|Any CPU
57 | {44C5D95F-7F18-4EDA-9C50-4780AA07EC36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
58 | {44C5D95F-7F18-4EDA-9C50-4780AA07EC36}.Debug|Any CPU.Build.0 = Debug|Any CPU
59 | {44C5D95F-7F18-4EDA-9C50-4780AA07EC36}.Release|Any CPU.ActiveCfg = Release|Any CPU
60 | {44C5D95F-7F18-4EDA-9C50-4780AA07EC36}.Release|Any CPU.Build.0 = Release|Any CPU
61 | {C0605A57-C2C6-48ED-ABB3-473FB89ADA71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62 | {C0605A57-C2C6-48ED-ABB3-473FB89ADA71}.Debug|Any CPU.Build.0 = Debug|Any CPU
63 | {C0605A57-C2C6-48ED-ABB3-473FB89ADA71}.Release|Any CPU.ActiveCfg = Release|Any CPU
64 | {C0605A57-C2C6-48ED-ABB3-473FB89ADA71}.Release|Any CPU.Build.0 = Release|Any CPU
65 | {077A1845-8E2D-4035-B2F9-3375A00AD6E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
66 | {077A1845-8E2D-4035-B2F9-3375A00AD6E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
67 | {077A1845-8E2D-4035-B2F9-3375A00AD6E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
68 | {077A1845-8E2D-4035-B2F9-3375A00AD6E7}.Release|Any CPU.Build.0 = Release|Any CPU
69 | {02A7DC84-9707-48DF-B420-CD9F8CADDAEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
70 | {02A7DC84-9707-48DF-B420-CD9F8CADDAEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
71 | {02A7DC84-9707-48DF-B420-CD9F8CADDAEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
72 | {02A7DC84-9707-48DF-B420-CD9F8CADDAEF}.Release|Any CPU.Build.0 = Release|Any CPU
73 | {69FA7DCE-C9F7-4EE0-87BA-F718E5DEEFC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
74 | {69FA7DCE-C9F7-4EE0-87BA-F718E5DEEFC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
75 | {69FA7DCE-C9F7-4EE0-87BA-F718E5DEEFC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
76 | {69FA7DCE-C9F7-4EE0-87BA-F718E5DEEFC8}.Release|Any CPU.Build.0 = Release|Any CPU
77 | {37EDF9A9-16E5-418C-BC5B-25A0FF30A1D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
78 | {37EDF9A9-16E5-418C-BC5B-25A0FF30A1D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
79 | {37EDF9A9-16E5-418C-BC5B-25A0FF30A1D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
80 | {37EDF9A9-16E5-418C-BC5B-25A0FF30A1D9}.Release|Any CPU.Build.0 = Release|Any CPU
81 | EndGlobalSection
82 | GlobalSection(SolutionProperties) = preSolution
83 | HideSolutionNode = FALSE
84 | EndGlobalSection
85 | GlobalSection(NestedProjects) = preSolution
86 | {24C6EB77-6E2F-4CAD-BA1F-FF5020AF1817} = {96A4B550-34BF-42E5-8616-9417AC211E06}
87 | {56E87951-234F-42DF-9000-FE8075E86E48} = {96A4B550-34BF-42E5-8616-9417AC211E06}
88 | {0955C355-CCDB-4423-B769-57B50481A037} = {96A4B550-34BF-42E5-8616-9417AC211E06}
89 | {B94943EB-4EED-450A-B602-8185135F1507} = {D2541A66-D747-491B-A2D5-449097B9E199}
90 | {1A48DCED-CBBB-4F3D-AA30-49C85CCF6A42} = {D2541A66-D747-491B-A2D5-449097B9E199}
91 | {44C5D95F-7F18-4EDA-9C50-4780AA07EC36} = {D2541A66-D747-491B-A2D5-449097B9E199}
92 | {C0605A57-C2C6-48ED-ABB3-473FB89ADA71} = {D2541A66-D747-491B-A2D5-449097B9E199}
93 | {077A1845-8E2D-4035-B2F9-3375A00AD6E7} = {D2541A66-D747-491B-A2D5-449097B9E199}
94 | {02A7DC84-9707-48DF-B420-CD9F8CADDAEF} = {D2541A66-D747-491B-A2D5-449097B9E199}
95 | {69FA7DCE-C9F7-4EE0-87BA-F718E5DEEFC8} = {D2541A66-D747-491B-A2D5-449097B9E199}
96 | {37EDF9A9-16E5-418C-BC5B-25A0FF30A1D9} = {D2541A66-D747-491B-A2D5-449097B9E199}
97 | EndGlobalSection
98 | GlobalSection(ExtensibilityGlobals) = postSolution
99 | SolutionGuid = {7F50863A-4194-4CF2-A0DE-D292D9D0B9B7}
100 | EndGlobalSection
101 | EndGlobal
102 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DotNetCoreRpc基于.NetCore的RPC框架
2 |
3 | #### 前言
4 | 一直以来都想实现一个简单的RPC框架。.net core不断完善之后借助其自身的便利实现一个RPC框架。框架分Server端和Client端两部分。Client端可在Console或Web端等,能运行.net core的host上运行。Server端依赖Asp.Net Core,接下来介绍大致使用,详细介绍请参阅https://www.cnblogs.com/wucy/p/13096515.html
5 |
6 | #### 运行环境
7 |
8 | - visual studio 2022
9 | - .netstandard2.1
10 | - .net5;.net6;.net7;net8;.net9
11 | - asp.net core 3.1;sp.net core 5.0;asp.net core 6.0;asp.net core 7.0;asp.net core 8.0;asp.net core 9.0
12 |
13 |
14 | #### 使用方式
15 | 由于`v1.1.3之后版本`优化了一下client和server端的注册方式,所以分两个文档介绍。
16 | + [v1.1.3及之前版本](https://github.com/softlgl/DotNetCoreRpc/blob/master/docs/1.1.3及之前版本.md)
17 | + [v1.1.3之后版本](https://github.com/softlgl/DotNetCoreRpc/blob/master/docs/1.1.3更高版本.md)
18 |
--------------------------------------------------------------------------------
/demo/Test.Client/NacosDiscoveryDelegatingHandler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using Nacos;
3 | using Nacos.V2;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Net.Http;
7 | using System.Text;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | namespace Test.Client
12 | {
13 | public class NacosDiscoveryDelegatingHandler : DelegatingHandler
14 | {
15 | private readonly INacosNamingService _serverManager;
16 | private readonly ILogger _logger;
17 |
18 | public NacosDiscoveryDelegatingHandler(INacosNamingService serverManager, ILogger logger)
19 | {
20 | _serverManager = serverManager;
21 | _logger = logger;
22 | }
23 |
24 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
25 | {
26 | var current = request.RequestUri;
27 | try
28 | {
29 | //通过nacos sdk获取注册中心服务地址,内置了随机负载均衡算法,所以只返回一条信息
30 | var instance = await _serverManager.SelectOneHealthyInstance(current.Host);
31 | request.RequestUri = new Uri($"http://{instance.Ip}:{instance.Port}{current.PathAndQuery}");
32 | return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
33 | }
34 | catch (Exception e)
35 | {
36 | _logger?.LogDebug(e, "Exception during SendAsync()");
37 | throw;
38 | }
39 | finally
40 | {
41 | request.RequestUri = current;
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/demo/Test.Client/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using BenchmarkDotNet.Running;
4 | using DotNetCoreRpc.Client;
5 | using DotNetCoreRpc.Core;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using Test.IService;
8 | using Test.Model;
9 | using System.Threading.Tasks;
10 | using Microsoft.Extensions.Configuration;
11 | using Nacos.V2.DependencyInjection;
12 | using System.Collections.Generic;
13 | using System.Net.Http;
14 | using System.Net;
15 |
16 | namespace Test.Client
17 | {
18 | class Program
19 | {
20 | //TestServer服务名称
21 | const string TestServerName = "TestServer";
22 |
23 | static async Task Main(string[] args)
24 | {
25 | var builder = new ConfigurationBuilder()
26 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
27 | var configuration = builder.Build();
28 |
29 | IServiceCollection services = new ServiceCollection();
30 | services.AddLogging()
31 | //单机版Httpclient配置
32 | .AddHttpClient(TestServerName, client =>
33 | {
34 | client.BaseAddress = new Uri("http://localhost:34047");
35 |
36 | //http2.0或3.0
37 | //client.BaseAddress = new Uri("https://localhost:5001");
38 | //client.DefaultRequestVersion = HttpVersion.Version30;
39 | ////client.DefaultRequestVersion = HttpVersion.Version20;
40 | //client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact;
41 | })
42 | //.net8 or later
43 | //.AddAsKeyed()
44 | .AddDotNetCoreRpcClient(options =>
45 | {
46 | //.net8 or later
47 | //options.Lifetime = ServiceLifetime.Transient;
48 | //options.AddAsKeyed = true;
49 |
50 | options.Path = "/Test.Server6";
51 | options.AddRpcClient().AddRpcClient();
52 | });
53 | //基于Nacos注册中心
54 | //.AddNacosV2Naming(configuration)
55 | //.AddScoped()
56 | //.AddHttpClient(TestServerName, client =>
57 | //{
58 | // client.BaseAddress = new Uri($"http://{TestServerName}");
59 | //})
60 | //.AddHttpMessageHandler()
61 | //.AddDotNetCoreRpcClient(options => {
62 | // options.Path = "/Test.Server6";
63 | // options.AddRpcClient().AddRpcClient();
64 | //});
65 |
66 | using var scope = services.BuildServiceProvider().CreateScope();
67 | IServiceProvider serviceProvider = scope.ServiceProvider;
68 | //.net8 or later
69 | //IPersonService personService = serviceProvider.GetKeyedService(TestServerName);
70 | IPersonService personService = serviceProvider.GetService();
71 |
72 | //int maxCount = 10000;
73 |
74 | //Stopwatch stopwatch = new Stopwatch();
75 | //Console.WriteLine($"Add开始执行");
76 | //stopwatch.Start();
77 | //for (int i = 0; i < maxCount; i++)
78 | //{
79 | // PersonModel person = new PersonModel
80 | // {
81 | // Id = ++i,
82 | // IdCardNo = 5555555,
83 | // BirthDay = DateTime.Now,
84 | // HasMoney = false
85 | // };
86 | // person.Name = "softlgl" + person.Id;
87 | // bool add = personService.Add(person);
88 | //}
89 | //stopwatch.Stop();
90 | //Console.WriteLine($"Add执行完成,总用时:{stopwatch.ElapsedMilliseconds}ms");
91 |
92 | //stopwatch = new Stopwatch();
93 | //maxCount = 10000;
94 | //Console.WriteLine($"Get开始执行");
95 | //stopwatch.Start();
96 | //for (int i = 0; i < maxCount; i++)
97 | //{
98 | // personService.Get(++i);
99 | //}
100 | //stopwatch.Stop();
101 | //Console.WriteLine($"Get执行完成,总用时:{stopwatch.ElapsedMilliseconds}ms");
102 |
103 | //Stopwatch stopwatch = new Stopwatch();
104 | //Console.WriteLine($"Add开始执行");
105 | //stopwatch.Start();
106 | //Parallel.For(0,maxCount,i=> {
107 | // PersonModel person = new PersonModel
108 | // {
109 | // Id = ++i,
110 | // IdCardNo = 5555555,
111 | // BirthDay = DateTime.Now,
112 | // HasMoney = false
113 | // };
114 | // person.Name = "softlgl" + person.Id;
115 | // bool add = personService.Add(person);
116 | //});
117 | //stopwatch.Stop();
118 | //Console.WriteLine($"Add执行完成,总用时:{stopwatch.ElapsedMilliseconds}ms");
119 |
120 | //stopwatch = new Stopwatch();
121 | //maxCount = 10000;
122 | //Console.WriteLine($"Get开始执行");
123 | //stopwatch.Start();
124 | //Parallel.For(0, maxCount,i => {
125 | // personService.Get(++i);
126 | //});
127 | //stopwatch.Stop();
128 | //Console.WriteLine($"Get执行完成,总用时:{stopwatch.ElapsedMilliseconds}ms");
129 |
130 | PersonModel person = new PersonModel
131 | {
132 | Id = 1,
133 | Name = "softlgl",
134 | IdCardNo = 5555555,
135 | BirthDay = DateTime.Now,
136 | HasMoney = false
137 | };
138 | bool add = await personService.Add(person);
139 | Console.WriteLine($"添加Person1:{add}");
140 | person = personService.Get(1);
141 | Console.WriteLine($"获取Person,id=1,person=[{person.ToJson()}]");
142 | person = new PersonModel
143 | {
144 | Id = 2,
145 | Name = "yi念之间",
146 | IdCardNo = 666888,
147 | BirthDay = DateTime.Now,
148 | HasMoney = false
149 | };
150 | add = await personService.Add(person);
151 | Console.WriteLine($"添加Person2:{add}");
152 | var persons = await personService.GetPersons();
153 | Console.WriteLine($"获取Persons,persons=[{persons.ToJson()}]");
154 | await personService.Edit(1);
155 | Console.WriteLine($"修改Person,id=1完成");
156 | personService.Delete(1);
157 | Console.WriteLine($"删除Person,id=1完成");
158 | persons = await personService.GetPersons();
159 | Console.WriteLine($"最后获取Persons,persons=[{persons.ToJson()}]");
160 |
161 | Stopwatch stopwatch = Stopwatch.StartNew();
162 | for (int i = 0; i < 1000; i++)
163 | {
164 | await personService.GetPersons();
165 | }
166 | stopwatch.Stop();
167 | Console.WriteLine($"await:{stopwatch.Elapsed.TotalMilliseconds}");
168 |
169 | stopwatch.Reset();
170 | stopwatch.Start();
171 | List tasks = new List();
172 | for (int i = 0; i < 1000; i++)
173 | {
174 | tasks.Add(personService.GetPersons());
175 | }
176 | await Task.WhenAll(tasks);
177 | stopwatch.Stop();
178 | Console.WriteLine($"tasks await:{stopwatch.Elapsed.TotalMilliseconds}");
179 |
180 | //.net8 or later
181 | //IProductService productService = serviceProvider.GetKeyedService(TestServerName);
182 | IProductService productService = serviceProvider.GetService();
183 | ProductDto product = new ProductDto
184 | {
185 | Id = 1000,
186 | Name="抗原",
187 | Price = 158.22M
188 | };
189 | int productAddResult = await productService.Add(product);
190 | Console.WriteLine($"添加Product1:{productAddResult==1}");
191 | product = productService.Get(1000);
192 | Console.WriteLine($"获取添加Product1,id=1000,person=[{product.ToJson()}]");
193 | product = new ProductDto
194 | {
195 | Id = 2000,
196 | Name = "N95口罩",
197 | Price = 35.5M
198 | };
199 | productAddResult = await productService.Add(product);
200 | Console.WriteLine($"添加Product2:{productAddResult == 1}");
201 | product = productService.Get(2000);
202 | Console.WriteLine($"获取添加Product2,id=2000,person=[{product.ToJson()}]");
203 | var products = await productService.GetProducts();
204 | Console.WriteLine($"products=[{products.ToJson()}]");
205 | ValueTask editTask = productService.Edit(1);
206 | await editTask;
207 | Console.WriteLine($"修改Product,id=1完成");
208 |
209 | //BenchmarkRunner.Run();
210 |
211 | Console.ReadLine();
212 | }
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/demo/Test.Client/RpcClientBenchTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using BenchmarkDotNet.Attributes;
4 | using BenchmarkDotNet.Jobs;
5 | using DotNetCoreRpc.Client;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using Test.IService;
8 | using Test.Model;
9 |
10 | namespace Test.Client
11 | {
12 | [SimpleJob(RuntimeMoniker.Net70)]
13 | [MemoryDiagnoser]
14 | public class RpcClientBenchTest
15 | {
16 | private readonly IServiceCollection services;
17 | private readonly IServiceProvider serviceProvider;
18 | private readonly IPersonService personService;
19 |
20 | public RpcClientBenchTest()
21 | {
22 | services = new ServiceCollection();
23 | services.AddHttpClient("TestServer", client => { client.BaseAddress = new Uri("http://localhost:34047/Test.Server6"); })
24 | .AddDotNetCoreRpcClient(options =>
25 | {
26 | options.AddRpcClient().AddRpcClient();
27 | });
28 |
29 | serviceProvider=services.BuildServiceProvider();
30 | personService = serviceProvider.GetService();
31 |
32 | PersonModel person = new PersonModel
33 | {
34 | Id = 1,
35 | IdCardNo = 5555555,
36 | BirthDay = DateTime.Now,
37 | HasMoney = false
38 | };
39 | person.Name = "softlgl" + person.Id;
40 | personService.Add(person).GetAwaiter().GetResult();
41 | }
42 |
43 | [Benchmark]
44 | public void GetTest()
45 | {
46 | personService.Get(1);
47 | }
48 |
49 |
50 | [Benchmark]
51 | public async Task GetListTest()
52 | {
53 | await personService.GetPersons();
54 | }
55 |
56 | //[Benchmark]
57 | //public async void AddTest()
58 | //{
59 | // for (int i = 0; i < maxCount; i++)
60 | // {
61 | // PersonModel person = new PersonModel
62 | // {
63 | // Id = ++i,
64 | // IdCardNo = 5555555,
65 | // BirthDay = DateTime.Now,
66 | // HasMoney = false
67 | // };
68 | // person.Name = "softlgl" + person.Id;
69 | // bool add = await personService.Add(person);
70 | // }
71 | //}
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/demo/Test.Client/Test.Client.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Always
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/demo/Test.Client/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "nacos": {
10 | "ServerAddresses": [ "http://localhost:8848" ],
11 | "ServiceName": "apigateway",
12 | "DefaultTimeOut": 15000,
13 | //自定义Namespace的Id
14 | "Namespace": "01761089-727d-49cb-9abe-fbe647f83927",
15 | "GroupName": "DEFAULT_GROUP",
16 | "ClusterName": "DEFAULT",
17 | "ListenInterval": 1000,
18 | "RegisterEnabled": true,
19 | "InstanceEnabled": true,
20 | "LBStrategy": "WeightRandom",
21 | "NamingUseRpc": true
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/demo/Test.DAL/PersonDal.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Test.IDAL;
5 | using Test.Model;
6 |
7 | namespace Test.DAL
8 | {
9 | public class PersonDal:IPersonDal
10 | {
11 | private List persons = new List();
12 |
13 | public bool Add(PersonModel person)
14 | {
15 | if (persons.Any(i => i.Id == person.Id))
16 | {
17 | return false;
18 | }
19 | persons.Add(person);
20 | return true;
21 | }
22 |
23 | public void Delete(int id)
24 | {
25 | var person = persons.FirstOrDefault(i => i.Id == id);
26 | if (person != null)
27 | {
28 | persons.Remove(person);
29 | }
30 | }
31 |
32 | public PersonModel Get(int id)
33 | {
34 | return persons.FirstOrDefault(i => i.Id == id);
35 | }
36 |
37 | public List GetPersons()
38 | {
39 | return persons;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/demo/Test.DAL/ProductDal.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Test.IDAL;
5 | using Test.Model;
6 |
7 | namespace Test.DAL
8 | {
9 | public class ProductDal : IProductDal
10 | {
11 | private List products = new List();
12 |
13 | public bool Add(ProductDto product)
14 | {
15 | if (products.Any(i => i.Id == product.Id))
16 | {
17 | return false;
18 | }
19 | products.Add(product);
20 | return true;
21 | }
22 |
23 | public void Delete(int id)
24 | {
25 | var person = products.FirstOrDefault(i => i.Id == id);
26 | if (person != null)
27 | {
28 | products.Remove(person);
29 | }
30 | }
31 |
32 | public ProductDto Get(int id)
33 | {
34 | return products.FirstOrDefault(i => i.Id == id);
35 | }
36 |
37 | public List GetProducts()
38 | {
39 | return products;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/demo/Test.DAL/Test.DAL.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/demo/Test.IDAL/IPersonDal.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Test.Model;
4 |
5 | namespace Test.IDAL
6 | {
7 | public interface IPersonDal
8 | {
9 | PersonModel Get(int id);
10 | List GetPersons();
11 | bool Add(PersonModel person);
12 | void Delete(int id);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/demo/Test.IDAL/IProductDal.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Test.Model;
3 |
4 | namespace Test.IDAL
5 | {
6 | public interface IProductDal
7 | {
8 | ProductDto Get(int id);
9 | List GetProducts();
10 | bool Add(ProductDto product);
11 | void Delete(int id);
12 | }
13 | }
--------------------------------------------------------------------------------
/demo/Test.IDAL/Test.IDAL.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/demo/Test.IService/Class1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Test.IService
4 | {
5 | public class Class1
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/demo/Test.IService/IPersonService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using Test.Model;
5 |
6 | namespace Test.IService
7 | {
8 | public interface IPersonService
9 | {
10 | PersonModel Get(int id);
11 | Task> GetPersons();
12 | ValueTask Add(PersonModel person);
13 | void Delete(int id);
14 | Task Edit(int id);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/demo/Test.IService/IProductService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using Test.Model;
5 |
6 | namespace Test.IService
7 | {
8 | public interface IProductService
9 | {
10 | ProductDto Get(int id);
11 | Task> GetProducts();
12 | ValueTask Add(ProductDto person);
13 | void Delete(int id);
14 | ValueTask Edit(int id);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/demo/Test.IService/Test.IService.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/demo/Test.Model/PersonModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | namespace Test.Model
3 | {
4 | public class PersonModel
5 | {
6 | public int Id { get; set; }
7 | public long IdCardNo { get; set; }
8 | public string Name { get; set; }
9 | public DateTime BirthDay { get; set; }
10 | public bool HasMoney { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/demo/Test.Model/ProductDto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | namespace Test.Model
3 | {
4 | public class ProductDto
5 | {
6 | public long Id { get; set; }
7 | public string Name { get; set; }
8 | public decimal Price { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/demo/Test.Model/Test.Model.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/demo/Test.Server/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Hosting;
6 | using Microsoft.Extensions.Configuration;
7 | using Microsoft.Extensions.Hosting;
8 | using Microsoft.Extensions.Logging;
9 |
10 | namespace Test.Server
11 | {
12 | public class Program
13 | {
14 | public static void Main(string[] args)
15 | {
16 | CreateHostBuilder(args).Build().Run();
17 | }
18 |
19 | public static IHostBuilder CreateHostBuilder(string[] args) =>
20 | Host.CreateDefaultBuilder(args)
21 | .ConfigureWebHostDefaults(webBuilder =>
22 | {
23 | webBuilder.UseStartup();
24 | });
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/demo/Test.Server/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:37526"
7 | }
8 | },
9 | "profiles": {
10 | "IIS Express": {
11 | "commandName": "IISExpress",
12 | "launchBrowser": true,
13 | "environmentVariables": {
14 | "ASPNETCORE_ENVIRONMENT": "Development"
15 | }
16 | },
17 | "Test.Server": {
18 | "commandName": "Project",
19 | "launchBrowser": true,
20 | "environmentVariables": {
21 | "ASPNETCORE_ENVIRONMENT": "Development"
22 | },
23 | "applicationUrl": "http://0.0.0.0:34047"
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/demo/Test.Server/Startup.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softlgl/DotNetCoreRpc/fe911cfd41029b336af8d03eb4e33ded1f7d7776/demo/Test.Server/Startup.cs
--------------------------------------------------------------------------------
/demo/Test.Server/Test.Server.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/demo/Test.Server/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/demo/Test.Server/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "nacos": {
11 | "ServerAddresses": [ "http://localhost:8848" ],
12 | "ServiceName": "testserver",
13 | "DefaultTimeOut": 15000,
14 | //自定义Namespace的Id
15 | "Namespace": "01761089-727d-49cb-9abe-fbe647f83927",
16 | "GroupName": "DEFAULT_GROUP",
17 | "ClusterName": "DEFAULT",
18 | "ListenInterval": 1000,
19 | "RegisterEnabled": true,
20 | "InstanceEnabled": true,
21 | "LBStrategy": "WeightRandom",
22 | "NamingUseRpc": true
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/demo/Test.Server6/Program.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softlgl/DotNetCoreRpc/fe911cfd41029b336af8d03eb4e33ded1f7d7776/demo/Test.Server6/Program.cs
--------------------------------------------------------------------------------
/demo/Test.Server6/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:31355",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "Test.Server6": {
12 | "commandName": "Project",
13 | "dotnetRunMessages": true,
14 | "launchBrowser": true,
15 | "applicationUrl": "http://0.0.0.0:34047",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "IIS Express": {
21 | "commandName": "IISExpress",
22 | "launchBrowser": true,
23 | "environmentVariables": {
24 | "ASPNETCORE_ENVIRONMENT": "Development"
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/demo/Test.Server6/Test.Server6.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/demo/Test.Server6/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/demo/Test.Server6/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "nacos": {
9 | "ServerAddresses": [ "http://localhost:8848" ],
10 | "ServiceName": "testserver",
11 | "DefaultTimeOut": 15000,
12 | //自定义Namespace的Id
13 | "Namespace": "01761089-727d-49cb-9abe-fbe647f83927",
14 | "GroupName": "DEFAULT_GROUP",
15 | "ClusterName": "DEFAULT",
16 | "ListenInterval": 1000,
17 | "RegisterEnabled": true,
18 | "InstanceEnabled": true,
19 | "LBStrategy": "WeightRandom",
20 | "NamingUseRpc": true
21 | },
22 | "AllowedHosts": "*"
23 | }
24 |
--------------------------------------------------------------------------------
/demo/Test.Service/Class1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Test.Service
4 | {
5 | public class Class1
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/demo/Test.Service/Configs/ElasticSearchConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | namespace Test.Service.Configs
3 | {
4 | public class ElasticSearchConfig
5 | {
6 | public string Address { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/demo/Test.Service/Configs/RedisConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | namespace Test.Service.Configs
3 | {
4 | public class RedisConfig
5 | {
6 | public string Address { get; set; }
7 | public int db { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/demo/Test.Service/Filters/CacheFilter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Net.Http.Headers;
4 | using System.Threading.Tasks;
5 | using DotNetCoreRpc.Core;
6 | using DotNetCoreRpc.Server.RpcBuilder;
7 | using Microsoft.Extensions.Logging;
8 | using Test.Service.Configs;
9 |
10 | namespace Test.Service.Filters
11 | {
12 | public class CacheFilter : RpcFilterAttribute
13 | {
14 | //private readonly ElasticSearchConfig _elasticSearchConfig;
15 |
16 | //[FromServices]
17 | private RedisConfig RedisConfig { get; set; }
18 |
19 | #if NET8_0_OR_GREATER
20 | [FromServices("elasticSearchConfig")]
21 | private ElasticSearchConfig ElasticSearchConfig { get; set; }
22 | #endif
23 |
24 | [FromServices]
25 | private ILogger Logger { get; set; }
26 |
27 | //public CacheFilter(ElasticSearchConfig elasticSearchConfig)
28 | //{
29 | // _elasticSearchConfig = elasticSearchConfig;
30 | //}
31 |
32 | public override async Task InvokeAsync(RpcContext context, RpcRequestDelegate next)
33 | {
34 | Logger.LogInformation($"CacheFilter begin,Parameters={context.Parameters}");
35 | await next(context);
36 | Logger.LogInformation($"CacheFilter end,ReturnValue={context.ReturnValue.ToJson()}");
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/demo/Test.Service/Filters/LoggerFilter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading.Tasks;
4 | using DotNetCoreRpc.Core;
5 | using DotNetCoreRpc.Server.RpcBuilder;
6 | using Microsoft.Extensions.Logging;
7 | using Test.Service.Configs;
8 |
9 | namespace Test.Service.Filters
10 | {
11 | public class LoggerFilter:RpcFilterAttribute
12 | {
13 | [FromServices]
14 | private RedisConfig RedisConfig { get; set; }
15 |
16 | #if NET8_0_OR_GREATER
17 | [FromServices("elasticSearchConfig")]
18 | private ElasticSearchConfig ElasticSearchConfig { get; set; }
19 | #endif
20 |
21 | [FromServices]
22 | private ILogger Logger { get; set; }
23 |
24 | public override async Task InvokeAsync(RpcContext context, RpcRequestDelegate next)
25 | {
26 | Logger.LogInformation($"LoggerFilter begin,Parameters={context.Parameters[0].ToJson()}");
27 | await next(context);
28 | Logger.LogInformation($"LoggerFilter end,ReturnValue={context.ReturnValue.ToJson()}");
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/demo/Test.Service/PersonService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using Test.IDAL;
5 | using Test.IService;
6 | using Test.Model;
7 | using Test.Service.Filters;
8 |
9 | namespace Test.Service
10 | {
11 | public class PersonService:IPersonService
12 | {
13 | private readonly IPersonDal _personDal;
14 | public PersonService(IPersonDal personDal)
15 | {
16 | _personDal = personDal;
17 | }
18 |
19 | [LoggerFilter]
20 | public ValueTask Add(PersonModel person)
21 | {
22 | return new ValueTask(_personDal.Add(person));
23 | }
24 |
25 | public void Delete(int id)
26 | {
27 | _personDal.Delete(id);
28 | }
29 |
30 | public Task Edit(int id)
31 | {
32 | return Task.CompletedTask;
33 | }
34 |
35 | [LoggerFilter]
36 | public PersonModel Get(int id)
37 | {
38 | return _personDal.Get(id);
39 | }
40 |
41 | public async Task> GetPersons()
42 | {
43 | return await Task.FromResult(_personDal.GetPersons());
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/demo/Test.Service/ProductService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using Test.IDAL;
5 | using Test.IService;
6 | using Test.Model;
7 | using Test.Service.Filters;
8 |
9 | namespace Test.Service
10 | {
11 | public class ProductService:IProductService
12 | {
13 | private readonly IProductDal _productDal;
14 | public ProductService(IProductDal productDal)
15 | {
16 | _productDal = productDal;
17 | }
18 |
19 | [LoggerFilter]
20 | public ValueTask Add(ProductDto person)
21 | {
22 | bool result =_productDal.Add(person);
23 | return new ValueTask(result?1:0);
24 | }
25 |
26 | public void Delete(int id)
27 | {
28 | _productDal.Delete(id);
29 | }
30 |
31 | public ValueTask Edit(int id)
32 | {
33 | return new ValueTask(Task.CompletedTask);
34 | }
35 |
36 | [LoggerFilter]
37 | public ProductDto Get(int id)
38 | {
39 | return _productDal.Get(id);
40 | }
41 |
42 | public async Task> GetProducts()
43 | {
44 | return await Task.FromResult(_productDal.GetProducts());
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/demo/Test.Service/Test.Service.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1;net5.0;net6.0;net7.0;net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/docs/1.1.3及之前版本.md:
--------------------------------------------------------------------------------
1 | #### Client端配置使用
2 | 首先新建任意形式的.net core宿主,为了简单我使用的是Console程序,引入DotNetCoreRpc.Client包和DependencyInjection相关包
3 | ```
4 |
5 | ```
6 | 引入自己的服务接口包我这里是Test.IService,只需要引入interface层即可,写入如下测试代码,具体代码可参阅demo,由于DotNetCoreRpc通信是基于HttpClientFactory的,所以需要注册HttpClientFactory
7 | ```cs
8 | class Program
9 | {
10 | //TestServer服务名称
11 | const string TestServerName = "TestServer";
12 |
13 | static async Task Main(string[] args)
14 | {
15 | var builder = new ConfigurationBuilder()
16 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
17 | var configuration = builder.Build();
18 |
19 | IServiceCollection services = new ServiceCollection();
20 | services.AddLogging().AddDotNetCoreRpcClient()
21 | //单机版Httpclient配置
22 | .AddHttpClient(TestServerName, client => { client.BaseAddress = new Uri("http://localhost:34047/Test.Server6"); });
23 | //基于Nacos注册中心
24 | //.AddNacosV2Naming(configuration)
25 | //.AddScoped()
26 | //.AddHttpClient(TestServerName, client =>
27 | //{
28 | // client.BaseAddress = new Uri($"http://{TestServerName}/Test.Server6");
29 | //}).AddHttpMessageHandler();
30 |
31 | IServiceProvider serviceProvider = services.BuildServiceProvider();
32 | RpcClient rpcClient = serviceProvider.GetRequiredService();
33 | IPersonService personService = rpcClient.CreateClient(TestServerName);
34 |
35 | PersonModel person = new PersonModel
36 | {
37 | Id = 1,
38 | Name = "softlgl",
39 | IdCardNo = 5555555,
40 | BirthDay = DateTime.Now,
41 | HasMoney = false
42 | };
43 | bool add = await personService.Add(person);
44 | Console.WriteLine($"添加Person1:{add}");
45 | person = personService.Get(1);
46 | Console.WriteLine($"获取Person,id=1,person=[{person.ToJson()}]");
47 | person = new PersonModel
48 | {
49 | Id = 2,
50 | Name = "yi念之间",
51 | IdCardNo = 666888,
52 | BirthDay = DateTime.Now,
53 | HasMoney = false
54 | };
55 | add = await personService.Add(person);
56 | Console.WriteLine($"添加Person2:{add}");
57 | var persons = await personService.GetPersons();
58 | Console.WriteLine($"获取Persons,persons=[{persons.ToJson()}]");
59 | await personService.Edit(1);
60 | Console.WriteLine($"修改Person,id=1完成");
61 | personService.Delete(1);
62 | Console.WriteLine($"删除Person,id=1完成");
63 | persons = await personService.GetPersons();
64 | Console.WriteLine($"最后获取Persons,persons=[{persons.ToJson()}]");
65 |
66 | IProductService productService = rpcClient.CreateClient(TestServerName);
67 | ProductDto product = new ProductDto
68 | {
69 | Id = 1000,
70 | Name="抗原",
71 | Price = 158.22M
72 | };
73 | int productAddResult = await productService.Add(product);
74 | Console.WriteLine($"添加Product1:{productAddResult==1}");
75 | product = productService.Get(1000);
76 | Console.WriteLine($"获取添加Product1,id=1000,person=[{product.ToJson()}]");
77 | product = new ProductDto
78 | {
79 | Id = 2000,
80 | Name = "N95口罩",
81 | Price = 35.5M
82 | };
83 | productAddResult = await productService.Add(product);
84 | Console.WriteLine($"添加Product2:{productAddResult == 1}");
85 | product = productService.Get(2000);
86 | Console.WriteLine($"获取添加Product2,id=2000,person=[{product.ToJson()}]");
87 | var products = await productService.GetProducts();
88 | Console.WriteLine($"products=[{products.ToJson()}]");
89 | Task editTask = productService.Edit(1);
90 | await editTask;
91 | Console.WriteLine($"修改Product,id=1完成");
92 |
93 | Console.ReadLine();
94 | }
95 | }
96 | ```
97 | #### Server端配置使用
98 |
99 | 新建一个最简单的Asp.net Core项目,我这里的Demo是新建的Asp.net Core的空项目,引入DotNetCoreRpc.Server包
100 | ```
101 |
102 | ```
103 | 然后添加注入和相关中间件
104 | ```cs
105 | public class Startup
106 | {
107 | public void ConfigureServices(IServiceCollection services)
108 | {
109 | services.AddSingleton()
110 | .AddSingleton()
111 | .AddSingleton()
112 | .AddSingleton()
113 | .AddSingleton(new RedisConfig { Address="127.0.0.1:6379",db=10 })
114 | .AddSingleton(new ElasticSearchConfig { Address = "127.0.0.1:9200" })
115 | //*注册DotNetCoreRpcServer
116 | .AddDotNetCoreRpcServer(options => {
117 | //*确保以下添加的服务已经被注册到DI容器
118 |
119 | //添加作为服务的接口
120 | //options.AddService();
121 |
122 | //或添加作为服务的接口以xxx为结尾的接口
123 | //options.AddService("*Service");
124 |
125 | //或添加具体名称为xxx的接口
126 | //options.AddService("IPersonService");
127 | //或具体命名空间下的接口
128 | options.AddNameSpace("Test.IService");
129 |
130 | //添加全局过滤器
131 | options.AddFilter();
132 | });
133 | }
134 |
135 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
136 | {
137 | //通过中间件的方式引入
138 | //app.UseDotNetCoreRpc();
139 | app.UseRouting();
140 | app.UseEndpoints(endpoint => {
141 | endpoint.Map("/", async context=>await context.Response.WriteAsync("server start!"));
142 | //通过endpoint的方式引入
143 | endpoint.MapDotNetCoreRpc();
144 | //endpoint.MapDotNetCoreRpc("/Test.Server6");
145 | });
146 | }
147 | }
148 | ```
149 | 如果是ASP.NET Core 6的Minimal Api则使用以下方式
150 | ```cs
151 | var builder = WebApplication.CreateBuilder(args);
152 |
153 | builder.Services.AddSingleton()
154 | .AddSingleton()
155 | .AddSingleton()
156 | .AddSingleton()
157 | .AddSingleton(new RedisConfig { Address = "127.0.0.1:6379", db = 10 })
158 | .AddSingleton(new ElasticSearchConfig { Address = "127.0.0.1:9200" })
159 | .AddDotNetCoreRpcServer(options => {
160 | //options.AddService();
161 | //options.AddService("*Service");
162 | //options.AddService("IPersonService");
163 | options.AddNameSpace("Test.IService");
164 | options.AddFilter();
165 | });
166 |
167 | app.UseDotNetCoreRpc();
168 | //app.UseDotNetCoreRpc("/Test.Server6");
169 | app.MapGet("/", () => "Hello World!");
170 |
171 | app.Run();
172 | ```
173 | 过滤器的使用方式,可添加到类上或者方法上或者全局注册,优先级 方法>类>全局注册,RpcFilterAttribute是基于管道模式执行的,可支持注册多个Filter,支持属性注入。
174 | ```cs
175 | public class CacheFilter : RpcFilterAttribute
176 | {
177 | private readonly ElasticSearchConfig _elasticSearchConfig;
178 |
179 | [FromServices]
180 | private RedisConfig RedisConfig { get; set; }
181 |
182 | [FromServices]
183 | private ILogger Logger { get; set; }
184 |
185 | public CacheFilter(ElasticSearchConfig elasticSearchConfig)
186 | {
187 | _elasticSearchConfig = elasticSearchConfig;
188 | }
189 | public override async Task InvokeAsync(RpcContext context, RpcRequestDelegate next)
190 | {
191 | Logger.LogInformation($"CacheFilter begin,Parameters={context.Parameters}");
192 | await next(context);
193 | Logger.LogInformation($"CacheFilter end,ReturnValue={context.ReturnValue.ToJson()}");
194 | }
195 | }
196 | ```
197 |
--------------------------------------------------------------------------------
/docs/1.1.3更高版本.md:
--------------------------------------------------------------------------------
1 | 优化了以前Client端和Server端的注册方式,使其操作起来更简单。
2 | + 客户端为`AddRpcClient`添加服务,使得调用看起来更清晰。
3 | + 服务端去除扫描注册,根据动态扫描服务类型,简化操作。
4 |
5 | #### Client端配置使用
6 | 首先新建任意形式的.net core宿主,为了简单我使用的是Console程序,引入DotNetCoreRpc.Client包和DependencyInjection相关包
7 | ```
8 |
9 | ```
10 | 引入自己的服务接口包我这里是Test.IService,只需要引入interface层即可,写入如下测试代码,具体代码可参阅demo,由于DotNetCoreRpc通信是基于HttpClientFactory的,所以需要注册HttpClientFactory
11 | ```cs
12 | class Program
13 | {
14 | //TestServer服务名称
15 | const string TestServerName = "TestServer";
16 |
17 | static async Task Main(string[] args)
18 | {
19 | var builder = new ConfigurationBuilder()
20 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
21 | var configuration = builder.Build();
22 |
23 | IServiceCollection services = new ServiceCollection();
24 | services.AddLogging()
25 | //单机版Httpclient配置
26 | .AddHttpClient(TestServerName, client => { client.BaseAddress = new Uri("http://localhost:34047"); })
27 | //.net8 or later
28 | //.AddAsKeyed()
29 | .AddDotNetCoreRpcClient(options => {
30 | //.net8 or later
31 | //options.Lifetime = ServiceLifetime.Transient;
32 | //options.AddAsKeyed = true;
33 |
34 | options.Path = "/Test.Server6";
35 | options.AddRpcClient().AddRpcClient();
36 | });
37 | //基于Nacos注册中心
38 | //.AddNacosV2Naming(configuration)
39 | //.AddScoped()
40 | //.AddHttpClient(TestServerName, client =>
41 | //{
42 | // client.BaseAddress = new Uri($"http://{TestServerName}");
43 | //}).AddHttpMessageHandler()
44 | //.AddDotNetCoreRpcClient(options => {
45 | // options.Path = "/Test.Server6";
46 | // options.AddRpcClient().AddRpcClient();
47 | //});
48 |
49 | using var scope = services.BuildServiceProvider().CreateScope();
50 | IServiceProvider serviceProvider = scope.ServiceProvider;
51 | //.net8 or later
52 | //IPersonService personService = serviceProvider.GetKeyedService(TestServerName);
53 | IPersonService personService = serviceProvider.GetService();
54 |
55 | PersonModel person = new PersonModel
56 | {
57 | Id = 1,
58 | Name = "softlgl",
59 | IdCardNo = 5555555,
60 | BirthDay = DateTime.Now,
61 | HasMoney = false
62 | };
63 | bool add = await personService.Add(person);
64 | Console.WriteLine($"添加Person1:{add}");
65 | person = personService.Get(1);
66 | Console.WriteLine($"获取Person,id=1,person=[{person.ToJson()}]");
67 | person = new PersonModel
68 | {
69 | Id = 2,
70 | Name = "yi念之间",
71 | IdCardNo = 666888,
72 | BirthDay = DateTime.Now,
73 | HasMoney = false
74 | };
75 | add = await personService.Add(person);
76 | Console.WriteLine($"添加Person2:{add}");
77 | var persons = await personService.GetPersons();
78 | Console.WriteLine($"获取Persons,persons=[{persons.ToJson()}]");
79 | await personService.Edit(1);
80 | Console.WriteLine($"修改Person,id=1完成");
81 | personService.Delete(1);
82 | Console.WriteLine($"删除Person,id=1完成");
83 | persons = await personService.GetPersons();
84 | Console.WriteLine($"最后获取Persons,persons=[{persons.ToJson()}]");
85 |
86 | //.net8 or later
87 | //IProductService productService = serviceProvider.GetKeyedService(TestServerName);
88 | IProductService productService = serviceProvider.GetService();
89 | ProductDto product = new ProductDto
90 | {
91 | Id = 1000,
92 | Name="抗原",
93 | Price = 158.22M
94 | };
95 | int productAddResult = await productService.Add(product);
96 | Console.WriteLine($"添加Product1:{productAddResult==1}");
97 | product = productService.Get(1000);
98 | Console.WriteLine($"获取添加Product1,id=1000,person=[{product.ToJson()}]");
99 | product = new ProductDto
100 | {
101 | Id = 2000,
102 | Name = "N95口罩",
103 | Price = 35.5M
104 | };
105 | productAddResult = await productService.Add(product);
106 | Console.WriteLine($"添加Product2:{productAddResult == 1}");
107 | product = productService.Get(2000);
108 | Console.WriteLine($"获取添加Product2,id=2000,person=[{product.ToJson()}]");
109 | var products = await productService.GetProducts();
110 | Console.WriteLine($"products=[{products.ToJson()}]");
111 | Task editTask = productService.Edit(1);
112 | await editTask;
113 | Console.WriteLine($"修改Product,id=1完成");
114 |
115 | Console.ReadLine();
116 | }
117 | }
118 | ```
119 | #### Server端配置使用
120 |
121 | 新建一个最简单的Asp.net Core项目,我这里的Demo是新建的Asp.net Core的空项目,引入DotNetCoreRpc.Server包
122 | ```
123 |
124 | ```
125 | 然后添加注入和相关中间件
126 | ```cs
127 | public class Startup
128 | {
129 | public void ConfigureServices(IServiceCollection services)
130 | {
131 | services.AddSingleton()
132 | .AddSingleton()
133 | .AddSingleton()
134 | .AddSingleton()
135 | .AddSingleton(new RedisConfig { Address="127.0.0.1:6379",db=10 })
136 | .AddSingleton(new ElasticSearchConfig { Address = "127.0.0.1:9200" })
137 | //*注册DotNetCoreRpcServer
138 | .AddDotNetCoreRpcServer(options => {
139 | //添加全局过滤器
140 | options.AddFilter();
141 | });
142 | }
143 |
144 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
145 | {
146 | //通过中间件的方式引入
147 | //app.UseDotNetCoreRpc();
148 | app.UseRouting();
149 | app.UseEndpoints(endpoint => {
150 | endpoint.Map("/", async context=>await context.Response.WriteAsync("server start!"));
151 | //通过endpoint的方式引入
152 | endpoint.MapDotNetCoreRpc();
153 | //endpoint.MapDotNetCoreRpc("/Test.Server6");
154 | });
155 | }
156 | }
157 | ```
158 | 如果是ASP.NET Core 6的Minimal Api则使用以下方式
159 | ```cs
160 | var builder = WebApplication.CreateBuilder(args);
161 |
162 | builder.Services.AddSingleton()
163 | .AddSingleton()
164 | .AddSingleton()
165 | .AddSingleton()
166 | .AddSingleton(new RedisConfig { Address = "127.0.0.1:6379", db = 10 })
167 | .AddSingleton(new ElasticSearchConfig { Address = "127.0.0.1:9200" })
168 | .AddDotNetCoreRpcServer(options => {
169 | options.AddFilter();
170 | });
171 |
172 | var app = builder.Build();
173 |
174 | app.UseDotNetCoreRpc("/Test.Server6");
175 | app.MapGet("/", () => "Hello World!");
176 |
177 | app.Run();
178 | ```
179 | 过滤器的使用方式,可添加到类上或者方法上或者全局注册,优先级 方法>类>全局注册,RpcFilterAttribute是基于管道模式执行的,可支持注册多个Filter,支持属性注入。
180 | ```cs
181 | public class CacheFilter : RpcFilterAttribute
182 | {
183 | private readonly ElasticSearchConfig _elasticSearchConfig;
184 |
185 | [FromServices]
186 | private RedisConfig RedisConfig { get; set; }
187 |
188 | [FromServices]
189 | private ILogger Logger { get; set; }
190 |
191 | public CacheFilter(ElasticSearchConfig elasticSearchConfig)
192 | {
193 | _elasticSearchConfig = elasticSearchConfig;
194 | }
195 | public override async Task InvokeAsync(RpcContext context, RpcRequestDelegate next)
196 | {
197 | Logger.LogInformation($"CacheFilter begin,Parameters={context.Parameters}");
198 | await next(context);
199 | Logger.LogInformation($"CacheFilter end,ReturnValue={context.ReturnValue.ToJson()}");
200 | }
201 | }
202 | ```
203 |
--------------------------------------------------------------------------------
/src/DotNetCoreRpc.Client/ClientOptions.cs:
--------------------------------------------------------------------------------
1 | using Castle.DynamicProxy;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.DependencyInjection.Extensions;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Diagnostics;
7 | using System.IO;
8 | using System.Net.Http;
9 | using System.Reflection;
10 | using System.Text;
11 |
12 | namespace DotNetCoreRpc.Client
13 | {
14 | public class ClientOptions
15 | {
16 | ///
17 | /// 服务请求路径
18 | ///
19 | public string Path { get; set; }
20 |
21 | ///
22 | /// 服务名称
23 | ///
24 | public string ServiceName { get; }
25 |
26 | public IServiceCollection Services { get; }
27 |
28 | public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Scoped;
29 |
30 | #if NET8_0_OR_GREATER
31 | public bool AddAsKeyed { get; set; } = false;
32 | #endif
33 |
34 | public ClientOptions(IServiceCollection services, string serviceName)
35 | {
36 | Services = services;
37 | ServiceName = serviceName;
38 | }
39 |
40 | public ClientOptions AddRpcClient(ServiceLifetime? lifetime = null) where T : class
41 | {
42 | #if NET8_0_OR_GREATER
43 | if (AddAsKeyed)
44 | {
45 | Services.Add(ServiceDescriptor.DescribeKeyed(typeof(T), ServiceName, (provider, _) => CreateRpcClient(provider), lifetime ?? Lifetime));
46 | }
47 | else
48 | {
49 | #endif
50 | Services.Add(ServiceDescriptor.Describe(typeof(T), provider => CreateRpcClient(provider), lifetime ?? Lifetime));
51 | #if NET8_0_OR_GREATER
52 | }
53 | #endif
54 |
55 | T CreateRpcClient(IServiceProvider serviceProvider)
56 | {
57 | var proxyGenerator = serviceProvider.GetRequiredService();
58 | var httpClient = HttpClientFactoryHelper.CreateHttpClient(serviceProvider, ServiceName, Path);
59 |
60 | RpcClient rpcClient = new RpcClient(httpClient, proxyGenerator);
61 | return rpcClient.CreateClient();
62 | }
63 |
64 | return this;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/DotNetCoreRpc.Client/DotNetCoreRpc.Client.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0
5 | DotNetCoreRpc客户端库
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/DotNetCoreRpc.Client/HttpClientFactoryHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using Microsoft.Extensions.DependencyInjection;
4 |
5 | namespace DotNetCoreRpc.Client
6 | {
7 | public static class HttpClientFactoryHelper
8 | {
9 | public static HttpClient CreateHttpClient(IServiceProvider serviceProvider, string serviceName, string path)
10 | {
11 | var httpClientFactory = serviceProvider.GetRequiredService();
12 |
13 | HttpClient httpClient;
14 | #if NET8_0_OR_GREATER
15 | httpClient = serviceProvider.GetKeyedService(serviceName) ?? httpClientFactory.CreateClient(serviceName);
16 | #else
17 | httpClient = httpClientFactory.CreateClient(serviceName);
18 | #endif
19 | InitialHttpClient(httpClient, path);
20 | return httpClient;
21 | }
22 |
23 | private static void InitialHttpClient(HttpClient httpClient, string path)
24 | {
25 | if (httpClient.DefaultRequestHeaders.Contains("req-source"))
26 | {
27 | return;
28 | }
29 |
30 | httpClient.DefaultRequestHeaders.Add("req-source", "dncrpc");
31 | if (!string.IsNullOrWhiteSpace(path))
32 | {
33 | if (path.StartsWith("/"))
34 | {
35 | path = path.TrimStart('/');
36 | }
37 |
38 | httpClient.BaseAddress = new Uri(httpClient.BaseAddress.ToString() + path);
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/DotNetCoreRpc.Client/HttpRequestInterceptor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Http;
4 | using System.Net.Http.Headers;
5 | using System.Reflection;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using Castle.DynamicProxy;
9 | using DotNetCoreRpc.Core;
10 |
11 | namespace DotNetCoreRpc.Client
12 | {
13 | public class HttpRequestInterceptor : IInterceptor
14 | {
15 | private readonly RequestHandler _requestHandler;
16 | public HttpRequestInterceptor(HttpClient httpClient)
17 | {
18 | _requestHandler = new RequestHandler(httpClient);
19 | }
20 |
21 | public void Intercept(IInvocation invocation)
22 | {
23 | var methodReturnType = invocation.Method.ReturnType.GetTypeInfo();
24 |
25 | if (!methodReturnType.IsAsync())
26 | {
27 | var result = _requestHandler.SyncResultHandle(invocation.Method, invocation.Arguments);
28 | if (result == null)
29 | {
30 | return;
31 | }
32 |
33 | invocation.ReturnValue = result;
34 | return;
35 | }
36 |
37 | if (methodReturnType.IsTask())
38 | {
39 | invocation.ReturnValue = _requestHandler.TaskValueTaskWithoutResultHandle(invocation.Method, invocation.Arguments);
40 | return;
41 | }
42 |
43 | if (methodReturnType.IsValueTask())
44 | {
45 | invocation.ReturnValue = new ValueTask(_requestHandler.TaskValueTaskWithoutResultHandle(invocation.Method, invocation.Arguments));
46 | return;
47 | }
48 |
49 | if (methodReturnType.IsTaskWithResult())
50 | {
51 | invocation.ReturnValue = _requestHandler.GetTaskResultHandleFunc(methodReturnType).Invoke(invocation.Method, invocation.Arguments);
52 | return;
53 | }
54 |
55 | if (methodReturnType.IsValueTaskWithResult())
56 | {
57 | invocation.ReturnValue = _requestHandler.GetValueResultHandleFunc(methodReturnType).Invoke(invocation.Method, invocation.Arguments);
58 | return;
59 | }
60 | }
61 |
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/DotNetCoreRpc.Client/RequestHandler.cs:
--------------------------------------------------------------------------------
1 | using Castle.DynamicProxy;
2 | using DotNetCoreRpc.Core;
3 | using System;
4 | using System.Collections.Concurrent;
5 | using System.Collections.Generic;
6 | using System.Linq.Expressions;
7 | using System.Net;
8 | using System.Net.Http;
9 | using System.Net.Http.Headers;
10 | using System.Reflection;
11 | using System.Text;
12 | using System.Text.Json;
13 | using System.Threading;
14 | using System.Threading.Tasks;
15 |
16 | namespace DotNetCoreRpc.Client
17 | {
18 | public class RequestHandler
19 | {
20 | private static readonly ConcurrentDictionary> _taskFuncCache = new ConcurrentDictionary>();
21 | private static readonly ConcurrentDictionary> _valueTaskFuncCache = new ConcurrentDictionary>();
22 |
23 | private readonly HttpClient _httpClient;
24 | public RequestHandler(HttpClient httpClient)
25 | {
26 | _httpClient = httpClient;
27 | }
28 |
29 | public object SyncResultHandle(MethodInfo methodInfo, params object[] arguments)
30 | {
31 | return TaskResultHandle