├── .gitignore
├── Directory.Build.props
├── LICENSE
├── PluginFactory.sln
├── README.md
├── doc
└── api.md
├── package-icon.png
├── samples
└── TestPluginA
│ ├── ShareOptions.cs
│ ├── TestConfigPlugin.cs
│ ├── TestConfigPluginWithInit.cs
│ ├── TestInitPlugin.cs
│ ├── TestPlugin.cs
│ └── TestPluginA.csproj
├── src
├── Abstractions
│ ├── Configration
│ │ ├── IPluginConfigrationProvider.cs
│ │ ├── PluginConfigrationOptions.cs
│ │ ├── PluginConfigrationProvider.cs
│ │ └── PluginFactoryConfigration.cs
│ ├── IPlugin.cs
│ ├── IPluginContext.cs
│ ├── IPluginFactory.cs
│ ├── IPluginInitContext.cs
│ ├── IPluginLoader.cs
│ ├── ISupportConfigPlugin.cs
│ ├── ISupportInitPlugin.cs
│ ├── IsolationAssemblyLoadContext.cs
│ ├── PluginAttribute.cs
│ ├── PluginBase.cs
│ ├── PluginFactoryOptions.cs
│ ├── PluginInfo.cs
│ ├── SupportConfigPluginBase.cs
│ └── Xfrogcn.PluginFactory.Abstractions.csproj
└── PluginFactory
│ ├── DefaultPluginFactory.cs
│ ├── DefaultPluginLoader.cs
│ ├── HostBuilderExtensions.cs
│ ├── PluginContext.cs
│ ├── PluginFactoryServiceCollectionExtensions.cs
│ ├── PluginInfoLogValue.cs
│ ├── PluginInitContext.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ └── Xfrogcn.PluginFactory.csproj
└── test
├── Abstractions.Test
├── IsolationLoader.cs
├── PluginConfigrationOptionsTest.cs
├── PluginConfigrationProviderTest.cs
└── Xfrogcn.PluginFactory.Abstractions.Test.csproj
├── ClassA
├── ClassA.csproj
├── TestClassA.cs
└── build.cmd
├── ClassB
├── ClassB.csproj
├── TestClassB.cs
└── build.cmd
└── PluginFactory.Test
├── DefaultPluginFactoryConfigTest.cs
├── DefaultPluginFactoryTest.cs
├── DefaultPluginLoaderTest.cs
├── TestPluginA.cs
├── TestPluginB.cs
├── TestPluginC.cs
├── TestPluginD.cs
├── TestPluginE.cs
├── Xfrogcn.PluginFactory.Test.csproj
└── build-plugin.cmd
/.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/master/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 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 |
59 | # StyleCop
60 | StyleCopReport.xml
61 |
62 | # Files built by Visual Studio
63 | *_i.c
64 | *_p.c
65 | *_h.h
66 | *.ilk
67 | *.meta
68 | *.obj
69 | *.iobj
70 | *.pch
71 | *.pdb
72 | *.ipdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *_wpftmp.csproj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !?*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 | # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
235 | **/wwwroot/lib/
236 |
237 | # RIA/Silverlight projects
238 | Generated_Code/
239 |
240 | # Backup & report files from converting an old project file
241 | # to a newer Visual Studio version. Backup files are not needed,
242 | # because we have git ;-)
243 | _UpgradeReport_Files/
244 | Backup*/
245 | UpgradeLog*.XML
246 | UpgradeLog*.htm
247 | ServiceFabricBackup/
248 | *.rptproj.bak
249 |
250 | # SQL Server files
251 | *.mdf
252 | *.ldf
253 | *.ndf
254 |
255 | # Business Intelligence projects
256 | *.rdl.data
257 | *.bim.layout
258 | *.bim_*.settings
259 | *.rptproj.rsuser
260 |
261 | # Microsoft Fakes
262 | FakesAssemblies/
263 |
264 | # GhostDoc plugin setting file
265 | *.GhostDoc.xml
266 |
267 | # Node.js Tools for Visual Studio
268 | .ntvs_analysis.dat
269 | node_modules/
270 |
271 | # Visual Studio 6 build log
272 | *.plg
273 |
274 | # Visual Studio 6 workspace options file
275 | *.opt
276 |
277 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
278 | *.vbw
279 |
280 | # Visual Studio LightSwitch build output
281 | **/*.HTMLClient/GeneratedArtifacts
282 | **/*.DesktopClient/GeneratedArtifacts
283 | **/*.DesktopClient/ModelManifest.xml
284 | **/*.Server/GeneratedArtifacts
285 | **/*.Server/ModelManifest.xml
286 | _Pvt_Extensions
287 |
288 | # Paket dependency manager
289 | .paket/paket.exe
290 | paket-files/
291 |
292 | # FAKE - F# Make
293 | .fake/
294 |
295 | # JetBrains Rider
296 | .idea/
297 | *.sln.iml
298 |
299 | # CodeRush personal settings
300 | .cr/personal
301 |
302 | # Python Tools for Visual Studio (PTVS)
303 | __pycache__/
304 | *.pyc
305 |
306 | # Cake - Uncomment if you are using it
307 | # tools/**
308 | # !tools/packages.config
309 |
310 | # Tabs Studio
311 | *.tss
312 |
313 | # Telerik's JustMock configuration file
314 | *.jmconfig
315 |
316 | # BizTalk build output
317 | *.btp.cs
318 | *.btm.cs
319 | *.odx.cs
320 | *.xsd.cs
321 |
322 | # OpenCover UI analysis results
323 | OpenCover/
324 |
325 | # Azure Stream Analytics local run output
326 | ASALocalRun/
327 |
328 | # MSBuild Binary and Structured Log
329 | *.binlog
330 |
331 | # NVidia Nsight GPU debugger configuration file
332 | *.nvuser
333 |
334 | # MFractors (Xamarin productivity tool) working folder
335 | .mfractor/
336 |
337 | # Local History for Visual Studio
338 | .localhistory/
339 |
340 | # BeatPulse healthcheck temp database
341 | healthchecksdb
342 | **/output
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2.0.1
4 | 王海波
5 | 王海波
6 | https://github.com/xfrogcn/pluginfactory
7 | git
8 | net5.0
9 | package-icon.png
10 |
11 |
12 |
13 | Xfrogcn Plugin Factory
14 |
15 |
16 | MIT
17 |
18 |
19 | xfrogcn@163.com
20 |
21 | true
22 |
23 |
24 |
25 | True
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 无叶菜
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 |
--------------------------------------------------------------------------------
/PluginFactory.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30406.217
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xfrogcn.PluginFactory.Abstractions", "src\Abstractions\Xfrogcn.PluginFactory.Abstractions.csproj", "{DFAA6ADD-58E5-47AD-9277-A61B9809A083}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0CBBE3B7-974D-4065-987B-2F36A6D1F8D6}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{90495F73-ABF2-4B63-90AA-D6A4B50D65AF}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3687AC96-B900-49BA-8E61-BC1D42ED378E}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassA", "test\ClassA\ClassA.csproj", "{DB2F5525-EBC1-44C6-9FCA-876A649E3609}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassB", "test\ClassB\ClassB.csproj", "{CEC58C66-A2BF-41E5-A897-1B095D798E66}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xfrogcn.PluginFactory.Abstractions.Test", "test\Abstractions.Test\Xfrogcn.PluginFactory.Abstractions.Test.csproj", "{472C9DF9-A102-4286-A9BF-DD36FFCB3C96}"
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xfrogcn.PluginFactory", "src\PluginFactory\Xfrogcn.PluginFactory.csproj", "{9999A5B1-06ED-42EB-AA72-3EEBCD2FEAAA}"
21 | EndProject
22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xfrogcn.PluginFactory.Test", "test\PluginFactory.Test\Xfrogcn.PluginFactory.Test.csproj", "{08409CE2-3EDA-4F50-ACCE-9BFB88839297}"
23 | EndProject
24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestPluginA", "samples\TestPluginA\TestPluginA.csproj", "{024EA651-9C96-4669-8E20-00BDF6B9AB1C}"
25 | EndProject
26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FD64627A-57ED-467B-B455-8A496D44FE4D}"
27 | ProjectSection(SolutionItems) = preProject
28 | Directory.Build.props = Directory.Build.props
29 | EndProjectSection
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 | {DFAA6ADD-58E5-47AD-9277-A61B9809A083}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 | {DFAA6ADD-58E5-47AD-9277-A61B9809A083}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 | {DFAA6ADD-58E5-47AD-9277-A61B9809A083}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 | {DFAA6ADD-58E5-47AD-9277-A61B9809A083}.Release|Any CPU.Build.0 = Release|Any CPU
41 | {DB2F5525-EBC1-44C6-9FCA-876A649E3609}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 | {DB2F5525-EBC1-44C6-9FCA-876A649E3609}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 | {DB2F5525-EBC1-44C6-9FCA-876A649E3609}.Release|Any CPU.ActiveCfg = Release|Any CPU
44 | {DB2F5525-EBC1-44C6-9FCA-876A649E3609}.Release|Any CPU.Build.0 = Release|Any CPU
45 | {CEC58C66-A2BF-41E5-A897-1B095D798E66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46 | {CEC58C66-A2BF-41E5-A897-1B095D798E66}.Debug|Any CPU.Build.0 = Debug|Any CPU
47 | {CEC58C66-A2BF-41E5-A897-1B095D798E66}.Release|Any CPU.ActiveCfg = Release|Any CPU
48 | {CEC58C66-A2BF-41E5-A897-1B095D798E66}.Release|Any CPU.Build.0 = Release|Any CPU
49 | {472C9DF9-A102-4286-A9BF-DD36FFCB3C96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 | {472C9DF9-A102-4286-A9BF-DD36FFCB3C96}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 | {472C9DF9-A102-4286-A9BF-DD36FFCB3C96}.Release|Any CPU.ActiveCfg = Release|Any CPU
52 | {472C9DF9-A102-4286-A9BF-DD36FFCB3C96}.Release|Any CPU.Build.0 = Release|Any CPU
53 | {9999A5B1-06ED-42EB-AA72-3EEBCD2FEAAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54 | {9999A5B1-06ED-42EB-AA72-3EEBCD2FEAAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
55 | {9999A5B1-06ED-42EB-AA72-3EEBCD2FEAAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
56 | {9999A5B1-06ED-42EB-AA72-3EEBCD2FEAAA}.Release|Any CPU.Build.0 = Release|Any CPU
57 | {08409CE2-3EDA-4F50-ACCE-9BFB88839297}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
58 | {08409CE2-3EDA-4F50-ACCE-9BFB88839297}.Debug|Any CPU.Build.0 = Debug|Any CPU
59 | {08409CE2-3EDA-4F50-ACCE-9BFB88839297}.Release|Any CPU.ActiveCfg = Release|Any CPU
60 | {08409CE2-3EDA-4F50-ACCE-9BFB88839297}.Release|Any CPU.Build.0 = Release|Any CPU
61 | {024EA651-9C96-4669-8E20-00BDF6B9AB1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62 | {024EA651-9C96-4669-8E20-00BDF6B9AB1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
63 | {024EA651-9C96-4669-8E20-00BDF6B9AB1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
64 | {024EA651-9C96-4669-8E20-00BDF6B9AB1C}.Release|Any CPU.Build.0 = Release|Any CPU
65 | EndGlobalSection
66 | GlobalSection(SolutionProperties) = preSolution
67 | HideSolutionNode = FALSE
68 | EndGlobalSection
69 | GlobalSection(NestedProjects) = preSolution
70 | {DFAA6ADD-58E5-47AD-9277-A61B9809A083} = {0CBBE3B7-974D-4065-987B-2F36A6D1F8D6}
71 | {DB2F5525-EBC1-44C6-9FCA-876A649E3609} = {3687AC96-B900-49BA-8E61-BC1D42ED378E}
72 | {CEC58C66-A2BF-41E5-A897-1B095D798E66} = {3687AC96-B900-49BA-8E61-BC1D42ED378E}
73 | {472C9DF9-A102-4286-A9BF-DD36FFCB3C96} = {3687AC96-B900-49BA-8E61-BC1D42ED378E}
74 | {9999A5B1-06ED-42EB-AA72-3EEBCD2FEAAA} = {0CBBE3B7-974D-4065-987B-2F36A6D1F8D6}
75 | {08409CE2-3EDA-4F50-ACCE-9BFB88839297} = {3687AC96-B900-49BA-8E61-BC1D42ED378E}
76 | {024EA651-9C96-4669-8E20-00BDF6B9AB1C} = {90495F73-ABF2-4B63-90AA-D6A4B50D65AF}
77 | EndGlobalSection
78 | GlobalSection(ExtensibilityGlobals) = postSolution
79 | SolutionGuid = {8C1A8E02-8867-4C7F-99AC-900D923F5575}
80 | EndGlobalSection
81 | EndGlobal
82 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # .NET Core 插件框架
2 |
3 | pluginfactory 是 .NET Core 下基于依赖注入实现的插件框架,此框架是插件化开发与依赖注入的完美集合,同时融入了 .NET Core 中的配置机制,可以很好地与 ASP.NET Core 等框架融合。
4 |
5 | - [.NET Core 插件框架](#net-core-插件框架)
6 | - [使用向导](#使用向导)
7 | - [安装](#安装)
8 | - [在主程序中启用](#在主程序中启用)
9 | - [通过`IHostBuilder`的`UsePluginFactory`方法启用插件库](#通过ihostbuilder的usepluginfactory方法启用插件库)
10 | - [通过`IServiceCollection`的`AddPluginFactory`方法启用插件库](#通过iservicecollection的addpluginfactory方法启用插件库)
11 | - [编写插件](#编写插件)
12 | - [插件启动](#插件启动)
13 | - [编写支持初始化的插件](#编写支持初始化的插件)
14 | - [使用插件配置](#使用插件配置)
15 | - [插件化 ASP.NET Core](#插件化-aspnet-core)
16 |
17 |
18 | ## 使用向导
19 |
20 | 示例项目可参考:`Xfrogcn.PluginFactory.Example` [Gitee地址](https://gitee.com/WuYeCai/Xfrogcn.PluginFactory.Example) [Github地址](https://github.com/xfrogcn/Xfrogcn.PluginFactory.Example)
21 |
22 | ### 安装
23 |
24 | 在主程序项目中添加`Xfrogcn.PluginFactory`包
25 |
26 | ```dotnet
27 | dotnet add package Xfrogcn.PluginFactory --version 1.0.0
28 | ```
29 |
30 | 在插件项目中添加`Xfrogcn.PluginFactory.Abstractions`包
31 |
32 | ```dotnet
33 | dotnet add package Xfrogcn.PluginFactory.Abstractions --version 1.0.0
34 | ```
35 |
36 | ### 在主程序中启用
37 |
38 | 可通过以下两种方式来启用插件库,一是通过在`Host`层级的Use机制以及在依赖注入`IServiceCollection`层级的Add机制,以下分别说明:
39 |
40 | #### 通过`IHostBuilder`的`UsePluginFactory`方法启用插件库
41 |
42 | ```c#
43 | var builder = Host.CreateDefaultBuilder(args);
44 | builder.UsePluginFactory();
45 | ```
46 |
47 | `UsePluginFactory`具有多个重载版本,详细请查看[API](./doc/api.md)文档
48 | 默认配置下,将使用程序运行目录下的`Plugins`目录作为插件程序集目录, 使用宿主配置文件作为插件配置文件(通常为appsettings.json)
49 | 你也可以通过使用带有`Assembly`或`IEnumerable`参数的版本直接传入插件所在的程序集
50 |
51 | #### 通过`IServiceCollection`的`AddPluginFactory`方法启用插件库
52 |
53 | ```c#
54 | var builder = Host.CreateDefaultBuilder(args)
55 | .ConfigureServices((hostContext, services) =>
56 | {
57 | services.AddPluginFactory();
58 | });
59 | ```
60 |
61 | `AddPluginFactory`具有多个重载版本,详细请查看[API](./doc/api.md)文档
62 | 默认配置下,将使用程序运行目录下的`Plugins`目录作为插件程序集目录
63 |
64 | `注意:` AddPluginFactory方法`不会`使用默认的配置文件作为插件配置,你需要显式地传入`IConfiguration`, 如果是在 ASP.NET Core 环境中,你可以在Startup类中直接获取到
65 |
66 | ### 编写插件
67 |
68 | 插件是实现了IPlugin接口的类,在插件库中也提供了PluginBase基类,一般从此类继承即可。标准插件具有启动和停止方法,通过`IPluginFactory`进行控制。
69 |
70 | 要编写插件,一般遵循以下步骤:
71 |
72 | 1. 创建插件项目(.NET Core 类库),如TestPluginA
73 | 1. 添加`Xfrogcn.PluginFactory.Abstractions`包
74 |
75 | ```nuget
76 | dotnet add package Xfrogcn.PluginFactory.Abstractions
77 | ```
78 |
79 | 1. 创建插件类,如Plugin,从PluginBase继承
80 |
81 | ```c#
82 | public class Plugin : PluginBase
83 | {
84 | public override Task StartAsync(IPluginContext context)
85 | {
86 | Console.WriteLine("插件A已启动");
87 | return base.StartAsync(context);
88 | }
89 |
90 | public override Task StopAsync(IPluginContext context)
91 | {
92 | Console.WriteLine("插件A已停止");
93 | return base.StopAsync(context);
94 | }
95 | }
96 | ```
97 |
98 | *启动或停止方法中可通过context中的ServiceProvider获取注入服务*
99 |
100 | 1. 通过`PluginAttribute`特性设置插件的元数据
101 |
102 | ```c#
103 | [Plugin(Alias = "PluginA", Description = "测试插件")]
104 | public class Plugin : PluginBase
105 | {
106 | }
107 | ```
108 |
109 | *插件元数据以及插件载入的插件列表信息可以通过`IPluginLoader.PluginList`获取*
110 |
111 | ### 插件启动
112 |
113 | `IPluginFactory`本身实现了.NET Core扩展库的`IHostedService`机制,故如果你是在宿主环境下使用,如(ASP.NET Core),插件的启动及停止将自动跟随宿主进行
114 | 如果未使用宿主,可通过获取`IPluginFactory`实例调用相应方法来完成
115 |
116 | ```c#
117 | // 手动启动
118 | var pluginFactory = provider.GetRequiredService();
119 | await pluginFactory.StartAsync(default);
120 | await pluginFactory.StopAsync(default);
121 | ```
122 |
123 | ### 编写支持初始化的插件
124 |
125 | 在很多场景,我们需要在插件中控制宿主的依赖注入,如注入新的服务等,这时候我们可通过实现支持初始化的插件(`ISupportInitPlugin`)来实现,该接口的`Init`方法将在依赖注入构建之前调用,通过方法参数`IPluginInitContext`中的`ServiceCollection`可以控制宿主注入容器。
126 |
127 | ```c#
128 | [Plugin(Alias = "PluginA", Description = "测试插件")]
129 | public class Plugin : PluginBase, ISupportInitPlugin
130 | {
131 | public void Init(IPluginInitContext context)
132 | {
133 | // 注入服务
134 | //context.ServiceCollection.TryAddScoped();
135 | }
136 | }
137 | ```
138 |
139 | ### 使用插件配置
140 |
141 | 插件支持 .NET Core 扩展库中的Options及Configuration机制,你只需要从`SupportConfigPluginBase`类继承实现插件即可,其中TOptions泛型为插件的配置类型。插件配置自动从宿主配置或启用插件工厂时传入的配置中获取,插件配置位于配置下的Plugins节点,该节点下以插件类名称或插件别名(通过`PluginAttribute`特性指定)作为键名,此键之下为插件的配置,如以下配置文件:
142 |
143 | ```appsettings.json
144 | {
145 | "Plugins": {
146 | "PluginA": {
147 | "TestConfig": "Hello World"
148 | },
149 |
150 | }
151 | }
152 | ```
153 |
154 | 扩展PluginA实现配置:
155 |
156 | 1. 定义配置类,如PluginOptions
157 |
158 | ```c#
159 | public class PluginOptions
160 | {
161 | public string TestConfig { get; set; }
162 | }
163 | ```
164 |
165 | 2. 实现插件
166 |
167 | ```c#
168 | [Plugin(Alias = "PluginA", Description = "测试插件")]
169 | public class Plugin : SupportConfigPluginBase, ISupportInitPlugin
170 | {
171 |
172 | public Plugin(IOptionsMonitor options) : base(options)
173 | {
174 | }
175 |
176 | public void Init(IPluginInitContext context)
177 | {
178 | // 注入服务
179 | //context.ServiceCollection.TryAddScoped();
180 | Console.WriteLine($"Init 插件配置:{Options.TestConfig}");
181 | }
182 |
183 | public override Task StartAsync(IPluginContext context)
184 | {
185 | Console.WriteLine("插件A已启动");
186 | Console.WriteLine($"StartAsync 插件配置:{Options.TestConfig}");
187 | return base.StartAsync(context);
188 | }
189 |
190 | public override Task StopAsync(IPluginContext context)
191 | {
192 | Console.WriteLine("插件A已停止");
193 | return base.StopAsync(context);
194 | }
195 | ```
196 |
197 | *注意:在插件初始化方法中也可使用注入的配置*
198 | 3. 跨插件配置
199 |
200 | 有些配置可能需要在多个插件中共享,此时你可通过`Plugins`下的`_Share`节点进行配置,此节点下配置将会被合并到插件配置中,可通过PluginOptions进行访问。
201 |
202 | ```appsettings.json
203 | {
204 | "Plugins": {
205 | "PluginA": {
206 | },
207 | "_Share": {
208 | "TestConfig": "Hello World"
209 | }
210 | }
211 | }
212 | ```
213 |
214 | ### 插件化 ASP.NET Core
215 |
216 | 要让 ASP.NET Core 获取得到插件中的控制器,你只需要在插件的初始化方法`Init`中,向MVC注入插件程序集:
217 |
218 | ```c#
219 | context.ServiceCollection.AddMvcCore()
220 | .AddApplicationPart(typeof(Plugin).Assembly);
221 | ```
222 |
--------------------------------------------------------------------------------
/doc/api.md:
--------------------------------------------------------------------------------
1 | # API
2 |
3 | - [API](#api)
4 | - [IHostBuilder.UsePluginFactory 扩展方法](#ihostbuilderusepluginfactory-扩展方法)
5 | - [UsePluginFactory()](#usepluginfactory)
6 | - [UsePluginFactory(IConfiguration configuration)](#usepluginfactoryiconfiguration-configuration)
7 | - [UsePluginFactory(IConfiguration configuration, Assembly assembly)](#usepluginfactoryiconfiguration-configuration-assembly-assembly)
8 | - [UsePluginFactory(IConfiguration configuration, IEnumerable<Assembly> assemblies)](#usepluginfactoryiconfiguration-configuration-ienumerableassembly-assemblies)
9 | - [UsePluginFactory(Assembly assembly)](#usepluginfactoryassembly-assembly)
10 | - [UsePluginFactory(IEnumerable<Assembly> assemblies)](#usepluginfactoryienumerableassembly-assemblies)
11 | - [UsePluginFactory(Action<PluginFactoryOptions> options)](#usepluginfactoryactionpluginfactoryoptions-options)
12 | - [UsePluginFactory(IConfiguration configuration, Action<PluginFactoryOptions> options)](#usepluginfactoryiconfiguration-configuration-actionpluginfactoryoptions-options)
13 | - [IServiceCollection.AddPluginFactory 扩展方法](#iservicecollectionaddpluginfactory-扩展方法)
14 | - [AddPluginFactory()](#addpluginfactory)
15 | - [AddPluginFactory(IConfiguration configuration)](#addpluginfactoryiconfiguration-configuration)
16 | - [AddPluginFactory(IConfiguration configuration, Assembly assembly)](#addpluginfactoryiconfiguration-configuration-assembly-assembly)
17 | - [AddPluginFactory(IConfiguration configuration, IEnumerable<Assembly> assemblies)](#addpluginfactoryiconfiguration-configuration-ienumerableassembly-assemblies)
18 | - [AddPluginFactory(Action<PluginFactoryOptions> configureOptions)](#addpluginfactoryactionpluginfactoryoptions-configureoptions)
19 | - [AddPluginFactory(IConfiguration configuration, Action<PluginFactoryOptions> configureOptions)](#addpluginfactoryiconfiguration-configuration-actionpluginfactoryoptions-configureoptions)
20 | - [AddPluginFactory(PluginFactoryOptions options, IConfiguration configuration)](#addpluginfactorypluginfactoryoptions-options-iconfiguration-configuration)
21 | - [IPluginLoader](#ipluginloader)
22 | - [PluginList属性](#pluginlist属性)
23 | - [Load()](#load)
24 | - [Init()](#init)
25 | - [IPluginFactory](#ipluginfactory)
26 | - [StartAsync(CancellationToken cancellationToken)](#startasynccancellationtoken-cancellationtoken)
27 | - [StopAsync(CancellationToken cancellationToken)](#stopasynccancellationtoken-cancellationtoken)
28 | - [IPluginContext](#iplugincontext)
29 | - [PluginFactory属性](#pluginfactory属性)
30 | - [ServiceProvider属性](#serviceprovider属性)
31 | - [CancellationToken属性](#cancellationtoken属性)
32 | - [IPluginInitContext](#iplugininitcontext)
33 | - [PluginPath属性](#pluginpath属性)
34 | - [PluginLoader属性](#pluginloader属性)
35 | - [ServiceCollection属性](#servicecollection属性)
36 | - [InitServiceProvider属性](#initserviceprovider属性)
37 | - [IPlugin](#iplugin)
38 | - [StartAsync(IPluginContext context)](#startasynciplugincontext-context)
39 | - [Task StopAsync(IPluginContext context)](#task-stopasynciplugincontext-context)
40 | - [ISupportInitPlugin](#isupportinitplugin)
41 | - [Init(IPluginInitContext context)](#initiplugininitcontext-context)
42 | - [ISupportConfigPlugin](#isupportconfigplugin)
43 | - [PluginFactoryOptions](#pluginfactoryoptions)
44 | - [PluginPath属性](#pluginpath属性-1)
45 | - [FileProvider属性](#fileprovider属性)
46 | - [DisabledPluginList属性](#disabledpluginlist属性)
47 | - [AddAssembly(Assembly assembly)](#addassemblyassembly-assembly)
48 | - [PluginInfo](#plugininfo)
49 | - [Id属性](#id属性)
50 | - [Name属性](#name属性)
51 | - [Description属性](#description属性)
52 | - [Alias属性](#alias属性)
53 | - [IsEnable属性](#isenable属性)
54 | - [PluginType属性](#plugintype属性)
55 | - [CanConfig顺序](#canconfig顺序)
56 | - [CanInit属性](#caninit属性)
57 | - [PluginBase](#pluginbase)
58 | - [SupportConfigPluginBase](#supportconfigpluginbase)
59 | - [PluginAttribute](#pluginattribute)
60 | - [Id属性](#id属性-1)
61 | - [Alias属性](#alias属性-1)
62 | - [Name属性](#name属性-1)
63 | - [Description属性](#description属性-1)
64 |
65 | ## IHostBuilder.UsePluginFactory 扩展方法
66 |
67 | ### UsePluginFactory()
68 |
69 | - 说明:无参数,使用默认配置来启用插件工厂,此时会使用IHostBuilder的配置作为插件配置容器,使用应用目录的Plugins目录作为插件目录
70 |
71 | ### UsePluginFactory(IConfiguration configuration)
72 |
73 | - 说明:使用指定的配置来初始化插件工厂
74 | - 参数列表:
75 | - *configuration*: IConfiguration,插件所使用的配置对象,插件将从此配置的Plugins节点获取插件自身的配置
76 |
77 | ### UsePluginFactory(IConfiguration configuration, Assembly assembly)
78 |
79 | - 说明:使用指定的配置来初始化插件工厂, 并自动加载assembly参数指定的程序集
80 | - 参数列表:
81 | - *configuration*: IConfiguration,插件所使用的配置对象,插件将从此配置的Plugins节点获取插件自身的配置
82 | - *assembly*:Assembly,要载入的插件程序集
83 |
84 | ### UsePluginFactory(IConfiguration configuration, IEnumerable<Assembly> assemblies)
85 |
86 | - 说明:使用指定的配置来初始化插件工厂, 并自动加载assemblies参数指定的程序集列表
87 | - 参数列表:
88 | - *configuration*: IConfiguration,插件所使用的配置对象,插件将从此配置的Plugins节点获取插件自身的配置
89 | - *assemblies*:List<Assembly>,要载入的插件程序集列表
90 |
91 | ### UsePluginFactory(Assembly assembly)
92 |
93 | - 说明:使用默认配置初始化插件工厂, 并自动加载assembly参数指定的程序集
94 | - 参数列表:
95 | - *assembly*:Assembly,要载入的插件程序集
96 |
97 | ### UsePluginFactory(IEnumerable<Assembly> assemblies)
98 |
99 | - 说明:使用默认配置初始化插件工厂, 并自动加载assemblies参数指定的程序集列表
100 | - 参数列表:
101 | - *assemblies*:List<Assembly>,要载入的插件程序集列表
102 |
103 | ### UsePluginFactory(Action<PluginFactoryOptions> options)
104 |
105 | - 说明:使用指定的设置来初始化插件工厂
106 | - 参数列表:
107 | - *options*:Action<PluginFactoryOptions>,设置委托,可设置项参考[PluginFactoryOptions](#PluginFactoryOptions)
108 |
109 | ### UsePluginFactory(IConfiguration configuration, Action<PluginFactoryOptions> options)
110 |
111 | - 说明:指定插件配置容器,并使用指定的设置来初始化插件工厂
112 | - 参数列表:
113 | - *configuration*: IConfiguration,插件所使用的配置对象,插件将从此配置的Plugins节点获取插件自身的配置
114 | - *options*:Action<PluginFactoryOptions>,设置委托,可设置项参考[PluginFactoryOptions](#PluginFactoryOptions)
115 |
116 | ## IServiceCollection.AddPluginFactory 扩展方法
117 |
118 | ### AddPluginFactory()
119 |
120 | - 说明:使用默认设置初始化插件工厂,此时内部会使用内存配置容器作为插件配置,故`不会`从配置文件或其他配置源载入配置;插件目录将使用Plugins目录
121 |
122 | ### AddPluginFactory(IConfiguration configuration)
123 |
124 | - 说明:使用指定的配置来初始化插件工厂
125 | - 参数列表:
126 | - *configuration*: IConfiguration,插件所使用的配置对象,插件将从此配置的Plugins节点获取插件自身的配置
127 |
128 | ### AddPluginFactory(IConfiguration configuration, Assembly assembly)
129 |
130 | - 说明:使用指定的配置来初始化插件工厂, 并自动加载assembly参数指定的程序集
131 | - 参数列表:
132 | - *configuration*: IConfiguration,插件所使用的配置对象,插件将从此配置的Plugins节点获取插件自身的配置
133 | - *assembly*:Assembly,要载入的插件程序集
134 |
135 | ### AddPluginFactory(IConfiguration configuration, IEnumerable<Assembly> assemblies)
136 |
137 | - 说明:使用指定的配置来初始化插件工厂, 并自动加载assemblies参数指定的程序集列表
138 | - 参数列表:
139 | - *configuration*: IConfiguration,插件所使用的配置对象,插件将从此配置的Plugins节点获取插件自身的配置
140 | - *assemblies*:List<Assembly>,要载入的插件程序集列表
141 |
142 | ### AddPluginFactory(Action<PluginFactoryOptions> configureOptions)
143 |
144 | - 说明:使用指定的设置来初始化插件工厂
145 | - 参数列表:
146 | - *options*:Action<PluginFactoryOptions>,设置委托,可设置项参考[PluginFactoryOptions](#PluginFactoryOptions)
147 |
148 | ### AddPluginFactory(IConfiguration configuration, Action<PluginFactoryOptions> configureOptions)
149 |
150 | - 说明:指定插件配置容器,并使用指定的设置来初始化插件工厂
151 | - 参数列表:
152 | - *configuration*: IConfiguration,插件所使用的配置对象,插件将从此配置的Plugins节点获取插件自身的配置
153 | - *options*:Action<PluginFactoryOptions>,设置委托,可设置项参考[PluginFactoryOptions](#PluginFactoryOptions)
154 |
155 | ### AddPluginFactory(PluginFactoryOptions options, IConfiguration configuration)
156 |
157 | - 说明:指定设置对象,并使用指定配置对象来初始化插件工厂
158 | - 参数列表:
159 | - *options*:PluginFactoryOptions,设置对象,可设置项参考[PluginFactoryOptions](#PluginFactoryOptions)
160 | - *configuration*: IConfiguration,插件所使用的配置对象,插件将从此配置的Plugins节点获取插件自身的配置
161 |
162 | ## IPluginLoader
163 |
164 | `IPluginLoader`主要用于从指定位置载入插件
165 |
166 | ### PluginList属性
167 |
168 | - 说明:只读属性,返回已加载的插件列表,类型为IReadOnlyList≶PluginInfo>,参见[PluginInfo](#PluginInfo)
169 |
170 | ### Load()
171 |
172 | - 说明:加载插件程序集,*注意*:通常情况下你无需调用此方法,框架内部会自动调用,如果你要实现自己的加载机制,则可实现此接口
173 | - 返回:void
174 |
175 | ### Init()
176 |
177 | - 说明:初始化插件,*注意*:通常情况下你无需调用此方法,框架内部会自动调用,如果你要实现自己的加载机制,则可实现此接口
178 | - 返回:void
179 |
180 | ## IPluginFactory
181 |
182 | `IPluginFactory`插件工厂接口,用于控制插件的生命周期,该接口从`IHostedService`继承,故支持宿主的托管服务机制
183 |
184 | ### StartAsync(CancellationToken cancellationToken)
185 |
186 | - 说明:启动所有插件
187 | - 参数:
188 | - cancellationToken: CancellationToken,取消监听令牌
189 | - 返回:Task
190 |
191 | ### StopAsync(CancellationToken cancellationToken)
192 |
193 | - 说明:停止所有插件
194 | - 参数:
195 | - cancellationToken: CancellationToken,取消监听令牌
196 | - 返回:Task
197 |
198 | ## IPluginContext
199 |
200 | `IPluginContext`插件上下文,是插件运行时的上下文数据,将传递给插件的相关方法
201 |
202 | ### PluginFactory属性
203 |
204 | - 说明:只读,当前的IPluginFactory实例
205 |
206 | ### ServiceProvider属性
207 |
208 | - 说明:只读,当前的IServiceProvider实例
209 |
210 | ### CancellationToken属性
211 |
212 | - 说明:只读,关联的取消监听令牌,你可以通过此令牌判断上层操作是否已被取消
213 |
214 | ## IPluginInitContext
215 |
216 | `IPluginInitContext`插件初始化上下文,由支持初始化的插件接口[ISupportInitPlugin](#ISupportInitPlugin)的Init方法使用
217 |
218 | ### PluginPath属性
219 |
220 | - 说明:只读,插件路径
221 |
222 | ### PluginLoader属性
223 |
224 | - 说明:只读,当前关联的插件载入器, 请参考[IPluginLoader](#IPluginLoader)
225 |
226 | ### ServiceCollection属性
227 |
228 | - 说明:只读,应用的依赖注入服务容器,通过此属性可注入新的服务
229 |
230 | ### InitServiceProvider属性
231 |
232 | - 说明:只读,初始化所使用的服务提供器,通过此属性可获取依赖的其他服务,*注意通过此提供器可获取初始化插件工厂方法之前所注入的服务。*
233 |
234 | ## IPlugin
235 |
236 | `IPlugin`是插件的基础接口,所有插件需实现此接口
237 |
238 | ### StartAsync(IPluginContext context)
239 |
240 | - 说明:启动插件
241 | - 参数:
242 | - context, [IPluginContext](#IPluginContext), 插件上下文
243 | - 返回:Task
244 |
245 | ### Task StopAsync(IPluginContext context)
246 |
247 | - 说明:停止插件
248 | - 参数:
249 | - context, [IPluginContext](#IPluginContext), 插件上下文
250 | - 返回:Task
251 |
252 | ## ISupportInitPlugin
253 |
254 | `ISupportInitPlugin`接口用于实现插件的初始化机制
255 |
256 | ### Init(IPluginInitContext context)
257 |
258 | - 说明:插件初始化
259 | - 参数:
260 | - context:[IPluginInitContext](#IPluginInitContext), 插件初始化上下文
261 |
262 | ## ISupportConfigPlugin
263 |
264 | `ISupportConfigPlugin`接口用于实现插件的配置机制,它是泛型接口,泛型参数为`TOptions`,表示插件的配置类型
265 |
266 | ## PluginFactoryOptions
267 |
268 | `PluginFactoryOptions`是插件工厂的配置类型
269 |
270 | ### PluginPath属性
271 |
272 | - 说明:获取或设置插件工厂所使用的插件路径
273 |
274 | ### FileProvider属性
275 |
276 | - 说明:获取或设置插件载入时所使用的文件提供器,默认将使用`PhysicalFileProvider`提供器
277 |
278 | ### DisabledPluginList属性
279 |
280 | - 说明,只读,获取当前被禁用的插件列表
281 |
282 | ### AddAssembly(Assembly assembly)
283 |
284 | - 说明:添加插件程序集
285 | - 参数:
286 | - assembly:Assembly,插件程序集
287 |
288 | ## PluginInfo
289 |
290 | `PluginInfo`类用于保存插件的相关信息
291 |
292 | ### Id属性
293 |
294 | - 说明:插件ID
295 |
296 | ### Name属性
297 |
298 | - 说明:插件名称
299 |
300 | ### Description属性
301 |
302 | - 说明:插件说明
303 |
304 | ### Alias属性
305 |
306 | - 说明:插件别名
307 |
308 | ### IsEnable属性
309 |
310 | - 说明:是否启用
311 |
312 | ### PluginType属性
313 |
314 | - 说明:插件的类型信息
315 |
316 | ### CanConfig顺序
317 |
318 | - 说明:插件是否可配置
319 |
320 | ### CanInit属性
321 |
322 | - 说明:插件是否可初始化
323 |
324 | ## PluginBase
325 |
326 | `PluginBase`插件基类,实现了[IPlugin](#IPlugin)接口,你可以从此类继承,然后重写相应的方法
327 |
328 | ## SupportConfigPluginBase
329 |
330 | `SupportConfigPluginBase`支持配置的插件基类,从`PluginBase`继承,并实现了`ISupportConfigPlugin<TOptions>`方法,继承此类,必须提供支持IOptionsMonitor<TOptions> options参数的构造函数,以传入插件配置。
331 |
332 | ## PluginAttribute
333 |
334 | `PluginAttribute`特性,用于设置插件的相关信息。
335 |
336 | ### Id属性
337 |
338 | - 说明:插件ID
339 |
340 | ### Alias属性
341 |
342 | - 说明:插件别名
343 |
344 | ### Name属性
345 |
346 | - 说明:插件名称
347 |
348 | ### Description属性
349 |
350 | - 说明:插件描述
--------------------------------------------------------------------------------
/package-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xfrogcn/Xfrogcn.PluginFactory/af7236b33d649c9572be63b06a8d9d84757b13c5/package-icon.png
--------------------------------------------------------------------------------
/samples/TestPluginA/ShareOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace TestPluginA
6 | {
7 | ///
8 | /// 公共配置
9 | ///
10 | public class ShareOptions
11 | {
12 | public string ConnectionString { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/samples/TestPluginA/TestConfigPlugin.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Options;
2 | using Xfrogcn.PluginFactory;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace TestPluginA
8 | {
9 | public class TestConfigPluginOptions : ShareOptions
10 | {
11 |
12 | }
13 | [Plugin(Alias = "TestConfigPlugin")]
14 | public class TestConfigPlugin : SupportConfigPluginBase
15 | {
16 | public TestConfigPlugin(IOptionsMonitor options) : base(options)
17 | {
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/samples/TestPluginA/TestConfigPluginWithInit.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Options;
2 | using Xfrogcn.PluginFactory;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace TestPluginA
8 | {
9 | public class TestConfigPluginWithInitOptions : ShareOptions
10 | {
11 |
12 | }
13 |
14 | [Plugin(Alias = "TestConfigPluginWithInit")]
15 | public class TestConfigPluginWithInit : SupportConfigPluginBase, ISupportInitPlugin
16 | {
17 | public TestConfigPluginWithInit() : base(null)
18 | {
19 |
20 | }
21 | public TestConfigPluginWithInit(IOptionsMonitor options) : base(options)
22 | {
23 | }
24 |
25 | public void Init(IPluginInitContext context)
26 | {
27 |
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/samples/TestPluginA/TestInitPlugin.cs:
--------------------------------------------------------------------------------
1 | using Xfrogcn.PluginFactory;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace TestPluginA
7 | {
8 | [Plugin(Alias = "TestInitPlugin")]
9 | public class TestInitPlugin : PluginBase, ISupportInitPlugin
10 | {
11 | public void Init(IPluginInitContext context)
12 | {
13 |
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/samples/TestPluginA/TestPlugin.cs:
--------------------------------------------------------------------------------
1 | using Xfrogcn.PluginFactory;
2 | using System;
3 |
4 | namespace TestPluginA
5 | {
6 | [Plugin(Alias = "TestPlugin")]
7 | public class TestPlugin : PluginBase
8 | {
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/samples/TestPluginA/TestPluginA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Abstractions/Configration/IPluginConfigrationProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace Xfrogcn.PluginFactory
4 | {
5 | ///
6 | /// Plugin配置节点获取器
7 | ///
8 | ///
9 | public interface IPluginConfigrationProvider
10 | {
11 | public IConfiguration Configuration { get; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Abstractions/Configration/PluginConfigrationOptions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Options;
2 | namespace Xfrogcn.PluginFactory
3 | {
4 | ///
5 | /// 从配置中获取插件设置
6 | ///
7 | /// 插件类型
8 | /// 插件设置
9 | public class PluginConfigrationOptions : ConfigureFromConfigurationOptions
10 | where TPluginOptions : class, new()
11 | {
12 | public PluginConfigrationOptions(IPluginConfigrationProvider provider) : base(provider.Configuration)
13 | {
14 | }
15 |
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Abstractions/Configration/PluginConfigrationProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using System;
3 | using System.Linq;
4 | using System.Security.Cryptography.X509Certificates;
5 | using System.Xml.Serialization;
6 |
7 | namespace Xfrogcn.PluginFactory
8 | {
9 | ///
10 | /// PluginConfigrationProvider从 配置中获取配置
11 | /// 每个插件以插件类型全名称或插件别名为键
12 | ///
13 | public class PluginConfigrationProvider : IPluginConfigrationProvider
14 | where TPlugin : IPlugin
15 | {
16 | public const string DEFAULT_SHARE_KEY = "_Share";
17 |
18 | public PluginConfigrationProvider(PluginFactoryConfigration configration)
19 | {
20 | if (configration == null)
21 | {
22 | throw new ArgumentNullException(nameof(configration));
23 | }
24 |
25 | Type pluginType = typeof(TPlugin);
26 | string configKey = typeof(TPlugin).FullName;
27 | var section = configration.Configuration.GetSection(configKey);
28 | if (!section.Exists())
29 | {
30 | // 内嵌类型,将+号替换为.
31 | configKey = configKey.Replace("+", ".");
32 | section= configration.Configuration.GetSection(configKey);
33 | }
34 |
35 | var attr = pluginType.GetCustomAttributes(typeof(PluginAttribute), false).OfType().FirstOrDefault();
36 | IConfigurationSection aliasSection = null;
37 | if(attr !=null && !String.IsNullOrEmpty(attr.Alias))
38 | {
39 | configKey = attr.Alias;
40 | var section2 = configration.Configuration.GetSection(configKey);
41 | if (section2.Exists())
42 | {
43 | aliasSection = section2;
44 | }
45 |
46 | }
47 |
48 | // 共享配置
49 | var shareSection = configration.Configuration.GetSection(DEFAULT_SHARE_KEY);
50 |
51 |
52 | // 合并多个配置
53 | IConfigurationSection[] configList = new IConfigurationSection[]
54 | {
55 | // 共享配置可以被覆盖
56 | shareSection, section, aliasSection
57 | };
58 | if (configList.Count(x => x != null && x.Exists()) > 1)
59 | {
60 | var cb = new ConfigurationBuilder();
61 | configList.All(s =>
62 | {
63 | if(s !=null && s.Exists())
64 | {
65 | cb.AddConfiguration(s);
66 | }
67 | return true;
68 | });
69 | Configuration = cb.Build();
70 | }
71 | else
72 | {
73 | Configuration = configList.FirstOrDefault(x => x != null && x.Exists());
74 | if(Configuration == null)
75 | {
76 | Configuration = section;
77 | }
78 | }
79 |
80 |
81 | }
82 |
83 | public IConfiguration Configuration { get; }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/Abstractions/Configration/PluginFactoryConfigration.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using System;
3 |
4 | namespace Xfrogcn.PluginFactory
5 | {
6 | ///
7 | /// IConfiguration的封装
8 | ///
9 | public class PluginFactoryConfigration
10 | {
11 | public static readonly string DEFAULT_CONFIG_KEY = "Plugins";
12 |
13 | public PluginFactoryConfigration(IConfiguration configuration)
14 | {
15 | if (configuration == null)
16 | {
17 | throw new ArgumentNullException(nameof(configuration));
18 | }
19 | Configuration = configuration.GetSection(DEFAULT_CONFIG_KEY);
20 | }
21 |
22 | public IConfiguration Configuration { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Abstractions/IPlugin.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace Xfrogcn.PluginFactory
4 | {
5 | public interface IPlugin
6 | {
7 | Task StartAsync(IPluginContext context);
8 |
9 | Task StopAsync(IPluginContext context);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Abstractions/IPluginContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 |
4 | namespace Xfrogcn.PluginFactory
5 | {
6 | public interface IPluginContext
7 | {
8 | public IPluginFactory PluginFactory { get; }
9 |
10 | public IServiceProvider ServiceProvider { get; }
11 |
12 | public CancellationToken CancellationToken { get; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Abstractions/IPluginFactory.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Hosting;
2 |
3 | namespace Xfrogcn.PluginFactory
4 | {
5 | ///
6 | /// 插件工厂
7 | ///
8 | public interface IPluginFactory : IHostedService
9 | {
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Abstractions/IPluginInitContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using System;
3 |
4 | namespace Xfrogcn.PluginFactory
5 | {
6 | public interface IPluginInitContext
7 | {
8 | string PluginPath { get; }
9 |
10 | IPluginLoader PluginLoader { get; }
11 |
12 | IServiceCollection ServiceCollection { get; }
13 |
14 | IServiceProvider InitServiceProvider { get; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Abstractions/IPluginLoader.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Xfrogcn.PluginFactory
4 | {
5 | ///
6 | /// 插件载入器
7 | ///
8 | public interface IPluginLoader
9 | {
10 |
11 | ///
12 | /// 插件信息列表
13 | ///
14 | IReadOnlyList PluginList { get; }
15 |
16 | ///
17 | /// 载入插件
18 | ///
19 | ///
20 | void Load();
21 |
22 | ///
23 | /// 初始化插件
24 | ///
25 | void Init();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Abstractions/ISupportConfigPlugin.cs:
--------------------------------------------------------------------------------
1 | namespace Xfrogcn.PluginFactory
2 | {
3 | ///
4 | /// 支持配置的插件接口
5 | ///
6 | ///
7 | public interface ISupportConfigPlugin
8 | where TOptions: class, new()
9 | {
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Abstractions/ISupportInitPlugin.cs:
--------------------------------------------------------------------------------
1 | namespace Xfrogcn.PluginFactory
2 | {
3 | ///
4 | /// 支持初始化的插件接口
5 | /// 初始化方法在构建ServiceProvider之前调用,可向服务容器中注入插件自定义的服务
6 | ///
7 | ///
8 | /// 如果插件支持此接口,必须支持无参构造
9 | /// 注意:请毋在初始化接口中设置实例属性或变量,因为初始化方法与Start与Stop方法对应的
10 | /// 插件实例可能不同
11 | ///
12 | public interface ISupportInitPlugin
13 | {
14 | void Init(IPluginInitContext context);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Abstractions/IsolationAssemblyLoadContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Reflection;
4 | using System.Runtime.CompilerServices;
5 | using System.Runtime.Loader;
6 |
7 | [assembly: InternalsVisibleTo("PluginFactory.Abstractions.Test")]
8 |
9 | namespace Xfrogcn.PluginFactory
10 | {
11 | ///
12 | /// 以隔离的方式加载程序集,每个加载的程序集可以有独立的依赖版本
13 | /// 隔离的插件需位于单独的文件夹中,且文件夹名称需与程序集名称一致,且必须具有deps.json文件
14 | ///
15 | public class IsolationAssemblyLoadContext : AssemblyLoadContext
16 | {
17 | private AssemblyDependencyResolver _resolver;
18 | private readonly string _assemblyName;
19 |
20 | public IsolationAssemblyLoadContext(string assemblyPath)
21 | {
22 | if(String.IsNullOrEmpty(assemblyPath))
23 | {
24 | throw new ArgumentNullException(nameof(assemblyPath));
25 | }
26 | _assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
27 |
28 | _resolver = new AssemblyDependencyResolver(assemblyPath);
29 | }
30 |
31 | public Assembly Load()
32 | {
33 | return Load(new AssemblyName(_assemblyName));
34 | }
35 |
36 | protected override Assembly Load(AssemblyName assemblyName)
37 | {
38 | string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
39 | if (assemblyPath != null)
40 | {
41 | return LoadFromAssemblyPath(assemblyPath);
42 | }
43 | return null;
44 | }
45 |
46 | protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
47 | {
48 | string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
49 | if (libraryPath != null)
50 | {
51 | return LoadUnmanagedDllFromPath(libraryPath);
52 | }
53 |
54 | return IntPtr.Zero;
55 | }
56 |
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Abstractions/PluginAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Xfrogcn.PluginFactory
4 | {
5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple =false)]
6 | public class PluginAttribute : Attribute
7 | {
8 | ///
9 | /// 插件Id
10 | ///
11 | public string Id { get; set; }
12 |
13 | ///
14 | /// 插件别名,在插件配置中可使用别名作为插件配置键
15 | ///
16 | public string Alias { get; set; }
17 |
18 | ///
19 | /// 插件名称
20 | ///
21 | public string Name { get; set; }
22 |
23 | ///
24 | /// 插件描述
25 | ///
26 | public string Description { get; set; }
27 |
28 |
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Abstractions/PluginBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 |
6 | namespace Xfrogcn.PluginFactory
7 | {
8 | public abstract class PluginBase : IPlugin
9 | {
10 | public virtual Task StartAsync(IPluginContext context)
11 | {
12 | return Task.CompletedTask;
13 | }
14 |
15 | public virtual Task StopAsync(IPluginContext context)
16 | {
17 | return Task.CompletedTask;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Abstractions/PluginFactoryOptions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using Microsoft.Extensions.FileProviders;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Reflection;
8 |
9 | namespace Xfrogcn.PluginFactory
10 | {
11 | public class PluginFactoryOptions
12 | {
13 | public const string DEFAULT_PLUGIN_PATH_KEY = "Path";
14 | public const string DEFAULT_ISENABLED_KEY = "IsEnabled";
15 |
16 | ///
17 | /// 插件路径
18 | ///
19 | public string PluginPath { get; set; }
20 |
21 | ///
22 | /// 文件提供器
23 | ///
24 | public IFileProvider FileProvider { get; set; }
25 |
26 | private List _additionalAssemblies = new List();
27 | public IReadOnlyList AdditionalAssemblies => _additionalAssemblies;
28 |
29 | public Func Predicate { get; set; } = _ => true;
30 |
31 | private List _disabledPluginList = new List();
32 |
33 | ///
34 | /// 被禁用的插件
35 | ///
36 | public IReadOnlyList DisabledPluginList => _disabledPluginList;
37 |
38 | public void DisablePlugin(string pluginTypeName)
39 | {
40 | if( !_disabledPluginList.Any(x=>x.Equals(pluginTypeName, StringComparison.OrdinalIgnoreCase)))
41 | {
42 | _disabledPluginList.Add(pluginTypeName);
43 | }
44 | }
45 |
46 | public void AddAssembly(Assembly assembly)
47 | {
48 | _additionalAssemblies.Add(assembly);
49 | }
50 |
51 | ///
52 | /// 从配置中获取插件工厂设置
53 | ///
54 | ///
55 | public void ConfigFromConfigration(PluginFactoryConfigration configuration)
56 | {
57 | if (configuration == null)
58 | {
59 | throw new ArgumentNullException(nameof(configuration));
60 | }
61 | IConfiguration pluginConfig = configuration.Configuration;
62 | string path = pluginConfig[DEFAULT_PLUGIN_PATH_KEY];
63 | if (!String.IsNullOrEmpty(path) && !String.Equals(path, PluginPath, StringComparison.OrdinalIgnoreCase))
64 | {
65 | if (!Path.IsPathRooted(path))
66 | {
67 | path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
68 | }
69 | path = Path.GetFullPath(path);
70 | PluginPath = path;
71 |
72 | if (FileProvider == null || FileProvider is PhysicalFileProvider)
73 | {
74 | if(Directory.Exists(path))
75 | {
76 | FileProvider = new PhysicalFileProvider(path);
77 | }
78 | else
79 | {
80 | FileProvider = null;
81 | }
82 | }
83 |
84 | }
85 |
86 | configDisablePlugin(pluginConfig);
87 |
88 | }
89 |
90 | ///
91 | /// 配置下以插件全类型名称或别名作为键,如果下面存在IsEnabled配置且值为false,0则放入禁止列表
92 | ///
93 | /// 插件工厂根配置节点
94 | private void configDisablePlugin(IConfiguration pluginConfig)
95 | {
96 | foreach(IConfigurationSection section in pluginConfig.GetChildren())
97 | {
98 | var isEnabledSection = section.GetSection(DEFAULT_ISENABLED_KEY);
99 | if(isEnabledSection.Exists() && (isEnabledSection.Value=="0" || isEnabledSection.Value.ToLower() == "false"))
100 | {
101 | DisablePlugin(section.Key);
102 | }
103 | }
104 | }
105 |
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/Abstractions/PluginInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Xfrogcn.PluginFactory
4 | {
5 | ///
6 | /// 插件信息
7 | ///
8 | public class PluginInfo
9 | {
10 | ///
11 | /// 插件Id
12 | ///
13 | public string Id { get; set; }
14 | ///
15 | /// 插件名称
16 | ///
17 | public string Name { get; set; }
18 | ///
19 | /// 插件描述
20 | ///
21 | public string Description { get; set; }
22 |
23 | ///
24 | /// 别名
25 | ///
26 | public string Alias { get; set; }
27 |
28 | ///
29 | /// 是否有效
30 | ///
31 | public bool IsEnable { get; set; } = true;
32 |
33 | ///
34 | /// 插件类型
35 | ///
36 | public Type PluginType { get; set; }
37 |
38 | ///
39 | /// 是否可以配置
40 | ///
41 | public bool CanConfig { get; set; }
42 |
43 | ///
44 | /// 是否支持Init
45 | ///
46 | public bool CanInit { get; set; }
47 |
48 | ///
49 | /// 配置类型
50 | ///
51 | public Type ConfigType { get; set; }
52 |
53 | ///
54 | /// 插件顺序
55 | ///
56 | public int Order { get; set; }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Abstractions/SupportConfigPluginBase.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Options;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Xfrogcn.PluginFactory
7 | {
8 | ///
9 | /// 支持配置的插件基类
10 | ///
11 | ///
12 | public abstract class SupportConfigPluginBase : PluginBase, ISupportConfigPlugin
13 | where TOptions : class, new()
14 | {
15 | protected TOptions Options { get; private set; }
16 | public SupportConfigPluginBase(IOptionsMonitor options)
17 | {
18 | if (options != null)
19 | {
20 | Options = options.CurrentValue;
21 | options.OnChange(OnOptionsChanged);
22 | }
23 | }
24 |
25 | protected virtual void OnOptionsChanged(TOptions options)
26 | {
27 | Options = options;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Abstractions/Xfrogcn.PluginFactory.Abstractions.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Xfrogcn.PluginFactory.Abstractions
5 | Xfrogcn.PluginFactory
6 | Xfrogcn.PluginFactory.Abstractions
7 | true
8 | true
9 | false
10 | whb-vs-cert.pfx
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | all
20 | runtime; build; native; contentfiles; analyzers; buildtransitive
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/PluginFactory/DefaultPluginFactory.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Linq;
6 | using System.Security.Cryptography.X509Certificates;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace Xfrogcn.PluginFactory
11 | {
12 | public class DefaultPluginFactory : IPluginFactory
13 | {
14 | private IPluginLoader _loader;
15 |
16 | private readonly ILogger _logger;
17 |
18 | private readonly IServiceProvider _serviceProvider;
19 |
20 | private List _pluginList = new List();
21 |
22 | public DefaultPluginFactory(
23 | IPluginLoader loader,
24 | ILoggerFactory loggerFactory,
25 | IServiceProvider serviceProvider,
26 | IEnumerable plugins)
27 | {
28 | if (loader == null)
29 | {
30 | throw new ArgumentNullException(nameof(loader));
31 | }
32 | if( loggerFactory == null)
33 | {
34 | throw new ArgumentNullException(nameof(loggerFactory));
35 | }
36 |
37 |
38 |
39 | if (plugins != null)
40 | {
41 | List disabledList = loader.PluginList.Where(x => !x.IsEnable).ToList();
42 | _pluginList.AddRange(plugins);
43 | // 禁用插件列表
44 | }
45 |
46 |
47 | _loader = loader;
48 | _logger = loggerFactory.CreateLogger("PluginFactory");
49 | _serviceProvider = serviceProvider;
50 | }
51 |
52 | public async Task StartAsync(CancellationToken cancellationToken)
53 | {
54 | await ForEachPlugin(async (plugin, ctx) =>
55 | {
56 | Log._pluginBeginStart(_logger, null);
57 | Stopwatch sw = new Stopwatch();
58 | try
59 | {
60 | await plugin.StartAsync(ctx);
61 | }
62 | catch (Exception e)
63 | {
64 | Log._pluginStartError(_logger, e.ToString(), e);
65 | throw;
66 | }
67 | sw.Stop();
68 | Log._pluginCompleteStart(_logger, sw.ElapsedMilliseconds, null);
69 |
70 | }, cancellationToken);
71 | }
72 |
73 | public async Task StopAsync(CancellationToken cancellationToken)
74 | {
75 | await ForEachPlugin(async (plugin, ctx) =>
76 | {
77 | Log._pluginBeginStop(_logger, null);
78 | Stopwatch sw = new Stopwatch();
79 | try
80 | {
81 | await plugin.StopAsync(ctx);
82 | }
83 | catch (Exception e)
84 | {
85 | Log._pluginStopError(_logger, e.ToString(), e);
86 | throw;
87 | }
88 | sw.Stop();
89 | Log._pluginCompleteStop(_logger, sw.ElapsedMilliseconds, null);
90 |
91 | }, cancellationToken);
92 | }
93 |
94 | protected async Task ForEachPlugin(Func proc, CancellationToken cancellationToken)
95 | {
96 | foreach (IPlugin p in _pluginList)
97 | {
98 | PluginInfo pi = getPluginInfo(p.GetType());
99 | PluginInfoLogValue logValue = new PluginInfoLogValue(pi);
100 | using (var scope = _logger.BeginScope(logValue))
101 | {
102 | IPluginContext context = new PluginContext(
103 | this,
104 | _serviceProvider,
105 | cancellationToken
106 | );
107 | await proc(p, context);
108 | }
109 | }
110 | }
111 |
112 | private PluginInfo getPluginInfo(Type pluginType)
113 | {
114 | return _loader.PluginList.First(x => x.PluginType == pluginType);
115 | }
116 |
117 |
118 | public IPlugin GetPluginById(string id)
119 | {
120 | var pi = _loader.PluginList.FirstOrDefault(x => x.Id == id);
121 | if( pi == null)
122 | {
123 | return null;
124 | }
125 |
126 | return _pluginList.FirstOrDefault(x => x.GetType() == pi.PluginType);
127 | }
128 |
129 | public IPlugin GetPluginByName(string name)
130 | {
131 | var pi = _loader.PluginList.FirstOrDefault(x => x.Name == name || x.Alias == name);
132 | if (pi == null)
133 | {
134 | return null;
135 | }
136 |
137 | return _pluginList.FirstOrDefault(x => x.GetType() == pi.PluginType);
138 | }
139 |
140 | public TPlugin GetPlugin()
141 | where TPlugin : class, IPlugin
142 | {
143 | return _pluginList.FirstOrDefault(x => x.GetType() == typeof(TPlugin)) as TPlugin;
144 | }
145 |
146 | static class Log
147 | {
148 | static EventId PluginStartingEventId = new EventId(100, "PluginStarting");
149 | static EventId PluginStartFinishEventId = new EventId(101, "PluginStartFinish");
150 | static EventId PluginStoppingEventId = new EventId(102, "PluginStopping");
151 | static EventId PluginStopFinishEventId = new EventId(103, "PluginStopFinish");
152 |
153 | static EventId PluginStartErrorEventId = new EventId(110, "PluginStartError");
154 | static EventId PluginStopErrorEventId = new EventId(111, "PluginStopError");
155 |
156 | public static Action _pluginBeginStart =
157 | LoggerMessage.Define(LogLevel.Information, PluginStartingEventId, Resources.PluginBeginStart);
158 | public static Action _pluginCompleteStart =
159 | LoggerMessage.Define(LogLevel.Information, PluginStartFinishEventId, Resources.PluginCompleteStart);
160 | public static Action _pluginStartError =
161 | LoggerMessage.Define(LogLevel.Error, PluginStartErrorEventId, Resources.PluginStartException);
162 |
163 | public static Action _pluginBeginStop =
164 | LoggerMessage.Define(LogLevel.Information, PluginStoppingEventId, Resources.PluginBeginStop);
165 | public static Action _pluginCompleteStop =
166 | LoggerMessage.Define(LogLevel.Information, PluginStartFinishEventId, Resources.PluginCompleteStop);
167 | public static Action _pluginStopError =
168 | LoggerMessage.Define(LogLevel.Error, PluginStopErrorEventId, Resources.PluginStopException);
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/PluginFactory/DefaultPluginLoader.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Security.Cryptography.X509Certificates;
8 |
9 | namespace Xfrogcn.PluginFactory
10 | {
11 | ///
12 | /// 默认的插件载入器
13 | ///
14 | public class DefaultPluginLoader : IPluginLoader
15 | {
16 | readonly PluginFactoryOptions _options;
17 | readonly IServiceCollection _services;
18 |
19 | public DefaultPluginLoader(PluginFactoryOptions options, IServiceCollection services)
20 | {
21 | _options = options;
22 | _services = services;
23 | }
24 |
25 | private List _pluginList = new List();
26 | public IReadOnlyList PluginList => _pluginList;
27 |
28 | public virtual void Load()
29 | {
30 |
31 |
32 | lock (_pluginList)
33 | {
34 | // 载入附加组件
35 | foreach(Assembly assembly in _options.AdditionalAssemblies)
36 | {
37 | LoadPluginFromAssembly(assembly);
38 | }
39 | if (_options.FileProvider == null)
40 | {
41 | return;
42 | }
43 |
44 | var dir = _options.FileProvider.GetDirectoryContents(string.Empty);
45 |
46 | if (!dir.Exists)
47 | {
48 | return;
49 | }
50 | foreach (var p in dir)
51 | {
52 | if (p.IsDirectory)
53 | {
54 | // 隔离插件
55 | var pluginDir = _options.FileProvider.GetDirectoryContents(p.Name);
56 | foreach (var pd in pluginDir)
57 | {
58 | if (pd.IsDirectory)
59 | {
60 | continue;
61 | }
62 | string fileName = Path.GetFileNameWithoutExtension(pd.PhysicalPath);
63 | if (fileName.Equals(p.Name, StringComparison.OrdinalIgnoreCase))
64 | {
65 | // 插件程序集
66 | LoadPluginFromAssembly(pd.PhysicalPath);
67 | }
68 |
69 | }
70 | }
71 | else if (p.PhysicalPath != null && Path.GetExtension(p.PhysicalPath) == ".dll")
72 | {
73 | //
74 | LoadPluginFromAssembly(p.PhysicalPath);
75 | }
76 |
77 | }
78 |
79 | }
80 | }
81 |
82 | protected virtual void LoadPluginFromAssembly(string assemblyPath)
83 | {
84 | if (_options.Predicate!=null && !_options.Predicate(assemblyPath))
85 | {
86 | return;
87 | }
88 |
89 | IsolationAssemblyLoadContext context = new IsolationAssemblyLoadContext(assemblyPath);
90 | var assembly = context.Load();
91 | if (assembly != null)
92 | {
93 | LoadPluginFromAssembly(assembly);
94 | }
95 | }
96 |
97 |
98 | protected virtual void LoadPluginFromAssembly(Assembly assembly)
99 | {
100 | if (assembly == null)
101 | {
102 | // 异常
103 | throw new ArgumentNullException(nameof(assembly));
104 | }
105 |
106 | var types = assembly.GetExportedTypes();
107 | List plist = new List();
108 | foreach (Type t in types)
109 | {
110 | PluginInfo pi = LoadPluginFromType(t);
111 | if (pi != null && !_pluginList.Any(p => p.PluginType == pi.PluginType))
112 | {
113 | _pluginList.Add(pi);
114 | }
115 | }
116 |
117 | }
118 |
119 | protected virtual PluginInfo LoadPluginFromType(Type type)
120 | {
121 | if(type.IsAbstract)
122 | {
123 | return null;
124 | }
125 |
126 | Type[] iTypes = type.GetInterfaces();
127 | if(iTypes==null || iTypes.Length == 0)
128 | {
129 | return null;
130 | }
131 | var pluginType = typeof(IPlugin);
132 |
133 |
134 |
135 | PluginInfo pi = null;
136 | if (typeof(IPlugin).GetTypeInfo().IsAssignableFrom(type))
137 | {
138 | pi = new PluginInfo()
139 | {
140 | PluginType = type,
141 | CanInit = false,
142 | CanConfig = false
143 | };
144 | }
145 | if(pi == null)
146 | {
147 | return null;
148 | }
149 |
150 | var attr = type.GetCustomAttributes(typeof(PluginAttribute), false).OfType().FirstOrDefault();
151 | if(attr != null)
152 | {
153 | pi.Id = attr.Id;
154 | pi.Name = attr.Name;
155 | pi.Alias = attr.Alias;
156 | pi.Description = attr.Description;
157 | }
158 | pi.Id = string.IsNullOrEmpty(pi.Id) ? type.FullName : pi.Id;
159 | pi.Name = string.IsNullOrEmpty(pi.Name) ? (string.IsNullOrEmpty(pi.Alias) ? type.FullName : pi.Alias) : pi.Name;
160 |
161 | // 初始化
162 | if (typeof(ISupportInitPlugin).IsAssignableFrom(type))
163 | {
164 | pi.CanInit = true;
165 | }
166 |
167 | // 配置
168 | Type cfgType = iTypes.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ISupportConfigPlugin<>));
169 | if (cfgType != null)
170 | {
171 | pi.ConfigType = cfgType.GenericTypeArguments[0];
172 | pi.CanConfig = true;
173 | }
174 |
175 | // 是否禁用
176 | if ( (!String.IsNullOrEmpty(pi.Alias) && _options.DisabledPluginList.Contains(pi.Alias)) ||
177 | _options.DisabledPluginList.Contains(pi.Name) ||
178 | _options.DisabledPluginList.Contains(pi.PluginType.FullName) ||
179 | _options.DisabledPluginList.Contains(pi.PluginType.FullName.Replace("+",".")))
180 | {
181 | pi.IsEnable = false;
182 | }
183 |
184 | return pi;
185 | }
186 |
187 |
188 | public virtual void Init()
189 | {
190 | var initList = _pluginList.Where(x => x.CanInit && x.IsEnable).ToList();
191 | if (initList.Count == 0)
192 | {
193 | return;
194 | }
195 |
196 | IPluginInitContext initContext = new PluginInitContext(_options.PluginPath, this, _services);
197 | var initInstanceList = initContext.InitServiceProvider.GetRequiredService>();
198 | foreach (ISupportInitPlugin p in initInstanceList)
199 | {
200 | p.Init(initContext);
201 | }
202 |
203 | }
204 |
205 |
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/src/PluginFactory/HostBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reflection;
4 | using Microsoft.Extensions.Configuration;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Xfrogcn.PluginFactory;
7 |
8 | namespace Microsoft.Extensions.Hosting
9 | {
10 | public static class HostBuilderExtensions
11 | {
12 | public static IHostBuilder UsePluginFactory(this IHostBuilder hostBuilder)
13 | {
14 | return UsePluginFactory(hostBuilder, (IConfiguration)null);
15 | }
16 |
17 | public static IHostBuilder UsePluginFactory(this IHostBuilder hostBuilder, IConfiguration configuration)
18 | {
19 | return UsePluginFactory(hostBuilder, configuration, (Assembly)null);
20 | }
21 |
22 | public static IHostBuilder UsePluginFactory(this IHostBuilder hostBuilder, IConfiguration configuration, Assembly assembly)
23 | {
24 | return UsePluginFactory(hostBuilder, configuration, new Assembly[] { assembly });
25 | }
26 |
27 |
28 |
29 | public static IHostBuilder UsePluginFactory(this IHostBuilder hostBuilder, IConfiguration configuration, IEnumerable< Assembly> assemblies)
30 | {
31 | hostBuilder.ConfigureServices((context, sc) =>
32 | {
33 | configuration = configuration ?? context.Configuration;
34 | sc.AddPluginFactory(configuration, assemblies);
35 | });
36 | return hostBuilder;
37 | }
38 |
39 | public static IHostBuilder UsePluginFactory(this IHostBuilder hostBuilder, Assembly assembly)
40 | {
41 | return UsePluginFactory(hostBuilder, new Assembly[] { assembly });
42 | }
43 |
44 | public static IHostBuilder UsePluginFactory(this IHostBuilder hostBuilder, IEnumerable assemblies)
45 | {
46 | return UsePluginFactory(hostBuilder, null, assemblies);
47 | }
48 |
49 | public static IHostBuilder UsePluginFactory(this IHostBuilder hostBuilder, Action options)
50 | {
51 | return UsePluginFactory(hostBuilder, null, options);
52 | }
53 |
54 | public static IHostBuilder UsePluginFactory(this IHostBuilder hostBuilder, IConfiguration configuration, Action configureOptions)
55 | {
56 | hostBuilder.ConfigureServices((context, sc) =>
57 | {
58 | configuration = configuration ?? context.Configuration;
59 | sc.AddPluginFactory(configuration, configureOptions);
60 | });
61 | return hostBuilder;
62 | }
63 |
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/PluginFactory/PluginContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 | using System.Threading;
6 |
7 | namespace Xfrogcn.PluginFactory
8 | {
9 | class PluginContext : IPluginContext
10 | {
11 | public PluginContext(IPluginFactory pluginFactory, IServiceProvider serviceProvider, CancellationToken cancellationToken)
12 | {
13 | PluginFactory = pluginFactory;
14 | ServiceProvider = serviceProvider;
15 | CancellationToken = cancellationToken;
16 | }
17 |
18 | public IPluginFactory PluginFactory { get; }
19 |
20 | public IServiceProvider ServiceProvider { get; }
21 |
22 | public CancellationToken CancellationToken { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/PluginFactory/PluginFactoryServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using Microsoft.Extensions.Configuration;
7 | using Microsoft.Extensions.DependencyInjection.Extensions;
8 | using Microsoft.Extensions.FileProviders;
9 | using Microsoft.Extensions.Options;
10 | using Xfrogcn.PluginFactory;
11 |
12 | namespace Microsoft.Extensions.DependencyInjection
13 | {
14 | public static class PluginFactoryServiceCollectionExtensions
15 | {
16 |
17 | public static readonly string DEFAULT_PLUGIN_PATH = "Plugins";
18 |
19 | public static IServiceCollection AddPluginFactory(this IServiceCollection services)
20 | {
21 | services.AddOptions();
22 |
23 | var options = createDefaultOptions();
24 |
25 | services.AddPluginFactory(options, null);
26 |
27 | return services;
28 | }
29 |
30 | public static IServiceCollection AddPluginFactory(this IServiceCollection services, IConfiguration configuration)
31 | {
32 | return AddPluginFactory(services, configuration, (Assembly)null);
33 | }
34 |
35 | public static IServiceCollection AddPluginFactory(this IServiceCollection services, IConfiguration configuration, Assembly assembly)
36 | {
37 | return AddPluginFactory(services, configuration, new Assembly[] { assembly });
38 | }
39 |
40 |
41 |
42 | public static IServiceCollection AddPluginFactory(this IServiceCollection services, IConfiguration configuration, IEnumerable assemblies)
43 | {
44 | if (configuration == null)
45 | {
46 | throw new ArgumentNullException(nameof(configuration));
47 | }
48 |
49 | //注入配置
50 | PluginFactoryConfigration factoryConfigration = new PluginFactoryConfigration(configuration);
51 | services.TryAddSingleton(factoryConfigration);
52 |
53 | // 从配置中获取设置
54 | PluginFactoryOptions options = createDefaultOptions();
55 | if (assemblies != null)
56 | {
57 | foreach (var a in assemblies)
58 | {
59 | if (a == null)
60 | {
61 | continue;
62 | }
63 | options.AddAssembly(a);
64 | }
65 | }
66 | options.ConfigFromConfigration(factoryConfigration);
67 |
68 |
69 | services.AddPluginFactory(options, configuration);
70 |
71 |
72 |
73 | return services;
74 | }
75 |
76 |
77 |
78 |
79 | public static IServiceCollection AddPluginFactory(this IServiceCollection services, Action configureOptions)
80 | {
81 | return AddPluginFactory(services, null, configureOptions);
82 | }
83 |
84 | public static IServiceCollection AddPluginFactory(this IServiceCollection services, IConfiguration configuration, Action configureOptions)
85 | {
86 | PluginFactoryOptions options = createDefaultOptions();
87 | if (configureOptions != null)
88 | {
89 | configureOptions(options);
90 | }
91 |
92 | services.AddPluginFactory(options, configuration);
93 |
94 | return services;
95 | }
96 |
97 |
98 | public static IServiceCollection AddPluginFactory(this IServiceCollection services, PluginFactoryOptions options, IConfiguration configuration)
99 | {
100 | if (options == null)
101 | {
102 | throw new ArgumentNullException(nameof(options));
103 | }
104 | services.AddLogging();
105 | services.AddOptions();
106 |
107 |
108 | services.TryAddSingleton(options);
109 |
110 | if( configuration == null)
111 | {
112 | configuration = new ConfigurationBuilder()
113 | .AddInMemoryCollection()
114 | .Build();
115 | }
116 |
117 | // 配置根ConfigurationChangeTokenSource需要
118 | services.TryAddSingleton(configuration);
119 | // 插件全局配置
120 | services.TryAddSingleton(new PluginFactoryConfigration(configuration));
121 |
122 | // 从配置中获取插件设置,以插件类型名称或插件别名作为配置键
123 | services.TryAddSingleton(typeof(IPluginConfigrationProvider<>), typeof(PluginConfigrationProvider<>));
124 |
125 | // 注册配置变更监听
126 | services.TryAddSingleton(typeof(IOptionsChangeTokenSource<>), typeof(ConfigurationChangeTokenSource<>));
127 |
128 | // 注入插件工厂
129 | services.TryAddSingleton();
130 | // 兼容托管服务,在宿主环境中自动调用开始和停止方法
131 | services.AddHostedService((sp) =>
132 | {
133 | IPluginFactory factory = sp.GetRequiredService();
134 | return factory;
135 | });
136 |
137 | IPluginLoader loader = createPluginLoader(services, options);
138 | // 载入器单例
139 | services.TryAddSingleton(loader);
140 |
141 |
142 | return services;
143 | }
144 |
145 | private static PluginFactoryOptions createDefaultOptions()
146 | {
147 | string pluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DEFAULT_PLUGIN_PATH);
148 |
149 | // 默认设置
150 | PluginFactoryOptions options = new PluginFactoryOptions()
151 | {
152 | PluginPath = pluginPath
153 | };
154 | if(Directory.Exists(pluginPath))
155 | {
156 | options.FileProvider = new PhysicalFileProvider(pluginPath);
157 | }
158 | return options;
159 | }
160 |
161 |
162 | private static IPluginLoader createPluginLoader(IServiceCollection services, PluginFactoryOptions options)
163 | {
164 | IPluginLoader loader = new DefaultPluginLoader(options, services);
165 | loader.Load();
166 |
167 |
168 | foreach (PluginInfo pi in loader.PluginList)
169 | {
170 | if (!pi.IsEnable)
171 | {
172 | continue;
173 | }
174 | services.TryAddEnumerable(ServiceDescriptor.Singleton(typeof(IPlugin), pi.PluginType));
175 | }
176 |
177 |
178 |
179 | // 注入配置映射
180 | var list = loader.PluginList.Where(x => x.CanConfig && x.IsEnable).ToList();
181 |
182 | foreach(PluginInfo pi in list)
183 | {
184 | Type cfgOptionsType = typeof(IConfigureOptions<>).MakeGenericType(pi.ConfigType);
185 | Type impleType = typeof(PluginConfigrationOptions<,>).MakeGenericType(pi.PluginType, pi.ConfigType);
186 | services.TryAddEnumerable(ServiceDescriptor.Singleton(cfgOptionsType, impleType));
187 | }
188 |
189 | loader.Init();
190 | return loader;
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/src/PluginFactory/PluginInfoLogValue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Xfrogcn.PluginFactory
7 | {
8 |
9 | ///
10 | /// 记录插件信息的日志值,可用于Logger作用域或日志记录
11 | ///
12 | internal class PluginInfoLogValue : IReadOnlyList>
13 | {
14 | private PluginInfo _pluginInfo = null;
15 |
16 | private string _formatted = null;
17 |
18 | private List> _values;
19 | public PluginInfoLogValue(PluginInfo pluginInfo)
20 | {
21 | if (pluginInfo == null)
22 | {
23 | throw new ArgumentNullException(nameof(pluginInfo));
24 | }
25 | _pluginInfo = pluginInfo;
26 | }
27 |
28 | private List> Values
29 | {
30 | get
31 | {
32 | if (_values == null)
33 | {
34 | var values = new List>();
35 | values.Add(new KeyValuePair("PluginID", _pluginInfo.Id));
36 | values.Add(new KeyValuePair("PluginName", _pluginInfo.Name));
37 | values.Add(new KeyValuePair("PluginAlias", _pluginInfo.Alias??string.Empty));
38 | values.Add(new KeyValuePair("PluginType", _pluginInfo.PluginType.FullName));
39 | _values = values;
40 | }
41 |
42 | return _values;
43 | }
44 | }
45 |
46 |
47 |
48 |
49 |
50 | public KeyValuePair this[int index]
51 | {
52 | get
53 | {
54 | if (index < 0 || index >= Count)
55 | {
56 | throw new IndexOutOfRangeException(nameof(index));
57 | }
58 |
59 | return Values[index];
60 | }
61 | }
62 |
63 | public int Count => Values.Count;
64 |
65 | public IEnumerator> GetEnumerator()
66 | {
67 | return Values.GetEnumerator();
68 | }
69 |
70 | IEnumerator IEnumerable.GetEnumerator()
71 | {
72 | return Values.GetEnumerator();
73 | }
74 |
75 | public override string ToString()
76 | {
77 | if(_formatted == null)
78 | {
79 | var builder = new StringBuilder();
80 | builder.AppendLine(Resources.PluginInfo + ": ");
81 |
82 | for (var i = 0; i < Values.Count; i++)
83 | {
84 | var kvp = Values[i];
85 | builder.Append(kvp.Key);
86 | builder.Append(": ");
87 |
88 | foreach (var value in (IEnumerable