Backup
This commit is contained in:
parent
653ceb0189
commit
4b29a88239
1
.idea/.idea.PSCHelpdesk/.idea/.name
Normal file
1
.idea/.idea.PSCHelpdesk/.idea/.name
Normal file
@ -0,0 +1 @@
|
||||
PSCHelpdesk
|
||||
@ -11,52 +11,18 @@
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="95257dc5-08bd-4c50-8726-85956b3c2c92" name="Changes" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/HetznerServer/ViewModels/ServerViewModel.cs" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/HetznerServer/Views/ServerView.axaml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/HetznerServer/Views/ServerView.axaml.cs" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk.sln" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/.gitignore" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/Directory.Build.props" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Android/Icon.png" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Android/MainActivity.cs" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Android/PSCHelpdesk.Android.csproj" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Android/Properties/AndroidManifest.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Android/Resources/AboutResources.txt" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Android/Resources/drawable-night-v31/avalonia_anim.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Android/Resources/drawable-v31/avalonia_anim.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Android/Resources/drawable/splash_screen.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Android/Resources/values-night/colors.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Android/Resources/values-v31/styles.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Android/Resources/values/colors.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Android/Resources/values/styles.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Browser/PSCHelpdesk.Browser.csproj" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Browser/Program.cs" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Browser/Properties/AssemblyInfo.cs" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Browser/Properties/launchSettings.json" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Browser/runtimeconfig.template.json" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Browser/wwwroot/app.css" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Browser/wwwroot/favicon.ico" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Browser/wwwroot/index.html" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Browser/wwwroot/main.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Desktop/PSCHelpdesk.Desktop.csproj" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Desktop/Program.cs" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.Desktop/app.manifest" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.iOS/AppDelegate.cs" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.iOS/Entitlements.plist" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.iOS/Info.plist" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.iOS/Main.cs" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.iOS/PSCHelpdesk.iOS.csproj" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.iOS/Resources/LaunchScreen.xib" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk.sln" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/App.axaml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/App.axaml.cs" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Assets/avalonia-logo.ico" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/PSCHelpdesk.csproj" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewLocator.cs" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Views/MainWindow.axaml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Views/MainWindow.axaml.cs" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/Shared/ViewModels/ViewModelBase.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.PSCHelpdesk/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.PSCHelpdesk/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/HetznerServer/HetznerServer.cs" beforeDir="false" afterPath="$PROJECT_DIR$/HetznerServer/HetznerServer.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/HetznerServer/HetznerServer.csproj" beforeDir="false" afterPath="$PROJECT_DIR$/HetznerServer/HetznerServer.csproj" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/HetznerServer/HetznerServerBootstrap.cs" beforeDir="false" afterPath="$PROJECT_DIR$/HetznerServer/HetznerServerBootstrap.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/PSCHelpdesk.sln" beforeDir="false" afterPath="$PROJECT_DIR$/PSCHelpdesk.sln" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/PSCHelpdesk.sln.DotSettings.user" beforeDir="false" afterPath="$PROJECT_DIR$/PSCHelpdesk.sln.DotSettings.user" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/PSCHelpdesk.csproj" beforeDir="false" afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/PSCHelpdesk.csproj" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Services/MenuService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Services/MenuService.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Services/PluginService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Services/PluginService.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Startup.cs" beforeDir="false" afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Startup.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs" beforeDir="false" afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Shared/Shared.csproj" beforeDir="false" afterPath="$PROJECT_DIR$/Shared/Shared.csproj" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@ -70,57 +36,38 @@
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="HighlightingSettingsPerFile">
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/667004c0a83e47158c9865f6d54f01d91ac00/34/243c7c7d/DefaultPluginLoader.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/667004c0a83e47158c9865f6d54f01d91ac00/59/bf99e13a/IPluginLoader.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/667004c0a83e47158c9865f6d54f01d91ac00/5a/bd6f74c9/DefaultDirectoryTraverser.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/667004c0a83e47158c9865f6d54f01d91ac00/a5/00a41a89/DefaultAssemblyScanner.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/667004c0a83e47158c9865f6d54f01d91ac00/bb/f5cef1fb/IDirectoryTraverser.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/95e899f2b13d4189874af98ec00443321e400/0d/00fcbad0/PublicNet.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/95e899f2b13d4189874af98ec00443321e400/f7/54c88032/Ipv6.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/a924c6fef45b419cbbc71e86813a9b3a35000/f5/d14c12da/IServerActionsService.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/16a25d89779ca224b34f189688787a4b4f091751ee7706b8b396a5e88aa225/HeaderedSelectingItemsControl.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/27be5d106a789638721745a2a46f9cb0f7a39905117d644638b6e0d56261be/ReplaySubject.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/2c86fa7dbfba747d28348dc8a7bfd0e9d9fe887ed61499b2417e8ee3ec5888/ClrPropertyInfo.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="mock:///Dummy.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="mock:///Dummy.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="mock:///Dummy.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="mock:///Dummy.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="mock:///Dummy.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="mock:///Dummy.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="mock:///Dummy.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="mock:///Dummy.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="mock:///Dummy.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="mock:///Dummy.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/1b81cb3be224213a6a73519b6e340a628d9a1fb8629c351a186a26f6376669/List.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/1eb3d84af5e656ecdf75c8c4df55d5f931ced552db5e04c6fc85b4ecf45288/ConcurrentDictionary.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/2e6883f773fb7c69a15db509adac9a0c068e4ca54fa119e835fd2324311c3b/Ioc.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/33fa97197c7d5dc2e649bd1e13ca25ad6fd7928c626fa7796267b9434bd4ba/ServiceCollection.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/37bd8e7b5e226f349e547a5599819e82461d93768c749730253eeec329288a2b/Array.Enumerators.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/45823974e7a2167a2d01b44459552e2f13db5da9bcf7c58193328bc8a093ae/Process.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/45c62c1f9af1eb2248ec17220752868b3b6728095879e849b6ea733ef7f5ff/CastHelpers.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/4743f513c7ddc8411223a46f0ca426ed929391acebcff993721dff2f0c6b34/ThrowHelper.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/5094e71058416d72278f32a6913f67a9c664bd46c296c5cce3ee63993dde3f/Type.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/571361bb1a5845cf1e8a5dc5b730e5d9b36de9a6be6f33fbd43b4096aa24e7/JsonSettings.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/6cb17de97a8121f48e5408d49811afb1bc7ab58eb8836f1d2e30a5d79/MenuItem.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/711f7792fa6122388fd055dc53371e134cb0064e6918a15d025c8778db5b73/Char.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/7e333a9f3297ba553cccfd3b7c3f1f96125b23d09f883e4d6e66d531559a4c/Type.CoreCLR.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/8874fa6fbc50b05ba8332188d36141eb4bba81fb1f92189ba9d7a25f545/ThrowHelper.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/8f5178316d8f8271b8f333bd87cd9e9f7da6764081a31277e776c6cdcb3a25a8/SystemDialog.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/9d493cae194a42808b62a282d8d797b4ef18a9435baa6da7a73bee8e66ca1/ReactiveCommandBase.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/5cde391207de75962d7bacb899ca2bd3985c86911b152d185b58999a422bf0/Type.CoreCLR.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/87c584767b46b5fd42769be76547105558e6690f785614efddca134b2d682/Type.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/9d4f4ac7db6d2c5d183ab2d92602280ed4349fd6e6a1b6313546b3d01fdab5/ServiceProvider.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/a119bbffad50bc743b255772611ceba491d63fce14a64e86c71a607766c5b0/AvaloniaObject.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/ad21a37fb9c62642a9445c29321d3d136d19fa60425b26806692bbfee3523c/Path.Unix.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/b06c2ce0981bf6e5989cda2e3e737e53c2d54ee6ae7e7ce318d378f52e5f66/StyledElement.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/bf9021a960b74107a7e141aa06bc9d8a0a53c929178c2fb95b1597be8af8dc/ExceptionDispatchInfo.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/c9f1e78ef04f5214281bc448c6a251bb5f9a3135b662f76745bbfb39b14150/First.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/ceb1c14a2cad4d38a6c5bf90e72339c81cc000/_cb8e6/IStorageProvider.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/d0d96f40fdd9bc395fad59e4a8a6539e4eecce4865809ef4be1a473f911eceaa/ReadOnlySpan.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/d6b757e154dd7f8c23e0e785431c97a76e4b9c6bdae38b978238421dbab55d/MethodBaseInvoker.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/d7bc48c79f70bd7a187cf86258b2815e2deb22274953837f735bbfe18f6c69e2/MemoryExtensions.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/dad3f0ebff0dd1f8e1d244c3c44c649be8228d5e25fb37ef1de7f3c0e261c/String.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/de1dc94e26de2eb8cdb9b19e9451c6a0738025b875978dc863de7ba87ee78c4/ServiceCollectionServiceExtensions.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/e6b66c6b133847f9a2c8b591fb074f9d100400/_4277f/UserControl.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/e91a2750b979a5265711aaa020b980ebe7c5168bdf221ad2e32d16e3f74b8382/AsyncVoidMethodBuilder.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/f51bb85dff27841839d69724c7524901cc897dafafe61e611f7a7460e327c/ConcurrentDictionary.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/f53745e7f6e85d65317047eeae9af151fe9aca1cb284d27e5c83962a50be46/ServiceDescriptor.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/f93d3944f37777fd1f922d0d91e96fb77c2f83024101cb4b92f8d9b68080da/SelectMany.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/ad1d58b8dd22123dfae3f2cf5d8b199992465c9c344d032f93a826d8e5539ff/RuntimeType.CoreCLR.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/bd1d5c50194fea68ff3559c160230b0ab50f5acf4ce3061bffd6d62958e2182/ExceptionDispatchInfo.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/HetznerServer/ViewModels/ServerViewModel.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/HetznerServer/Views/ServerView.axaml" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/HetznerServer/Views/ServerView.axaml.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Vendor/Prise/AssemblyScanning/DefaultAssemblyResolver.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Vendor/Prise/DependencyInjection/ServiceCollectionExtensions.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
</component>
|
||||
<component name="InvalidFacetManager">
|
||||
<ignored-facets>
|
||||
<facet id="PSCHelpdesk.Android/invalid/Android Facet - PSCHelpdesk.Android" />
|
||||
</ignored-facets>
|
||||
</component>
|
||||
<component name="ProblemsViewState">
|
||||
<option name="selectedTabId" value="Toolset" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"customColor": "",
|
||||
"associatedIndex": 8
|
||||
@ -227,6 +174,7 @@
|
||||
<workItem from="1730307680217" duration="708000" />
|
||||
<workItem from="1730315057448" duration="72343000" />
|
||||
<workItem from="1730626276818" duration="15656000" />
|
||||
<workItem from="1730716176795" duration="11704000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
@ -242,56 +190,43 @@
|
||||
<breakpoints>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs</url>
|
||||
<line>86</line>
|
||||
<line>78</line>
|
||||
<properties documentPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs" containingFunctionPresentation="Method 'SelectMenu'">
|
||||
<startOffsets>
|
||||
<option value="2529" />
|
||||
<option value="2343" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="2553" />
|
||||
<option value="2367" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="78" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs</url>
|
||||
<line>83</line>
|
||||
<line>75</line>
|
||||
<properties documentPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs" containingFunctionPresentation="Method 'SelectMenu'">
|
||||
<startOffsets>
|
||||
<option value="2363" />
|
||||
<option value="2177" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="2481" />
|
||||
<option value="2295" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="79" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs</url>
|
||||
<line>84</line>
|
||||
<line>76</line>
|
||||
<properties documentPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs" containingFunctionPresentation="Method 'SelectMenu'">
|
||||
<startOffsets>
|
||||
<option value="2490" />
|
||||
<option value="2304" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="2506" />
|
||||
<option value="2320" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="80" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Views/ContentDisplay.axaml.cs</url>
|
||||
<line>71</line>
|
||||
<properties documentPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Views/ContentDisplay.axaml.cs" containingFunctionPresentation="Property 'SelectedContent'">
|
||||
<startOffsets>
|
||||
<option value="2129" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="2213" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="81" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Views/ContentDisplay.axaml.cs</url>
|
||||
<line>37</line>
|
||||
@ -306,17 +241,56 @@
|
||||
<option name="timeStamp" value="82" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs</url>
|
||||
<line>48</line>
|
||||
<properties documentPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs" containingFunctionPresentation="">
|
||||
<url>file://$PROJECT_DIR$/HetznerServer/Menu/MainMenu.cs</url>
|
||||
<line>13</line>
|
||||
<properties documentPath="C:\Users\info\RiderProjects\pschelpdesk\HetznerServer\Menu\MainMenu.cs" containingFunctionPresentation="Method 'addMenu'">
|
||||
<startOffsets>
|
||||
<option value="1425" />
|
||||
<option value="335" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="1470" />
|
||||
<option value="428" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="83" />
|
||||
<option name="timeStamp" value="92" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/HetznerServer/HetznerServer.cs</url>
|
||||
<line>26</line>
|
||||
<properties documentPath="C:\Users\info\RiderProjects\pschelpdesk\HetznerServer\HetznerServer.cs" containingFunctionPresentation="Method 'addMenu'">
|
||||
<startOffsets>
|
||||
<option value="648" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="705" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="94" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/HetznerServer/HetznerServer.cs</url>
|
||||
<line>28</line>
|
||||
<properties documentPath="C:\Users\info\RiderProjects\pschelpdesk\HetznerServer\HetznerServer.cs" containingFunctionPresentation="Method 'addMenu'">
|
||||
<startOffsets>
|
||||
<option value="780" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="808" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="95" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/HetznerServer/HetznerServer.cs</url>
|
||||
<line>27</line>
|
||||
<properties documentPath="C:\Users\info\RiderProjects\pschelpdesk\HetznerServer\HetznerServer.cs" containingFunctionPresentation="Method 'addMenu'">
|
||||
<startOffsets>
|
||||
<option value="714" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="771" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="109" />
|
||||
</line-breakpoint>
|
||||
</breakpoints>
|
||||
</breakpoint-manager>
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Prise.Plugin;
|
||||
using PSCHelpdesk.Plugins.HetznerServer.Menu;
|
||||
using PSCHelpdesk.Plugins.HetznerServer.ViewModels;
|
||||
using PSCHelpdesk.Plugins.HetznerServer.Views;
|
||||
using PSCHelpdesk.Shared.Menu;
|
||||
using PSCHelpdesk.Shared.Plugin;
|
||||
using PSCHelpdesk.Shared.Service;
|
||||
|
||||
namespace PSCHelpdesk.Plugins.HetznerServer;
|
||||
|
||||
@ -16,11 +19,18 @@ public class HetznerServer : Contract
|
||||
|
||||
public async Task<List<Item>> addMenu()
|
||||
{
|
||||
var menu = new MainMenu();
|
||||
//menu.addMenu();
|
||||
|
||||
|
||||
|
||||
var menuService = Ioc.Default.GetService<IMenuService>();
|
||||
var testService = Ioc.Default.GetService<ITestService>();
|
||||
var list = new List<Item>();
|
||||
var serverTab = new Item()
|
||||
{
|
||||
Header = "Server",
|
||||
CommandParameter = typeof(ServerViewModel),
|
||||
// CommandParameter = typeof(ServerViewModel)
|
||||
};
|
||||
list.Add(serverTab);
|
||||
return list;
|
||||
|
||||
@ -20,6 +20,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shared\Shared.csproj" />
|
||||
<ProjectReference Include="..\Vendor\Prise.Plugin\Prise.Plugin.csproj" />
|
||||
<ProjectReference Include="..\Vendor\Prise.Proxy\Prise.Proxy.csproj" />
|
||||
<ProjectReference Include="..\Vendor\Prise.ReverseProxy\Prise.ReverseProxy.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -40,7 +43,6 @@
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.2.0" />
|
||||
<PackageReference Include="Avalonia.Xaml.Interactions" Version="11.2.0" />
|
||||
<PackageReference Include="HetznerCloud.API" Version="1.1.9" />
|
||||
<PackageReference Include="Prise.Plugin" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using PSCHelpdesk.Plugins.HetznerServer.ViewModels;
|
||||
using PSCHelpdesk.Shared.Service;
|
||||
|
||||
namespace PSCHelpdesk.Plugins.HetznerServer;
|
||||
|
||||
@ -9,8 +10,12 @@ using Prise.Plugin;
|
||||
[PluginBootstrapper(PluginType = typeof(HetznerServer))]
|
||||
public class HetznerServerBootstrap : IPluginBootstrapper
|
||||
{
|
||||
//[BootstrapperService(ServiceType = typeof(IMenuService), ProxyType = typeof(MenuService))]
|
||||
//private readonly IMenuService menuService;
|
||||
|
||||
public IServiceCollection Bootstrap(IServiceCollection services)
|
||||
{
|
||||
//services.AddSingleton<IMenuService>(this.menuService);
|
||||
services.AddTransient<ServerViewModel>();
|
||||
return services;
|
||||
}
|
||||
|
||||
20
HetznerServer/Menu/MainMenu.cs
Normal file
20
HetznerServer/Menu/MainMenu.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using Prise.Plugin;
|
||||
using PSCHelpdesk.Shared.Menu;
|
||||
using PSCHelpdesk.Shared.Service;
|
||||
|
||||
namespace PSCHelpdesk.Plugins.HetznerServer.Menu;
|
||||
|
||||
public class MainMenu
|
||||
{
|
||||
[PluginService(ProvidedBy = ProvidedBy.Host, ServiceType = typeof(IMenuService))]
|
||||
private readonly IMenuService menuService;
|
||||
|
||||
public void addMenu()
|
||||
{
|
||||
this.menuService.AddMenuItem(new Item()
|
||||
{
|
||||
Header = "Hetzner",
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
15
HetznerServer/MenuService.cs
Normal file
15
HetznerServer/MenuService.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using Prise.Proxy;
|
||||
using PSCHelpdesk.Shared.Menu;
|
||||
using PSCHelpdesk.Shared.Service;
|
||||
|
||||
namespace PSCHelpdesk.Plugins.HetznerServer;
|
||||
|
||||
public class MenuService : ReverseProxy, IMenuService
|
||||
{
|
||||
public MenuService(object hostService) : base(hostService) { }
|
||||
|
||||
public void AddMenuItem(Item item)
|
||||
{
|
||||
this.InvokeOnHostService<Item>(new[] { item });
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HetznerServer", "HetznerSer
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nextcloud", "Nextcloud\Nextcloud.csproj", "{4C34E3F4-718D-4E15-97A2-AE61A4ACEE02}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Vendor", "Vendor", "{D45462B2-C09A-4BC6-A8B1-F03C6533B084}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prise", "Vendor\Prise\Prise.csproj", "{AD222F87-EAC5-4120-AA33-EB32DF2DDCD5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prise.Plugin", "Vendor\Prise.Plugin\Prise.Plugin.csproj", "{F4EF7157-C5F0-4444-9E49-28F6BD56295F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prise.ReverseProxy", "Vendor\Prise.ReverseProxy\Prise.ReverseProxy.csproj", "{52EFDD1B-B775-49A1-BD3D-E7B7F6A989EA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prise.Proxy", "Vendor\Prise.Proxy\Prise.Proxy.csproj", "{79029F78-5113-46E4-8896-436707A7251F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -60,9 +70,29 @@ Global
|
||||
{4C34E3F4-718D-4E15-97A2-AE61A4ACEE02}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4C34E3F4-718D-4E15-97A2-AE61A4ACEE02}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4C34E3F4-718D-4E15-97A2-AE61A4ACEE02}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AD222F87-EAC5-4120-AA33-EB32DF2DDCD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AD222F87-EAC5-4120-AA33-EB32DF2DDCD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AD222F87-EAC5-4120-AA33-EB32DF2DDCD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AD222F87-EAC5-4120-AA33-EB32DF2DDCD5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F4EF7157-C5F0-4444-9E49-28F6BD56295F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F4EF7157-C5F0-4444-9E49-28F6BD56295F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F4EF7157-C5F0-4444-9E49-28F6BD56295F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F4EF7157-C5F0-4444-9E49-28F6BD56295F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{52EFDD1B-B775-49A1-BD3D-E7B7F6A989EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{52EFDD1B-B775-49A1-BD3D-E7B7F6A989EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{52EFDD1B-B775-49A1-BD3D-E7B7F6A989EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{52EFDD1B-B775-49A1-BD3D-E7B7F6A989EA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{79029F78-5113-46E4-8896-436707A7251F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{79029F78-5113-46E4-8896-436707A7251F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{79029F78-5113-46E4-8896-436707A7251F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{79029F78-5113-46E4-8896-436707A7251F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{552272D2-9506-4F9E-9D53-E908E53D9039} = {E7A87869-4D47-4292-BE39-77D36933088C}
|
||||
{4C34E3F4-718D-4E15-97A2-AE61A4ACEE02} = {E7A87869-4D47-4292-BE39-77D36933088C}
|
||||
{AD222F87-EAC5-4120-AA33-EB32DF2DDCD5} = {D45462B2-C09A-4BC6-A8B1-F03C6533B084}
|
||||
{F4EF7157-C5F0-4444-9E49-28F6BD56295F} = {D45462B2-C09A-4BC6-A8B1-F03C6533B084}
|
||||
{52EFDD1B-B775-49A1-BD3D-E7B7F6A989EA} = {D45462B2-C09A-4BC6-A8B1-F03C6533B084}
|
||||
{79029F78-5113-46E4-8896-436707A7251F} = {D45462B2-C09A-4BC6-A8B1-F03C6533B084}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@ -5,17 +5,21 @@
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACastHelpers_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F45c62c1f9af1eb2248ec17220752868b3b6728095879e849b6ea733ef7f5ff_003FCastHelpers_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AChar_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F711f7792fa6122388fd055dc53371e134cb0064e6918a15d025c8778db5b73_003FChar_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AClrPropertyInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F2c86fa7dbfba747d28348dc8a7bfd0e9d9fe887ed61499b2417e8ee3ec5888_003FClrPropertyInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConcurrentDictionary_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F1eb3d84af5e656ecdf75c8c4df55d5f931ced552db5e04c6fc85b4ecf45288_003FConcurrentDictionary_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConcurrentDictionary_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Ff51bb85dff27841839d69724c7524901cc897dafafe61e611f7a7460e327c_003FConcurrentDictionary_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADefaultAssemblyScanner_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F667004c0a83e47158c9865f6d54f01d91ac00_003Fa5_003F00a41a89_003FDefaultAssemblyScanner_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADefaultDirectoryTraverser_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F667004c0a83e47158c9865f6d54f01d91ac00_003F5a_003Fbd6f74c9_003FDefaultDirectoryTraverser_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADefaultPluginLoader_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F667004c0a83e47158c9865f6d54f01d91ac00_003F34_003F243c7c7d_003FDefaultPluginLoader_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fbd1d5c50194fea68ff3559c160230b0ab50f5acf4ce3061bffd6d62958e2182_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fbf9021a960b74107a7e141aa06bc9d8a0a53c929178c2fb95b1597be8af8dc_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFirst_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fc9f1e78ef04f5214281bc448c6a251bb5f9a3135b662f76745bbfb39b14150_003FFirst_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHeaderedSelectingItemsControl_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F16a25d89779ca224b34f189688787a4b4f091751ee7706b8b396a5e88aa225_003FHeaderedSelectingItemsControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIoc_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F2e6883f773fb7c69a15db509adac9a0c068e4ca54fa119e835fd2324311c3b_003FIoc_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIoc_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F2e6883f773fb7c69a15db509adac9a0c068e4ca54fa119e835fd2324311c3b_003FIoc_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIpv6_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F95e899f2b13d4189874af98ec00443321e400_003Ff7_003F54c88032_003FIpv6_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIServerActionsService_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa924c6fef45b419cbbc71e86813a9b3a35000_003Ff5_003Fd14c12da_003FIServerActionsService_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIStorageProvider_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fceb1c14a2cad4d38a6c5bf90e72339c81cc000_003F_005Fcb8e6_003FIStorageProvider_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AList_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F1b81cb3be224213a6a73519b6e340a628d9a1fb8629c351a186a26f6376669_003FList_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMemoryExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fd7bc48c79f70bd7a187cf86258b2815e2deb22274953837f735bbfe18f6c69e2_003FMemoryExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMethodBaseInvoker_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fd6b757e154dd7f8c23e0e785431c97a76e4b9c6bdae38b978238421dbab55d_003FMethodBaseInvoker_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APath_002EUnix_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fad21a37fb9c62642a9445c29321d3d136d19fa60425b26806692bbfee3523c_003FPath_002EUnix_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
@ -23,26 +27,33 @@
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AReactiveCommandBase_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F9d493cae194a42808b62a282d8d797b4ef18a9435baa6da7a73bee8e66ca1_003FReactiveCommandBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AReadOnlySpan_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fd0d96f40fdd9bc395fad59e4a8a6539e4eecce4865809ef4be1a473f911eceaa_003FReadOnlySpan_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AReplaySubject_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F27be5d106a789638721745a2a46f9cb0f7a39905117d644638b6e0d56261be_003FReplaySubject_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARuntimeType_002ECoreCLR_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fad1d58b8dd22123dfae3f2cf5d8b199992465c9c344d032f93a826d8e5539ff_003FRuntimeType_002ECoreCLR_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASelectMany_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Ff93d3944f37777fd1f922d0d91e96fb77c2f83024101cb4b92f8d9b68080da_003FSelectMany_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AServiceCollection_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F33fa97197c7d5dc2e649bd1e13ca25ad6fd7928c626fa7796267b9434bd4ba_003FServiceCollection_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AServiceDescriptor_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Ff53745e7f6e85d65317047eeae9af151fe9aca1cb284d27e5c83962a50be46_003FServiceDescriptor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AServiceProvider_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F9d4f4ac7db6d2c5d183ab2d92602280ed4349fd6e6a1b6313546b3d01fdab5_003FServiceProvider_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AServiceProvider_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F9d4f4ac7db6d2c5d183ab2d92602280ed4349fd6e6a1b6313546b3d01fdab5_003FServiceProvider_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AString_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fdad3f0ebff0dd1f8e1d244c3c44c649be8228d5e25fb37ef1de7f3c0e261c_003FString_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStyledElement_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fb06c2ce0981bf6e5989cda2e3e737e53c2d54ee6ae7e7ce318d378f52e5f66_003FStyledElement_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASystemDialog_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F8f5178316d8f8271b8f333bd87cd9e9f7da6764081a31277e776c6cdcb3a25a8_003FSystemDialog_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F4743f513c7ddc8411223a46f0ca426ed929391acebcff993721dff2f0c6b34_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F8874fa6fbc50b05ba8332188d36141eb4bba81fb1f92189ba9d7a25f545_003FThrowHelper_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AType_002ECoreCLR_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F5cde391207de75962d7bacb899ca2bd3985c86911b152d185b58999a422bf0_003FType_002ECoreCLR_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AType_002ECoreCLR_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F7e333a9f3297ba553cccfd3b7c3f1f96125b23d09f883e4d6e66d531559a4c_003FType_002ECoreCLR_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AType_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F87c584767b46b5fd42769be76547105558e6690f785614efddca134b2d682_003FType_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AType_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F5094e71058416d72278f32a6913f67a9c664bd46c296c5cce3ee63993dde3f_003FType_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUserControl_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fe6b66c6b133847f9a2c8b591fb074f9d100400_003F_005F4277f_003FUserControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue"></s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexRemoved">True</s:Boolean>
|
||||
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue"><AssemblyExplorer>
|
||||
<Assembly Path="/home/thomas/.nuget/packages/hcloud-api-net/1.0.0/lib/netstandard2.0/hcloud-api.dll" />
|
||||
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue"><AssemblyExplorer>
|
||||
<Assembly Path="\home\thomas\.nuget\packages\hcloud-api-net\1.0.0\lib\netstandard2.0\hcloud-api.dll" />
|
||||
<Assembly Path="C:\Users\info\.nuget\packages\prise.pluginbridge\1.7.5\lib\netstandard2.0\Prise.PluginBridge.dll" />
|
||||
<Assembly Path="C:\Users\info\.nuget\packages\microsoft.extensions.dependencymodel\6.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyModel.dll" />
|
||||
</AssemblyExplorer></s:String>
|
||||
<s:Boolean x:Key="/Default/UnloadedProject/UnloadedProjects/=1868ceb9_002D4dab_002D4196_002D91ae_002D60765bdd9820_0023PSCHelpdesk_002EiOS/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UnloadedProject/UnloadedProjects/=1c4ca302_002Da031_002D439a_002Dbfcb_002D42c4bac467f2_0023PSCHelpdesk_002EAndroid/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UnloadedProject/UnloadedProjects/=a910fef2_002D046b_002D4956_002Dbc0a_002Daa57ec9a6b27_0023PSCHelpdesk_002EBrowser/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
|
||||
|
||||
</wpf:ResourceDictionary>
|
||||
@ -18,17 +18,17 @@
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.2.0" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.2.0" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Nucs.JsonSettings2" Version="2.0.3" />
|
||||
<PackageReference Include="Prise" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\HetznerServer\HetznerServer.csproj" />
|
||||
<ProjectReference Include="..\..\Nextcloud\Nextcloud.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\Shared.csproj" />
|
||||
<ProjectReference Include="..\..\Vendor\Prise.Plugin\Prise.Plugin.csproj" />
|
||||
<ProjectReference Include="..\..\Vendor\Prise\Prise.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using Avalonia.Collections;
|
||||
using System.Collections.Generic;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using PSCHelpdesk.Shared.Menu;
|
||||
using PSCHelpdesk.Shared.Service;
|
||||
@ -10,12 +10,12 @@ namespace PSCHelpdesk.Services;
|
||||
|
||||
public class MenuService: ReactiveObject, IMenuService
|
||||
{
|
||||
private AvaloniaList<Item> menuItems = new AvaloniaList<Item>();
|
||||
private AvaloniaList<Item> menuOptionItems = new AvaloniaList<Item>();
|
||||
private List<Item> menuItems = new List<Item>();
|
||||
private List<Item> menuOptionItems = new List<Item>();
|
||||
|
||||
public event EventHandler MenuChanged;
|
||||
|
||||
public AvaloniaList<Item> MenuItems
|
||||
public List<Item> MenuItems
|
||||
{
|
||||
get => this.menuItems;
|
||||
private set => this.RaiseAndSetIfChanged(ref this.menuItems, value);
|
||||
@ -24,7 +24,7 @@ public class MenuService: ReactiveObject, IMenuService
|
||||
/// <summary>
|
||||
/// Gets the list of options items shown in the hamburger menu (at the bottom).
|
||||
/// </summary>
|
||||
public AvaloniaList<Item> MenuOptionItems
|
||||
public List<Item> MenuOptionItems
|
||||
{
|
||||
get => this.menuOptionItems;
|
||||
private set => this.RaiseAndSetIfChanged(ref this.menuOptionItems, value);
|
||||
|
||||
@ -59,6 +59,7 @@ public class PluginService
|
||||
|
||||
var pluginLoader = Ioc.Default.GetService(typeof(IPluginLoader)) as IPluginLoader;
|
||||
var menuService = Ioc.Default.GetRequiredService<IMenuService>();
|
||||
var testService = Ioc.Default.GetRequiredService<ITestService>();
|
||||
var appService = Ioc.Default.GetService(typeof(AppService)) as AppService;
|
||||
//var settingsService = Ioc.Default.GetService<SettingsManager>();
|
||||
|
||||
@ -70,10 +71,11 @@ public class PluginService
|
||||
var plugin = await pluginLoader.LoadPlugin<Contract>(pluginToEnable, configure: (context) =>
|
||||
{
|
||||
context
|
||||
//.AddHostTypes(new[] {typeof(Application)})
|
||||
|
||||
.AddHostService<IMenuService>(menuService)
|
||||
//.AddHostService(appService)
|
||||
.AddHostService<ITestService>(testService)
|
||||
//.AddHostService<SettingsManager>(settingsService)
|
||||
|
||||
;
|
||||
});
|
||||
|
||||
|
||||
@ -14,11 +14,12 @@ class Startup
|
||||
var menuService = new MenuService();
|
||||
|
||||
var te = new ServiceCollection()
|
||||
.AddPrise()
|
||||
.AddSingleton<AppService>()
|
||||
.AddSingleton<IMenuService>(menuService)
|
||||
.AddSingleton<ITestService>(new TestService())
|
||||
.AddSingleton<PluginService>()
|
||||
.AddTransient<PluginListViewModel>();
|
||||
.AddTransient<PluginListViewModel>()
|
||||
.AddPrise();
|
||||
|
||||
var ls = te.BuildServiceProvider();
|
||||
|
||||
|
||||
@ -29,14 +29,6 @@ public class MainWindowViewModel : ViewModelBase
|
||||
get => _selectedItem;
|
||||
set => SetAndRaisePropertyChanged(ref _selectedItem, value);
|
||||
}
|
||||
|
||||
private Item _selectedContent;
|
||||
public Item SelectedContent
|
||||
{
|
||||
get => _selectedContent;
|
||||
set => SetAndRaisePropertyChanged(ref _selectedContent, value);
|
||||
}
|
||||
|
||||
public MainWindowViewModel()
|
||||
{
|
||||
this.MenuItems = new List<Item>();
|
||||
|
||||
6
Shared/Service/ITestService.cs
Normal file
6
Shared/Service/ITestService.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace PSCHelpdesk.Shared.Service;
|
||||
|
||||
public interface ITestService
|
||||
{
|
||||
public void addItem();
|
||||
}
|
||||
9
Shared/Service/TestService.cs
Normal file
9
Shared/Service/TestService.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace PSCHelpdesk.Shared.Service;
|
||||
|
||||
public class TestService: ITestService
|
||||
{
|
||||
public void addItem()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@ -19,8 +19,4 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Views\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
14
Vendor/Prise.Mvc/ApplicationBuilderExtensions.cs
vendored
Normal file
14
Vendor/Prise.Mvc/ApplicationBuilderExtensions.cs
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public static class ApplicationBuilderExtensions
|
||||
{
|
||||
public static IApplicationBuilder EnsureStaticPluginCache(this IApplicationBuilder app)
|
||||
{
|
||||
app.ApplicationServices.GetRequiredService<IPluginCacheAccessorBootstrapper>();
|
||||
return app;
|
||||
}
|
||||
}
|
||||
}
|
||||
71
Vendor/Prise.Mvc/DefaultMvcPluginLoader.cs
vendored
Normal file
71
Vendor/Prise.Mvc/DefaultMvcPluginLoader.cs
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Prise.Caching;
|
||||
using Prise.AssemblyScanning;
|
||||
using Prise.AssemblyLoading;
|
||||
using System.Linq;
|
||||
using Prise.Utils;
|
||||
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public class DefaultMvcPluginLoader : IMvcPluginLoader
|
||||
{
|
||||
protected readonly IAssemblyScanner assemblyScanner;
|
||||
protected readonly IPluginTypeSelector pluginTypeSelector;
|
||||
protected readonly IAssemblyLoader assemblyLoader;
|
||||
protected readonly IPluginCache pluginCache;
|
||||
public DefaultMvcPluginLoader(IAssemblyScanner assemblyScanner,
|
||||
IPluginTypeSelector pluginTypeSelector,
|
||||
IAssemblyLoader assemblyLoader,
|
||||
IPluginCache pluginCache)
|
||||
{
|
||||
this.assemblyScanner = assemblyScanner;
|
||||
this.pluginTypeSelector = pluginTypeSelector;
|
||||
this.assemblyLoader = assemblyLoader;
|
||||
this.pluginCache = pluginCache;
|
||||
}
|
||||
|
||||
public async virtual Task<IEnumerable<AssemblyScanResult>> FindPlugins<T>(string pathToPlugins)
|
||||
{
|
||||
return (await this.assemblyScanner.Scan(new AssemblyScannerOptions
|
||||
{
|
||||
StartingPath = pathToPlugins,
|
||||
PluginType = typeof(T)
|
||||
}));
|
||||
}
|
||||
|
||||
public async virtual Task<IAssemblyShim> LoadPluginAssembly<T>(AssemblyScanResult plugin, Action<PluginLoadContext> configureLoadContext = null)
|
||||
{
|
||||
var pluginLoadContext = ToPluginLoadContext<T>(plugin);
|
||||
|
||||
configureLoadContext?.Invoke(pluginLoadContext);
|
||||
|
||||
pluginLoadContext.AddMvcTypes();
|
||||
|
||||
var pluginAssembly = await this.assemblyLoader.Load(pluginLoadContext);
|
||||
|
||||
this.pluginCache.Add(pluginAssembly, pluginLoadContext.HostServices.Select(s => s.ServiceType));
|
||||
|
||||
return pluginAssembly;
|
||||
}
|
||||
|
||||
public async virtual Task UnloadPluginAssembly<T>(AssemblyScanResult plugin)
|
||||
{
|
||||
var pluginLoadContext = ToPluginLoadContext<T>(plugin);
|
||||
await this.assemblyLoader.Unload(pluginLoadContext);
|
||||
|
||||
var pathToAssembly = Path.Combine(plugin.AssemblyPath, plugin.AssemblyName);
|
||||
this.pluginCache.Remove(pathToAssembly);
|
||||
}
|
||||
|
||||
protected PluginLoadContext ToPluginLoadContext<T>(AssemblyScanResult plugin)
|
||||
{
|
||||
var hostFramework = HostFrameworkUtils.GetHostframeworkFromHost();
|
||||
var pathToAssembly = Path.Combine(plugin.AssemblyPath, plugin.AssemblyName);
|
||||
return PluginLoadContext.DefaultPluginLoadContext(pathToAssembly, typeof(T), hostFramework);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Vendor/Prise.Mvc/DefaultMvcRazorPluginLoader.cs
vendored
Normal file
34
Vendor/Prise.Mvc/DefaultMvcRazorPluginLoader.cs
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Prise.Caching;
|
||||
using Prise.AssemblyScanning;
|
||||
using Prise.AssemblyLoading;
|
||||
using System.Linq;
|
||||
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public class DefaultMvcRazorPluginLoader : DefaultMvcPluginLoader
|
||||
{
|
||||
public DefaultMvcRazorPluginLoader(IAssemblyScanner assemblyScanner,
|
||||
IPluginTypeSelector pluginTypeSelector,
|
||||
IAssemblyLoader assemblyLoader,
|
||||
IPluginCache pluginCache) : base(assemblyScanner, pluginTypeSelector, assemblyLoader, pluginCache) { }
|
||||
|
||||
public override async Task<IAssemblyShim> LoadPluginAssembly<T>(AssemblyScanResult plugin, Action<PluginLoadContext> configureLoadContext = null)
|
||||
{
|
||||
var pluginLoadContext = ToPluginLoadContext<T>(plugin);
|
||||
|
||||
configureLoadContext?.Invoke(pluginLoadContext);
|
||||
|
||||
pluginLoadContext
|
||||
.AddMvcRazorTypes();
|
||||
|
||||
var pluginAssembly = await this.assemblyLoader.Load(pluginLoadContext);
|
||||
|
||||
this.pluginCache.Add(pluginAssembly, pluginLoadContext.HostServices.Select(s => s.ServiceType));
|
||||
|
||||
return pluginAssembly;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Vendor/Prise.Mvc/DefaultPriseMvcActionDescriptorChangeProvider.cs
vendored
Normal file
25
Vendor/Prise.Mvc/DefaultPriseMvcActionDescriptorChangeProvider.cs
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public class DefaultPriseMvcActionDescriptorChangeProvider : IActionDescriptorChangeProvider, IPriseMvcActionDescriptorChangeProvider
|
||||
{
|
||||
public CancellationTokenSource TokenSource { get; private set; }
|
||||
|
||||
public bool HasChanged { get; set; }
|
||||
|
||||
public IChangeToken GetChangeToken()
|
||||
{
|
||||
TokenSource = new CancellationTokenSource();
|
||||
return new CancellationChangeToken(TokenSource.Token);
|
||||
}
|
||||
|
||||
public void TriggerPluginChanged()
|
||||
{
|
||||
HasChanged = true;
|
||||
TokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
76
Vendor/Prise.Mvc/DefaultPriseMvcControllerActivator.cs
vendored
Normal file
76
Vendor/Prise.Mvc/DefaultPriseMvcControllerActivator.cs
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Prise.Activation;
|
||||
using Prise.Infrastructure;
|
||||
using Prise.Plugin;
|
||||
using Prise.Proxy;
|
||||
using Prise.Caching;
|
||||
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public class DefaultPriseMvcControllerActivator : IControllerActivator
|
||||
{
|
||||
public object Create(ControllerContext context)
|
||||
{
|
||||
var cache = context.HttpContext.RequestServices.GetRequiredService<IPluginCache>();
|
||||
var controllerType = context.ActionDescriptor.ControllerTypeInfo.AsType();
|
||||
|
||||
foreach (var cachedPluginAssembly in cache.GetAll())
|
||||
{
|
||||
var pluginAssembly = cachedPluginAssembly.AssemblyShim.Assembly;
|
||||
var pluginControllerType = pluginAssembly.GetTypes().FirstOrDefault(t => t.Name == controllerType.Name);
|
||||
if (pluginControllerType != null)
|
||||
{
|
||||
var activatorContextProvider = context.HttpContext.RequestServices.GetRequiredService<IPluginActivationContextProvider>();
|
||||
var remotePluginActivator = context.HttpContext.RequestServices.GetRequiredService<IRemotePluginActivator>();
|
||||
var proxyCreator = context.HttpContext.RequestServices.GetRequiredService<IPluginProxyCreator>();
|
||||
var resultConverter = context.HttpContext.RequestServices.GetRequiredService<IResultConverter>();
|
||||
var parameterConverter = context.HttpContext.RequestServices.GetRequiredService<IParameterConverter>();
|
||||
|
||||
object controller = null;
|
||||
IPluginBootstrapper bootstrapperProxy = null;
|
||||
IServiceCollection hostServices = new ServiceCollection();
|
||||
foreach (var hostServiceType in cachedPluginAssembly.HostTypes)
|
||||
hostServices.Add(new ServiceDescriptor(hostServiceType, context.HttpContext.RequestServices.GetRequiredService(hostServiceType)));
|
||||
|
||||
var pluginActivationContext = activatorContextProvider.ProvideActivationContext(pluginControllerType, cachedPluginAssembly.AssemblyShim);
|
||||
|
||||
if (pluginActivationContext.PluginBootstrapperType != null)
|
||||
{
|
||||
var remoteBootstrapperInstance = remotePluginActivator.CreateRemoteBootstrapper(pluginActivationContext, hostServices);
|
||||
|
||||
var remoteBootstrapperProxy = proxyCreator.CreateBootstrapperProxy(remoteBootstrapperInstance);
|
||||
|
||||
bootstrapperProxy = remoteBootstrapperProxy;
|
||||
}
|
||||
|
||||
controller = remotePluginActivator.CreateRemoteInstance(
|
||||
pluginActivationContext,
|
||||
bootstrapperProxy,
|
||||
hostServices: hostServices
|
||||
);
|
||||
|
||||
var controllerContext = new ControllerContext();
|
||||
controllerContext.HttpContext = context.HttpContext;
|
||||
var controllerContextProperty = controllerType.GetProperty("ControllerContext");
|
||||
controllerContextProperty.SetValue(controller, controllerContext);
|
||||
|
||||
return controller;
|
||||
}
|
||||
}
|
||||
|
||||
// Use MSFT's own activator utilities to create a controller instance
|
||||
// This avoids us to require to register all controllers as services
|
||||
return ActivatorUtilities.CreateInstance(context.HttpContext.RequestServices, controllerType);
|
||||
}
|
||||
|
||||
public void Release(ControllerContext context, object controller)
|
||||
{
|
||||
(controller as IDisposable)?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Vendor/Prise.Mvc/DefaultPrisePluginViewsAssemblyFileProvider.cs
vendored
Normal file
72
Vendor/Prise.Mvc/DefaultPrisePluginViewsAssemblyFileProvider.cs
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Prise.Caching;
|
||||
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public class DefaultPrisePluginViewsAssemblyFileProvider : IFileProvider
|
||||
{
|
||||
protected readonly PhysicalFileProvider webRootFileProvider;
|
||||
protected readonly string pathToPlugins;
|
||||
public DefaultPrisePluginViewsAssemblyFileProvider(string hostingRootPath, string pathToPlugins)
|
||||
{
|
||||
if (!Path.IsPathRooted(pathToPlugins))
|
||||
throw new ArgumentException($"{nameof(pathToPlugins)} must be rooted (absolute path).");
|
||||
|
||||
this.pathToPlugins = pathToPlugins;
|
||||
this.webRootFileProvider = new PhysicalFileProvider(hostingRootPath);
|
||||
}
|
||||
|
||||
private IPluginCache GetLoadedPluginsCache()
|
||||
{
|
||||
return DefaultStaticPluginCacheAccessor.CurrentCache;
|
||||
}
|
||||
|
||||
private IFileProvider GetPluginFileProvider(string subpath)
|
||||
{
|
||||
var cache = GetLoadedPluginsCache();
|
||||
if (cache == null)
|
||||
return null;
|
||||
|
||||
foreach (var loadedPlugin in cache.GetAll())
|
||||
{
|
||||
var pluginAssemblyName = loadedPlugin.AssemblyShim.Assembly.GetName().Name;
|
||||
var pathToPlugin = Path.Combine(pathToPlugins, pluginAssemblyName);
|
||||
var pathCandidate = Path.Combine(pathToPlugin, SanitizeSubPath(subpath));
|
||||
if (File.Exists(pathCandidate))
|
||||
return new PhysicalFileProvider(pathToPlugin);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private string SanitizeSubPath(string subPath)
|
||||
{
|
||||
if (subPath.StartsWith('/'))
|
||||
return subPath.Substring(1);
|
||||
return subPath;
|
||||
}
|
||||
|
||||
public IDirectoryContents GetDirectoryContents(string subpath)
|
||||
{
|
||||
var pluginFileProvider = GetPluginFileProvider(subpath);
|
||||
if (pluginFileProvider != null)
|
||||
return pluginFileProvider.GetDirectoryContents(subpath);
|
||||
return this.webRootFileProvider.GetDirectoryContents(subpath);
|
||||
}
|
||||
|
||||
public IFileInfo GetFileInfo(string subpath)
|
||||
{
|
||||
var pluginFileProvider = GetPluginFileProvider(subpath);
|
||||
if (pluginFileProvider != null)
|
||||
return pluginFileProvider.GetFileInfo(subpath);
|
||||
return this.webRootFileProvider.GetFileInfo(subpath);
|
||||
}
|
||||
|
||||
public IChangeToken Watch(string filter)
|
||||
{
|
||||
return this.webRootFileProvider.Watch(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Vendor/Prise.Mvc/DefaultStaticPluginCacheAccessor.cs
vendored
Normal file
9
Vendor/Prise.Mvc/DefaultStaticPluginCacheAccessor.cs
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
using Prise.Caching;
|
||||
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public static class DefaultStaticPluginCacheAccessor
|
||||
{
|
||||
public static IPluginCache CurrentCache { get; internal set; }
|
||||
}
|
||||
}
|
||||
23
Vendor/Prise.Mvc/DefaultStaticPluginCacheAccessorBootstrapper.cs
vendored
Normal file
23
Vendor/Prise.Mvc/DefaultStaticPluginCacheAccessorBootstrapper.cs
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using Prise.Caching;
|
||||
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public class DefaultStaticPluginCacheAccessorBootstrapper : IPluginCacheAccessorBootstrapper
|
||||
{
|
||||
protected bool isBootstrapped;
|
||||
public DefaultStaticPluginCacheAccessorBootstrapper(IPluginCache cache)
|
||||
{
|
||||
if (this.isBootstrapped)
|
||||
throw new NotSupportedException($"IPluginCache was already bootstrapped");
|
||||
|
||||
this.SetCurrentCache(cache);
|
||||
this.isBootstrapped = true;
|
||||
}
|
||||
|
||||
public void SetCurrentCache(IPluginCache cache)
|
||||
{
|
||||
DefaultStaticPluginCacheAccessor.CurrentCache = cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
Vendor/Prise.Mvc/IMvcPluginLoader.cs
vendored
Normal file
14
Vendor/Prise.Mvc/IMvcPluginLoader.cs
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public interface IMvcPluginLoader
|
||||
{
|
||||
Task<IEnumerable<AssemblyScanResult>> FindPlugins<T>(string pathToPlugins);
|
||||
Task<IAssemblyShim> LoadPluginAssembly<T>(AssemblyScanResult plugin, Action<PluginLoadContext> configure = null);
|
||||
Task UnloadPluginAssembly<T>(AssemblyScanResult plugin);
|
||||
}
|
||||
}
|
||||
9
Vendor/Prise.Mvc/IPluginCacheAccessorBootstrapper.cs
vendored
Normal file
9
Vendor/Prise.Mvc/IPluginCacheAccessorBootstrapper.cs
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
using Prise.Caching;
|
||||
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public interface IPluginCacheAccessorBootstrapper
|
||||
{
|
||||
void SetCurrentCache(IPluginCache cache);
|
||||
}
|
||||
}
|
||||
7
Vendor/Prise.Mvc/IPriseMvcActionDescriptorChangeProvider.cs
vendored
Normal file
7
Vendor/Prise.Mvc/IPriseMvcActionDescriptorChangeProvider.cs
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public interface IPriseMvcActionDescriptorChangeProvider
|
||||
{
|
||||
void TriggerPluginChanged();
|
||||
}
|
||||
}
|
||||
15
Vendor/Prise.Mvc/PluginAssemblyPart.cs
vendored
Normal file
15
Vendor/Prise.Mvc/PluginAssemblyPart.cs
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public class PluginAssemblyPart : AssemblyPart, ICompilationReferencesProvider
|
||||
{
|
||||
public PluginAssemblyPart(Assembly assembly) : base(assembly) { }
|
||||
|
||||
// This solves the NullRef bug for in-memory assemblies from Prise
|
||||
IEnumerable<string> ICompilationReferencesProvider.GetReferencePaths() => Array.Empty<string>();
|
||||
}
|
||||
}
|
||||
98
Vendor/Prise.Mvc/Prise.Mvc.csproj
vendored
Normal file
98
Vendor/Prise.Mvc/Prise.Mvc.csproj
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!-- <TargetFrameworks>netcoreapp2.1;</TargetFrameworks> -->
|
||||
<TargetFrameworks>netcoreapp2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
|
||||
<Title>Prise.Mvc</Title>
|
||||
<PackageId>Prise.Mvc</PackageId>
|
||||
<PackageDescription>Adds ASP.NET MVC features to an existing Prise setup!</PackageDescription>
|
||||
<Authors>Maarten Merken</Authors>
|
||||
<Company>MRKN</Company>
|
||||
<PackageTags>mvc;controllers;plugin</PackageTags>
|
||||
<PackageLicenseUrl>https://raw.githubusercontent.com/merken/Prise/master/LICENSE</PackageLicenseUrl>
|
||||
<PackageProjectUrl>https://github.com/merken/Prise</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/merken/Prise.git</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target DependsOnTargets="ResolveReferences" Name="CopyProjectReferencesToPackage">
|
||||
<ItemGroup>
|
||||
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
|
||||
<DefineConstants>NETCORE2_1</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Constants (NETCORE2_1, NETCORE3_1) are one or the other, if multiple constants are required, prefix it with $(DefineConstants);-->
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
|
||||
<DefineConstants>NETCORE3_1</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../icon.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath></PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor" Version="2.1.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="3.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Prise.Plugin\Prise.Plugin.csproj">
|
||||
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
|
||||
<IncludeAssets>Prise.Plugin.dll</IncludeAssets>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Prise.Proxy\Prise.Proxy.csproj">
|
||||
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
|
||||
<IncludeAssets>Prise.Proxy.dll</IncludeAssets>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Prise\Prise.csproj">
|
||||
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
|
||||
<IncludeAssets>Prise.dll</IncludeAssets>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
118
Vendor/Prise.Mvc/PriseMvcExtensions.cs
vendored
Normal file
118
Vendor/Prise.Mvc/PriseMvcExtensions.cs
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
#if !NETCORE2_1
|
||||
using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Prise.DependencyInjection;
|
||||
|
||||
using Prise.Activation;
|
||||
using Prise.AssemblyLoading;
|
||||
using Prise.AssemblyScanning;
|
||||
using Prise.Proxy;
|
||||
using Prise.Caching;
|
||||
|
||||
namespace Prise.Mvc
|
||||
{
|
||||
public static class PriseMvcExtensions
|
||||
{
|
||||
internal static PluginLoadContext AddMvcTypes(this PluginLoadContext loadContext)
|
||||
{
|
||||
return loadContext.AddHostTypes(new[] { typeof(ControllerBase) });
|
||||
}
|
||||
|
||||
internal static PluginLoadContext AddMvcRazorTypes(this PluginLoadContext loadContext)
|
||||
{
|
||||
return loadContext.AddHostTypes(new[] { typeof(ControllerBase), typeof(ITempDataDictionaryFactory) });
|
||||
}
|
||||
|
||||
public static IServiceCollection AddCorePriseServices(this IServiceCollection services)
|
||||
{
|
||||
return services
|
||||
.AddFactory<IAssemblyScanner>(DefaultFactories.DefaultAssemblyScanner, ServiceLifetime.Scoped)
|
||||
.AddFactory<IPluginTypeSelector>(DefaultFactories.DefaultPluginTypeSelector, ServiceLifetime.Scoped)
|
||||
.AddFactory<IParameterConverter>(DefaultFactories.DefaultParameterConverter, ServiceLifetime.Scoped)
|
||||
.AddFactory<IResultConverter>(DefaultFactories.DefaultResultConverter, ServiceLifetime.Scoped)
|
||||
.AddFactory<IPluginActivationContextProvider>(DefaultFactories.DefaultPluginActivationContextProvider, ServiceLifetime.Scoped)
|
||||
.AddFactory<IRemotePluginActivator>(DefaultFactories.DefaultRemotePluginActivator, ServiceLifetime.Scoped)
|
||||
.AddFactory<IPluginProxyCreator>(DefaultFactories.DefaultPluginProxyCreator, ServiceLifetime.Scoped)
|
||||
.AddFactory<IAssemblyLoader>(DefaultFactories.DefaultAssemblyLoader, ServiceLifetime.Singleton);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does all of the plumbing to load API Controllers as Prise Plugins.
|
||||
/// Limitiations:
|
||||
/// - No DispatchProxy can be used, backwards compatability is compromised (DispatchProxy requires an interface as base class, not ControllerBase)
|
||||
/// - Plugin Cache is set to Singleton because we cannot unload assemblies, this would disable the controller routing (and result in 404)
|
||||
/// - Assembly unloading is disabled, memory leaks can occur
|
||||
/// </summary>
|
||||
/// <returns>A fully configured Prise setup that allows you to load plugins via the IMvcPluginLoader</returns>
|
||||
public static IServiceCollection AddPriseMvc(this IServiceCollection services)
|
||||
{
|
||||
|
||||
return services
|
||||
.AddCorePriseServices()
|
||||
.AddSingleton<IPluginCache, DefaultScopedPluginCache>()
|
||||
.AddScoped<IMvcPluginLoader, DefaultMvcPluginLoader>()
|
||||
.ConfigureMvcServices()
|
||||
;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does all of the plumbing to load API Controllers and RAZOR Controllers as Prise Plugins.
|
||||
/// Limitiations:
|
||||
/// - No DispatchProxy can be used, backwards compatability is compromised (DispatchProxy requires an interface as base class, not ControllerBase)
|
||||
/// - Plugin Cache is set to Singleton because we cannot unload assemblies, this would disable the controller routing (and result in 404)
|
||||
/// - Assembly unloading is disabled, memory leaks can occur
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="builder"></param>
|
||||
/// <param name="webRootPath">By default, this should be the IWebHostEnvironment.WebRootPaht or IHostingEnvironment.WebRootPath</param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddPriseRazorPlugins(this IServiceCollection services, string webRootPath, string pathToPlugins)
|
||||
{
|
||||
return services
|
||||
.AddCorePriseServices()
|
||||
.AddSingleton<IPluginCache, DefaultScopedPluginCache>()
|
||||
.AddScoped<IMvcPluginLoader, DefaultMvcRazorPluginLoader>()
|
||||
.ConfigureMvcServices()
|
||||
.ConfigureRazorServices(webRootPath, pathToPlugins)
|
||||
;
|
||||
}
|
||||
|
||||
private static IServiceCollection ConfigureMvcServices(this IServiceCollection services)
|
||||
{
|
||||
var actionDescriptorChangeProvider = new DefaultPriseMvcActionDescriptorChangeProvider();
|
||||
// Registers the change provider
|
||||
return services
|
||||
.AddSingleton<IPriseMvcActionDescriptorChangeProvider>(actionDescriptorChangeProvider)
|
||||
.AddSingleton<IActionDescriptorChangeProvider>(actionDescriptorChangeProvider)
|
||||
// Registers the activator for controllers from plugin assemblies
|
||||
.Replace(ServiceDescriptor.Transient<IControllerActivator, DefaultPriseMvcControllerActivator>());
|
||||
}
|
||||
|
||||
private static IServiceCollection ConfigureRazorServices(this IServiceCollection services, string webRootPath, string pathToPlugins)
|
||||
{
|
||||
return services
|
||||
#if NETCORE2_1
|
||||
.Configure<RazorViewEngineOptions>(options =>
|
||||
{
|
||||
options.FileProviders.Add(new DefaultPrisePluginViewsAssemblyFileProvider(webRootPath, pathToPlugins));
|
||||
})
|
||||
#else
|
||||
.Configure<MvcRazorRuntimeCompilationOptions>(options =>
|
||||
{
|
||||
options.FileProviders.Add(new DefaultPrisePluginViewsAssemblyFileProvider(webRootPath, pathToPlugins));
|
||||
})
|
||||
#endif
|
||||
// Registers the static Plugin Cache Accessor
|
||||
.AddSingleton<IPluginCacheAccessorBootstrapper, DefaultStaticPluginCacheAccessorBootstrapper>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
30
Vendor/Prise.Plugin/BootstrapperServiceAttribute.cs
vendored
Normal file
30
Vendor/Prise.Plugin/BootstrapperServiceAttribute.cs
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace Prise.Plugin
|
||||
{
|
||||
[System.AttributeUsage(System.AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class BootstrapperServiceAttribute : System.Attribute
|
||||
{
|
||||
Type serviceType;
|
||||
public Type ServiceType
|
||||
{
|
||||
get { return this.serviceType; }
|
||||
set { this.serviceType = value; }
|
||||
}
|
||||
|
||||
Type bridgeType;
|
||||
[Obsolete("Usage of a BridgeType is obsolete, please use ProxyType instead. Existing plugins will continue to function as normal.", false)]
|
||||
public Type BridgeType
|
||||
{
|
||||
get { return this.bridgeType; }
|
||||
set { this.bridgeType = value; }
|
||||
}
|
||||
|
||||
Type proxyType;
|
||||
public Type ProxyType
|
||||
{
|
||||
get { return this.proxyType; }
|
||||
set { this.proxyType = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Vendor/Prise.Plugin/IPluginBootstrapper.cs
vendored
Normal file
9
Vendor/Prise.Plugin/IPluginBootstrapper.cs
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Prise.Plugin
|
||||
{
|
||||
public interface IPluginBootstrapper
|
||||
{
|
||||
IServiceCollection Bootstrap(IServiceCollection services);
|
||||
}
|
||||
}
|
||||
5
Vendor/Prise.Plugin/PluginActivatedAttribute.cs
vendored
Normal file
5
Vendor/Prise.Plugin/PluginActivatedAttribute.cs
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
namespace Prise.Plugin
|
||||
{
|
||||
[System.AttributeUsage(System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class PluginActivatedAttribute : System.Attribute { }
|
||||
}
|
||||
15
Vendor/Prise.Plugin/PluginAttribute.cs
vendored
Normal file
15
Vendor/Prise.Plugin/PluginAttribute.cs
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Prise.Plugin
|
||||
{
|
||||
[System.AttributeUsage(System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class PluginAttribute : System.Attribute
|
||||
{
|
||||
Type pluginType;
|
||||
public Type PluginType
|
||||
{
|
||||
get { return this.pluginType; }
|
||||
set { this.pluginType = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Vendor/Prise.Plugin/PluginBootstrapperAttribute.cs
vendored
Normal file
15
Vendor/Prise.Plugin/PluginBootstrapperAttribute.cs
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Prise.Plugin
|
||||
{
|
||||
[System.AttributeUsage(System.AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class PluginBootstrapperAttribute : System.Attribute
|
||||
{
|
||||
Type pluginType;
|
||||
public Type PluginType
|
||||
{
|
||||
get { return this.pluginType; }
|
||||
set { this.pluginType = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
8
Vendor/Prise.Plugin/PluginFactoryAttribute.cs
vendored
Normal file
8
Vendor/Prise.Plugin/PluginFactoryAttribute.cs
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace Prise.Plugin
|
||||
{
|
||||
[Obsolete("Usage of a PluginFactory is obsolete, please use field injection instead. Existing plugins will continue to function as normal.", false)]
|
||||
[System.AttributeUsage(System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class PluginFactoryAttribute : System.Attribute { }
|
||||
}
|
||||
37
Vendor/Prise.Plugin/PluginServiceAttribute.cs
vendored
Normal file
37
Vendor/Prise.Plugin/PluginServiceAttribute.cs
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
|
||||
namespace Prise.Plugin
|
||||
{
|
||||
[System.AttributeUsage(System.AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class PluginServiceAttribute : System.Attribute
|
||||
{
|
||||
Type serviceType;
|
||||
public Type ServiceType
|
||||
{
|
||||
get { return this.serviceType; }
|
||||
set { this.serviceType = value; }
|
||||
}
|
||||
|
||||
ProvidedBy providedBy;
|
||||
public ProvidedBy ProvidedBy
|
||||
{
|
||||
get { return this.providedBy; }
|
||||
set { this.providedBy = value; }
|
||||
}
|
||||
|
||||
Type bridgeType;
|
||||
[Obsolete("Usage of a BridgeType is obsolete, please use ProxyType instead. Existing plugins will continue to function as normal.", false)]
|
||||
public Type BridgeType
|
||||
{
|
||||
get { return this.bridgeType; }
|
||||
set { this.bridgeType = value; }
|
||||
}
|
||||
|
||||
Type proxyType;
|
||||
public Type ProxyType
|
||||
{
|
||||
get { return this.proxyType; }
|
||||
set { this.proxyType = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Vendor/Prise.Plugin/Prise.Plugin.csproj
vendored
Normal file
32
Vendor/Prise.Plugin/Prise.Plugin.csproj
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Title>Prise.Plugin</Title>
|
||||
<PackageId>Prise.Plugin</PackageId>
|
||||
<PackageDescription>Prise, A .NET Plugin Framework!</PackageDescription>
|
||||
<Authors>Maarten Merken</Authors>
|
||||
<Company>MRKN</Company>
|
||||
<PackageTags>plugin;framework;prise;decoupling;assembly;dispatchproxy;proxy</PackageTags>
|
||||
<PackageLicenseUrl>https://raw.githubusercontent.com/merken/Prise/master/LICENSE</PackageLicenseUrl>
|
||||
<PackageProjectUrl>https://github.com/merken/Prise</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/merken/Prise.git</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<LangVersion>default</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../icon.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath></PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
8
Vendor/Prise.Plugin/ProvidedBy.cs
vendored
Normal file
8
Vendor/Prise.Plugin/ProvidedBy.cs
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Prise.Plugin
|
||||
{
|
||||
public enum ProvidedBy
|
||||
{
|
||||
Plugin = 0,
|
||||
Host
|
||||
}
|
||||
}
|
||||
9
Vendor/Prise.Proxy/Infrastructure/IParameterConverter.cs
vendored
Normal file
9
Vendor/Prise.Proxy/Infrastructure/IParameterConverter.cs
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
public interface IParameterConverter : IDisposable
|
||||
{
|
||||
object ConvertToRemoteType(Type localType, object value);
|
||||
}
|
||||
}
|
||||
28
Vendor/Prise.Proxy/Infrastructure/IResultConverter.cs
vendored
Normal file
28
Vendor/Prise.Proxy/Infrastructure/IResultConverter.cs
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
public interface IResultConverter : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// This method should convert a remote value into a local one.
|
||||
/// This method will be called in case the remote type is not a Task.
|
||||
/// </summary>
|
||||
/// <param name="localType">The return type of the host</param>
|
||||
/// <param name="remoteType">The return type of the remote</param>
|
||||
/// <param name="value">The value from the remote, this can be different from the remoteType if the remote uses an old contract, in this case, convert to correct type.</param>
|
||||
/// <returns>A converted local instance</returns>
|
||||
object ConvertToLocalType(Type localType, Type remoteType, object value);
|
||||
|
||||
/// <summary>
|
||||
/// This method should convert a remote value into a local one.
|
||||
/// This method will be called in case the remote type is a Task.
|
||||
/// </summary>
|
||||
/// <param name="localType">The return type of the host, Task<localType></param>
|
||||
/// <param name="remoteType">The return type of the remote, Task<remoteType></param>
|
||||
/// <param name="task">The Task that holds the value from the remote, this can be different from the remoteType if the remote uses an old contract, in this case, convert to correct type.</param>
|
||||
/// <returns>A Task containing the local type with the remote value</returns>
|
||||
object ConvertToLocalTypeAsync(Type localType, Type remoteType, Task task);
|
||||
}
|
||||
}
|
||||
16
Vendor/Prise.Proxy/Method.cs
vendored
Normal file
16
Vendor/Prise.Proxy/Method.cs
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
public struct Method
|
||||
{
|
||||
public string Name { get; }
|
||||
public Type? ReturnType { get; }
|
||||
|
||||
public Method(string name, Type? returnType = null)
|
||||
{
|
||||
this.Name = name;
|
||||
this.ReturnType = returnType;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Vendor/Prise.Proxy/MethodFindingStrategy.cs
vendored
Normal file
13
Vendor/Prise.Proxy/MethodFindingStrategy.cs
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
[Flags]
|
||||
public enum MethodFindingStrategy
|
||||
{
|
||||
MethodNameMustMatch,
|
||||
MethodReturnTypeMustMatch,
|
||||
ParameterCountMustMatch,
|
||||
ParameterTypeMustMatch
|
||||
}
|
||||
}
|
||||
16
Vendor/Prise.Proxy/Parameter.cs
vendored
Normal file
16
Vendor/Prise.Proxy/Parameter.cs
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
public struct Parameter
|
||||
{
|
||||
public string Name { get; }
|
||||
public Type? Type { get; }
|
||||
|
||||
public Parameter(string name, Type? type = null)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Vendor/Prise.Proxy/PassthroughParameterConverter.cs
vendored
Normal file
29
Vendor/Prise.Proxy/PassthroughParameterConverter.cs
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
public class PassthroughParameterConverter : IParameterConverter
|
||||
{
|
||||
protected bool disposed = false;
|
||||
|
||||
public object ConvertToRemoteType(Type localType, object value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!this.disposed && disposing)
|
||||
{
|
||||
// Nothing to do here
|
||||
}
|
||||
this.disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Vendor/Prise.Proxy/PassthroughResultConverter.cs
vendored
Normal file
12
Vendor/Prise.Proxy/PassthroughResultConverter.cs
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
public class PassthroughResultConverter : ResultConverter
|
||||
{
|
||||
public override object Deserialize(Type localType, Type remoteType, object value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Vendor/Prise.Proxy/Prise.Proxy.csproj
vendored
Normal file
42
Vendor/Prise.Proxy/Prise.Proxy.csproj
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Title>Prise.Proxy</Title>
|
||||
<PackageId>Prise.Proxy</PackageId>
|
||||
<PackageDescription>Prise, A .NET Plugin Framework!</PackageDescription>
|
||||
<Authors>Maarten Merken</Authors>
|
||||
<Company>MRKN</Company>
|
||||
<PackageTags>plugin;framework;prise;decoupling;assembly;dispatchproxy;proxy</PackageTags>
|
||||
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>default</LangVersion>
|
||||
<PackageLicenseUrl>https://raw.githubusercontent.com/merken/Prise/master/LICENSE</PackageLicenseUrl>
|
||||
<PackageProjectUrl>https://github.com/merken/Prise</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/merken/Prise.git</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target DependsOnTargets="ResolveReferences" Name="CopyProjectReferencesToPackage">
|
||||
<ItemGroup>
|
||||
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../icon.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath></PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
254
Vendor/Prise.Proxy/PriseProxy.cs
vendored
Normal file
254
Vendor/Prise.Proxy/PriseProxy.cs
vendored
Normal file
@ -0,0 +1,254 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
/// <summary>
|
||||
/// This is the PriseProxy static class that encapsulates most of the boilerplate static methods in order to interact with the remote object (Plugin)
|
||||
/// TODO
|
||||
/// - Generic Methods (Task<string> Do<T>(T stuff))
|
||||
/// - Events (not sure if this is ever possible)
|
||||
/// </summary>
|
||||
public static class PriseProxy
|
||||
{
|
||||
public static object Invoke(object remoteObject, MethodInfo targetMethod, object[] args)
|
||||
=> Invoke(remoteObject, targetMethod, args, new PassthroughParameterConverter(),
|
||||
new PassthroughResultConverter());
|
||||
|
||||
public static object Invoke(object remoteObject, MethodInfo targetMethod, object[] args,
|
||||
IParameterConverter parameterConverter, IResultConverter resultConverter)
|
||||
{
|
||||
try
|
||||
{
|
||||
var localType = targetMethod.ReturnType;
|
||||
var remoteMethod = FindMethodOnObject(targetMethod, remoteObject);
|
||||
if (remoteMethod == null)
|
||||
throw new PriseProxyException(
|
||||
$"Target method {targetMethod.Name} is not found on Proxy Type {remoteObject.GetType().Name}.");
|
||||
|
||||
object result = null;
|
||||
if (remoteMethod.IsGenericMethod)
|
||||
{
|
||||
var generic = remoteMethod.MakeGenericMethod(targetMethod.GetGenericArguments());
|
||||
result = generic.Invoke(remoteObject, SerializeParameters(parameterConverter, remoteMethod, args));
|
||||
}
|
||||
else
|
||||
result = remoteMethod.Invoke(remoteObject,
|
||||
SerializeParameters(parameterConverter, remoteMethod, args));
|
||||
|
||||
var remoteType = remoteMethod.ReturnType;
|
||||
if (remoteType.BaseType == typeof(System.Threading.Tasks.Task))
|
||||
return resultConverter.ConvertToLocalTypeAsync(localType, remoteType,
|
||||
result as System.Threading.Tasks.Task);
|
||||
|
||||
if (remoteType == typeof(System.Threading.Tasks.Task))
|
||||
return resultConverter.ConvertToLocalTypeAsync(localType, remoteType,
|
||||
result as System.Threading.Tasks.Task);
|
||||
|
||||
|
||||
if (remoteType == typeof(void))
|
||||
return null;
|
||||
|
||||
return resultConverter.ConvertToLocalType(localType, remoteType, result);
|
||||
}
|
||||
catch (Exception ex) when (ex is TargetInvocationException)
|
||||
{
|
||||
throw ex.InnerException ?? ex;
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodInfo FindMethodOnObject(MethodInfo callingMethod, object targetObject)
|
||||
=> FindMethodOnObject(
|
||||
targetObject,
|
||||
new Method(callingMethod.Name, callingMethod.ReturnType),
|
||||
callingMethod.GetParameters().Select(p => new Parameter(p.Name, p.ParameterType)).ToArray(),
|
||||
throwOnError: true)!; // Throws error when null
|
||||
|
||||
public static MethodInfo? TryFindMethodOnObject(MethodInfo callingMethod, object targetObject)
|
||||
=> FindMethodOnObject(
|
||||
targetObject,
|
||||
new Method(callingMethod.Name, callingMethod.ReturnType),
|
||||
callingMethod.GetParameters().Select(p => new Parameter(p.Name, p.ParameterType)).ToArray());
|
||||
|
||||
public static MethodInfo? FindMethodOnObject(
|
||||
object targetObject,
|
||||
Method method,
|
||||
Parameter[] parameters,
|
||||
MethodFindingStrategy strategy = MethodFindingStrategy.MethodNameMustMatch |
|
||||
MethodFindingStrategy.MethodReturnTypeMustMatch |
|
||||
MethodFindingStrategy.ParameterCountMustMatch |
|
||||
MethodFindingStrategy.ParameterTypeMustMatch,
|
||||
bool throwOnError = false)
|
||||
{
|
||||
bool isNameCorrect(MethodInfo targetMethod) => targetMethod.Name == method.Name;
|
||||
|
||||
// First, find by method name
|
||||
var targetMethods = targetObject.GetType().GetMethods().AsEnumerable();
|
||||
if (strategy.HasFlag(MethodFindingStrategy.MethodNameMustMatch))
|
||||
targetMethods = targetMethods.Where(isNameCorrect);
|
||||
|
||||
if (!targetMethods.Any())
|
||||
if (throwOnError)
|
||||
throw new PriseProxyException(
|
||||
$"Target method {method.Name} is not found on Proxy Type {targetObject.GetType().Name}.");
|
||||
else
|
||||
return null;
|
||||
|
||||
if (targetMethods.Count() == 1)
|
||||
return targetMethods.First();
|
||||
|
||||
bool isReturnTypeCorrect(MethodInfo targetMethod) => targetMethod.ReturnType == method.ReturnType;
|
||||
|
||||
if (strategy.HasFlag(MethodFindingStrategy.MethodReturnTypeMustMatch))
|
||||
// Second, find by method name AND return type
|
||||
targetMethods = targetMethods.Where(isReturnTypeCorrect);
|
||||
|
||||
if (targetMethods.Count() == 1)
|
||||
return targetMethods.First();
|
||||
|
||||
bool isParameterCountCorrect(MethodInfo targetMethod) =>
|
||||
targetMethod.GetParameters().Count() == parameters.Length;
|
||||
|
||||
bool doAllParametersMatch(MethodInfo targetMethod)
|
||||
{
|
||||
var callingMethodParameters = parameters;
|
||||
var targetMethodParameters = targetMethod.GetParameters();
|
||||
for (var index = 0; index < callingMethodParameters.Count(); index++)
|
||||
{
|
||||
var callingParam = callingMethodParameters[index];
|
||||
if (callingParam.Type is null)
|
||||
throw new PriseProxyException(
|
||||
$"When using {nameof(MethodFindingStrategy.ParameterTypeMustMatch)}, Parameter.Type must be provided");
|
||||
var targetParam = targetMethodParameters[index];
|
||||
if (!(targetParam.Name == callingParam.Name &&
|
||||
targetParam.ParameterType.Name == callingParam.Type.Name))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
targetMethods = targetMethods.Where(targetMethod =>
|
||||
(!strategy.HasFlag(MethodFindingStrategy.ParameterCountMustMatch) ||
|
||||
isParameterCountCorrect(targetMethod)) &&
|
||||
(!strategy.HasFlag(MethodFindingStrategy.ParameterTypeMustMatch) || doAllParametersMatch(targetMethod))
|
||||
);
|
||||
|
||||
if (!targetMethods.Any())
|
||||
if (throwOnError)
|
||||
throw new PriseProxyException(
|
||||
$"Target method {method.Name} is not found on Proxy Type {targetObject.GetType().Name}.");
|
||||
else
|
||||
return null;
|
||||
|
||||
if (targetMethods.Count() > 1)
|
||||
if (throwOnError)
|
||||
throw new PriseProxyException(
|
||||
$"Target method {method.Name} could not be determined on object {targetObject.GetType().Name}.");
|
||||
else
|
||||
return null;
|
||||
|
||||
return targetMethods.First();
|
||||
}
|
||||
|
||||
internal static object[] SerializeParameters(IParameterConverter parameterConverter, MethodInfo targetMethod,
|
||||
object[] args)
|
||||
{
|
||||
var parameters = targetMethod.GetParameters();
|
||||
var results = new List<object>();
|
||||
|
||||
for (var index = 0; index < parameters.Count(); index++)
|
||||
{
|
||||
var parameter = parameters[index];
|
||||
var parameterValue = args[index];
|
||||
|
||||
if (parameter.ParameterType.BaseType == typeof(System.MulticastDelegate))
|
||||
{
|
||||
if (parameter.ParameterType.GenericTypeArguments.Any(g => g != typeof(EventArgs)))
|
||||
throw new PriseProxyException($"Custom EventArgs are not supported in Prise");
|
||||
|
||||
results.Add(parameterValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
object result = null;
|
||||
if (parameter.ParameterType.IsGenericParameter)
|
||||
{
|
||||
var runtimeType = parameterValue.GetType();
|
||||
result = parameterConverter.ConvertToRemoteType(runtimeType, parameterValue);
|
||||
}
|
||||
else
|
||||
result = parameterConverter.ConvertToRemoteType(parameter.ParameterType, parameterValue);
|
||||
|
||||
results.Add(result);
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is the PriseProxy wrapper class that will acts as the communication layer between the Host and the Plugin.
|
||||
/// Every call from the Host to the Plugin will go through here.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Plugin type</typeparam>
|
||||
public class PriseProxy<T> : DispatchProxy, IDisposable
|
||||
{
|
||||
private IParameterConverter parameterConverter;
|
||||
private IResultConverter resultConverter;
|
||||
private object remoteObject;
|
||||
protected bool disposed = false;
|
||||
|
||||
protected override object Invoke(MethodInfo targetMethod, object[] args) => PriseProxy.Invoke(this.remoteObject,
|
||||
targetMethod, args, this.parameterConverter, this.resultConverter);
|
||||
|
||||
public static object Create() => Create<T, PriseProxy<T>>();
|
||||
|
||||
internal PriseProxy<T> SetRemoteObject(object remoteObject)
|
||||
{
|
||||
if (remoteObject == null)
|
||||
throw new PriseProxyException($"Remote object for Proxy<{typeof(T).Name}> was null");
|
||||
|
||||
this.remoteObject = remoteObject;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal PriseProxy<T> SetParameterConverter(IParameterConverter parameterConverter)
|
||||
{
|
||||
if (parameterConverter == null)
|
||||
throw new PriseProxyException($"IParameterConverter for Proxy<{typeof(T).Name}> was null");
|
||||
|
||||
this.parameterConverter = parameterConverter;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal PriseProxy<T> SetResultConverter(IResultConverter resultConverter)
|
||||
{
|
||||
if (resultConverter == null)
|
||||
throw new PriseProxyException($"IResultConverter for Proxy<{typeof(T).Name}> was null");
|
||||
|
||||
this.resultConverter = resultConverter;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!this.disposed && disposing)
|
||||
{
|
||||
this.parameterConverter = null;
|
||||
this.resultConverter = null;
|
||||
this.remoteObject = null;
|
||||
}
|
||||
|
||||
this.disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Vendor/Prise.Proxy/PriseProxyException.cs
vendored
Normal file
25
Vendor/Prise.Proxy/PriseProxyException.cs
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
[Serializable]
|
||||
public class PriseProxyException : Exception
|
||||
{
|
||||
public PriseProxyException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public PriseProxyException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
public PriseProxyException()
|
||||
{
|
||||
}
|
||||
|
||||
protected PriseProxyException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Vendor/Prise.Proxy/ProxyCreator.cs
vendored
Normal file
32
Vendor/Prise.Proxy/ProxyCreator.cs
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
public static class ProxyCreator
|
||||
{
|
||||
public static object CreateGenericProxy(Type proxyType, object remoteObject)
|
||||
{
|
||||
return typeof(ProxyCreator).GetMethod(nameof(ProxyCreator.CreateProxy)).MakeGenericMethod(proxyType).Invoke(null, new[] { remoteObject, null, null });
|
||||
}
|
||||
|
||||
public static TProxyType CreateProxy<TProxyType>(
|
||||
object remoteObject,
|
||||
IParameterConverter parameterConverter = null,
|
||||
IResultConverter resultConverter = null)
|
||||
{
|
||||
if (parameterConverter == null)
|
||||
parameterConverter = new PassthroughParameterConverter();
|
||||
|
||||
if (resultConverter == null)
|
||||
resultConverter = new PassthroughResultConverter();
|
||||
|
||||
var proxy = PriseProxy<TProxyType>.Create();
|
||||
((PriseProxy<TProxyType>)proxy)
|
||||
.SetRemoteObject(remoteObject)
|
||||
.SetParameterConverter(parameterConverter)
|
||||
.SetResultConverter(resultConverter);
|
||||
|
||||
return (TProxyType)proxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
63
Vendor/Prise.Proxy/ResultConverter.cs
vendored
Normal file
63
Vendor/Prise.Proxy/ResultConverter.cs
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
public abstract class ResultConverter : IResultConverter
|
||||
{
|
||||
private bool disposed = false;
|
||||
public abstract object Deserialize(Type localType, Type remoteType, object value);
|
||||
public object ConvertToLocalType(Type localType, Type remoteType, object value)
|
||||
{
|
||||
return Deserialize(localType, remoteType, value);
|
||||
}
|
||||
|
||||
public object ConvertToLocalTypeAsync(Type localType, Type remoteType, Task task)
|
||||
{
|
||||
var taskResultType = localType.GenericTypeArguments != null && localType.GenericTypeArguments.Any() ? localType.GenericTypeArguments[0] : null;
|
||||
var taskCompletionSource = new TaskCompletionSource(taskResultType ?? typeof(object));
|
||||
|
||||
task.ContinueWith(t =>
|
||||
{
|
||||
if (t.IsCanceled)
|
||||
taskCompletionSource.TrySetCanceled();
|
||||
else if (t.IsFaulted)
|
||||
taskCompletionSource.TrySetException(t.Exception);
|
||||
else if (taskResultType == null)
|
||||
taskCompletionSource.TrySetResult(null);
|
||||
else
|
||||
{
|
||||
var property = t.GetType()
|
||||
.GetTypeInfo()
|
||||
.GetProperties()
|
||||
.FirstOrDefault(p => p.Name == "Result");
|
||||
|
||||
if (property != null)
|
||||
{
|
||||
var value = Deserialize(localType, remoteType, property.GetValue(task));
|
||||
taskCompletionSource.TrySetResult(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!this.disposed && disposing)
|
||||
{
|
||||
// Nothing to do here
|
||||
}
|
||||
this.disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Vendor/Prise.Proxy/TaskCompletionSource.cs
vendored
Normal file
56
Vendor/Prise.Proxy/TaskCompletionSource.cs
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a non-generic TaskCompletionSource<T>
|
||||
/// It allows you to set a type via its constructor
|
||||
/// </summary>
|
||||
public class TaskCompletionSource
|
||||
{
|
||||
private readonly Type type;
|
||||
private readonly object taskCompletionSource;
|
||||
|
||||
public TaskCompletionSource(Type type)
|
||||
{
|
||||
this.type = type;
|
||||
this.taskCompletionSource = typeof(TaskCompletionSource<>)
|
||||
.MakeGenericType(type)
|
||||
.GetConstructors(BindingFlags.Public | BindingFlags.Instance)[0]
|
||||
.Invoke(null);
|
||||
}
|
||||
|
||||
public bool TrySetCanceled()
|
||||
{
|
||||
var trySetCanceled = taskCompletionSource.GetType().GetMethod("TrySetCanceled");
|
||||
return (bool)trySetCanceled.Invoke(this.taskCompletionSource, null);
|
||||
}
|
||||
|
||||
public bool TrySetException(Exception ex)
|
||||
{
|
||||
var trySetException = taskCompletionSource.GetType()
|
||||
.GetMethods()
|
||||
.First(m => m.Name == "TrySetException" && m.GetParameters().First().ParameterType == typeof(IEnumerable<Exception>));
|
||||
return (bool)trySetException.Invoke(this.taskCompletionSource, new[] { new[] { ex } });
|
||||
}
|
||||
|
||||
public bool TrySetResult(object result)
|
||||
{
|
||||
var trySetResult = taskCompletionSource.GetType().GetMethod("TrySetResult");
|
||||
return (bool)trySetResult.Invoke(this.taskCompletionSource, new[] { result });
|
||||
}
|
||||
|
||||
public Task Task
|
||||
{
|
||||
get
|
||||
{
|
||||
var taskProperty = taskCompletionSource.GetType().GetProperty("Task");
|
||||
return taskProperty.GetValue(this.taskCompletionSource) as Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Vendor/Prise.Proxy/runtime/DispatchProxy.cs
vendored
Normal file
38
Vendor/Prise.Proxy/runtime/DispatchProxy.cs
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
namespace System.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// DispatchProxy provides a mechanism for the instantiation of proxy objects and handling of
|
||||
/// their method dispatch.
|
||||
/// Original file: https://github.com/dotnet/runtime/blob/6072e4d3a7a2a1493f514cdf4be75a3d56580e84/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxy.cs
|
||||
/// </summary>
|
||||
public abstract class DispatchProxy
|
||||
{
|
||||
protected DispatchProxy()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whenever any method on the generated proxy type is called, this method
|
||||
/// will be invoked to dispatch control.
|
||||
/// </summary>
|
||||
/// <param name="targetMethod">The method the caller invoked</param>
|
||||
/// <param name="args">The arguments the caller passed to the method</param>
|
||||
/// <returns>The object to return to the caller, or <c>null</c> for void methods</returns>
|
||||
protected abstract object? Invoke(MethodInfo? targetMethod, object?[]? args);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an object instance that derives from class <typeparamref name="TProxy"/>
|
||||
/// and implements interface <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The interface the proxy should implement.</typeparam>
|
||||
/// <typeparam name="TProxy">The base class to use for the proxy class.</typeparam>
|
||||
/// <returns>An object instance that implements <typeparamref name="T"/>.</returns>
|
||||
/// <exception cref="System.ArgumentException"><typeparamref name="T"/> is a class,
|
||||
/// or <typeparamref name="TProxy"/> is sealed or does not have a parameterless constructor</exception>
|
||||
public static T Create<T, TProxy>()
|
||||
where TProxy : DispatchProxy
|
||||
{
|
||||
return (T)DispatchProxyGenerator.CreateProxyInstance(typeof(TProxy), typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
935
Vendor/Prise.Proxy/runtime/DispatchProxyGenerator.cs
vendored
Normal file
935
Vendor/Prise.Proxy/runtime/DispatchProxyGenerator.cs
vendored
Normal file
@ -0,0 +1,935 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// Original file: https://github.com/dotnet/runtime/blob/6072e4d3a7a2a1493f514cdf4be75a3d56580e84/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Reflection
|
||||
{
|
||||
// Helper class to handle the IL EMIT for the generation of proxies.
|
||||
// Much of this code was taken directly from the Silverlight proxy generation.
|
||||
// Differences between this and the Silverlight version are:
|
||||
// 1. This version is based on DispatchProxy from NET Native and CoreCLR, not RealProxy in Silverlight ServiceModel.
|
||||
// There are several notable differences between them.
|
||||
// 2. Both DispatchProxy and RealProxy permit the caller to ask for a proxy specifying a pair of types:
|
||||
// the interface type to implement, and a base type. But they behave slightly differently:
|
||||
// - RealProxy generates a proxy type that derives from Object and *implements" all the base type's
|
||||
// interfaces plus all the interface type's interfaces.
|
||||
// - DispatchProxy generates a proxy type that *derives* from the base type and implements all
|
||||
// the interface type's interfaces. This is true for both the CLR version in NET Native and this
|
||||
// version for CoreCLR.
|
||||
// 3. DispatchProxy and RealProxy use different type hierarchies for the generated proxies:
|
||||
// - RealProxy type hierarchy is:
|
||||
// proxyType : proxyBaseType : object
|
||||
// Presumably the 'proxyBaseType' in the middle is to allow it to implement the base type's interfaces
|
||||
// explicitly, preventing collision for same name methods on the base and interface types.
|
||||
// - DispatchProxy hierarchy is:
|
||||
// proxyType : baseType (where baseType : DispatchProxy)
|
||||
// The generated DispatchProxy proxy type does not need to generate implementation methods
|
||||
// for the base type's interfaces, because the base type already must have implemented them.
|
||||
// 4. RealProxy required a proxy instance to hold a backpointer to the RealProxy instance to mirror
|
||||
// the .NET Remoting design that required the proxy and RealProxy to be separate instances.
|
||||
// But the DispatchProxy design encourages the proxy type to *be* an DispatchProxy. Therefore,
|
||||
// the proxy's 'this' becomes the equivalent of RealProxy's backpointer to RealProxy, so we were
|
||||
// able to remove an extraneous field and ctor arg from the DispatchProxy proxies.
|
||||
//
|
||||
internal static class DispatchProxyGenerator
|
||||
{
|
||||
// Generated proxies have a private Action field that all generated methods
|
||||
// invoke. It is the first field in the class and the first ctor parameter.
|
||||
private const int InvokeActionFieldAndCtorParameterIndex = 0;
|
||||
|
||||
// Proxies are requested for a pair of types: base type and interface type.
|
||||
// The generated proxy will subclass the given base type and implement the interface type.
|
||||
// We maintain a cache keyed by 'base type' containing a dictionary keyed by interface type,
|
||||
// containing the generated proxy type for that pair. There are likely to be few (maybe only 1)
|
||||
// base type in use for many interface types.
|
||||
// Note: this differs from Silverlight's RealProxy implementation which keys strictly off the
|
||||
// interface type. But this does not allow the same interface type to be used with more than a
|
||||
// single base type. The implementation here permits multiple interface types to be used with
|
||||
// multiple base types, and the generated proxy types will be unique.
|
||||
// This cache of generated types grows unbounded, one element per unique T/ProxyT pair.
|
||||
// This approach is used to prevent regenerating identical proxy types for identical T/Proxy pairs,
|
||||
// which would ultimately be a more expensive leak.
|
||||
// Proxy instances are not cached. Their lifetime is entirely owned by the caller of DispatchProxy.Create.
|
||||
private static readonly Dictionary<Type, Dictionary<Type, Type>> s_baseTypeAndInterfaceToGeneratedProxyType = new Dictionary<Type, Dictionary<Type, Type>>();
|
||||
private static readonly ProxyAssembly s_proxyAssembly = new ProxyAssembly();
|
||||
private static readonly MethodInfo s_dispatchProxyInvokeMethod = typeof(DispatchProxy).GetTypeInfo().GetDeclaredMethod("Invoke")!;
|
||||
|
||||
// Returns a new instance of a proxy the derives from 'baseType' and implements 'interfaceType'
|
||||
internal static object CreateProxyInstance(Type baseType, Type interfaceType)
|
||||
{
|
||||
Debug.Assert(baseType != null);
|
||||
Debug.Assert(interfaceType != null);
|
||||
|
||||
Type proxiedType = GetProxyType(baseType!, interfaceType!);
|
||||
return Activator.CreateInstance(proxiedType, (Action<object[]>)DispatchProxyGenerator.Invoke)!;
|
||||
}
|
||||
|
||||
private static Type GetProxyType(Type baseType, Type interfaceType)
|
||||
{
|
||||
lock (s_baseTypeAndInterfaceToGeneratedProxyType)
|
||||
{
|
||||
if (!s_baseTypeAndInterfaceToGeneratedProxyType.TryGetValue(baseType, out Dictionary<Type, Type>? interfaceToProxy))
|
||||
{
|
||||
interfaceToProxy = new Dictionary<Type, Type>();
|
||||
s_baseTypeAndInterfaceToGeneratedProxyType[baseType] = interfaceToProxy;
|
||||
}
|
||||
|
||||
if (!interfaceToProxy.TryGetValue(interfaceType, out Type? generatedProxy))
|
||||
{
|
||||
generatedProxy = GenerateProxyType(baseType, interfaceType);
|
||||
interfaceToProxy[interfaceType] = generatedProxy;
|
||||
}
|
||||
|
||||
return generatedProxy;
|
||||
}
|
||||
}
|
||||
|
||||
// Unconditionally generates a new proxy type derived from 'baseType' and implements 'interfaceType'
|
||||
private static Type GenerateProxyType(Type baseType, Type interfaceType)
|
||||
{
|
||||
// Parameter validation is deferred until the point we need to create the proxy.
|
||||
// This prevents unnecessary overhead revalidating cached proxy types.
|
||||
TypeInfo baseTypeInfo = baseType.GetTypeInfo();
|
||||
|
||||
// The interface type must be an interface, not a class
|
||||
if (!interfaceType.GetTypeInfo().IsInterface)
|
||||
{
|
||||
// "T" is the generic parameter seen via the public contract
|
||||
throw new ArgumentException("SR.Format(SR.InterfaceType_Must_Be_Interface, interfaceType.FullName)", "T");
|
||||
}
|
||||
|
||||
// The base type cannot be sealed because the proxy needs to subclass it.
|
||||
if (baseTypeInfo.IsSealed)
|
||||
{
|
||||
// "TProxy" is the generic parameter seen via the public contract
|
||||
throw new ArgumentException("SR.Format(SR.BaseType_Cannot_Be_Sealed, baseTypeInfo.FullName)", "TProxy");
|
||||
}
|
||||
|
||||
// The base type cannot be abstract
|
||||
if (baseTypeInfo.IsAbstract)
|
||||
{
|
||||
throw new ArgumentException("SR.Format(SR.BaseType_Cannot_Be_Abstract, baseType.FullName)", "TProxy");
|
||||
}
|
||||
|
||||
// The base type must have a public default ctor
|
||||
if (!baseTypeInfo.DeclaredConstructors.Any(c => c.IsPublic && c.GetParameters().Length == 0))
|
||||
{
|
||||
throw new ArgumentException("SR.Format(SR.BaseType_Must_Have_Default_Ctor, baseType.FullName)", "TProxy");
|
||||
}
|
||||
|
||||
// Create a type that derives from 'baseType' provided by caller
|
||||
ProxyBuilder pb = s_proxyAssembly.CreateProxy("generatedProxy", baseType);
|
||||
|
||||
foreach (Type t in interfaceType.GetTypeInfo().ImplementedInterfaces)
|
||||
pb.AddInterfaceImpl(t);
|
||||
|
||||
pb.AddInterfaceImpl(interfaceType);
|
||||
|
||||
Type generatedProxyType = pb.CreateType();
|
||||
return generatedProxyType;
|
||||
}
|
||||
|
||||
// All generated proxy methods call this static helper method to dispatch.
|
||||
// Its job is to unpack the arguments and the 'this' instance and to dispatch directly
|
||||
// to the (abstract) DispatchProxy.Invoke() method.
|
||||
private static void Invoke(object?[] args)
|
||||
{
|
||||
PackedArgs packed = new PackedArgs(args);
|
||||
MethodBase method = s_proxyAssembly.ResolveMethodToken(packed.DeclaringType, packed.MethodToken);
|
||||
if (method.IsGenericMethodDefinition)
|
||||
method = ((MethodInfo)method).MakeGenericMethod(packed.GenericTypes!);
|
||||
|
||||
// Call (protected method) DispatchProxy.Invoke()
|
||||
try
|
||||
{
|
||||
Debug.Assert(s_dispatchProxyInvokeMethod != null);
|
||||
object? returnValue = s_dispatchProxyInvokeMethod!.Invoke(packed.DispatchProxy,
|
||||
new object?[] { method, packed.Args });
|
||||
packed.ReturnValue = returnValue;
|
||||
}
|
||||
catch (TargetInvocationException tie)
|
||||
{
|
||||
Debug.Assert(tie.InnerException != null);
|
||||
ExceptionDispatchInfo.Capture(tie.InnerException).Throw();
|
||||
}
|
||||
}
|
||||
|
||||
private class PackedArgs
|
||||
{
|
||||
internal const int DispatchProxyPosition = 0;
|
||||
internal const int DeclaringTypePosition = 1;
|
||||
internal const int MethodTokenPosition = 2;
|
||||
internal const int ArgsPosition = 3;
|
||||
internal const int GenericTypesPosition = 4;
|
||||
internal const int ReturnValuePosition = 5;
|
||||
|
||||
internal static readonly Type[] PackedTypes = new Type[] { typeof(object), typeof(Type), typeof(int), typeof(object[]), typeof(Type[]), typeof(object) };
|
||||
|
||||
private readonly object?[] _args;
|
||||
internal PackedArgs() : this(new object[PackedTypes.Length]) { }
|
||||
internal PackedArgs(object?[] args) { _args = args; }
|
||||
|
||||
internal DispatchProxy? DispatchProxy { get { return (DispatchProxy?)_args[DispatchProxyPosition]; } }
|
||||
internal Type? DeclaringType { get { return (Type?)_args[DeclaringTypePosition]; } }
|
||||
internal int MethodToken { get { return (int)_args[MethodTokenPosition]!; } }
|
||||
internal object[]? Args { get { return (object[]?)_args[ArgsPosition]; } }
|
||||
internal Type[]? GenericTypes { get { return (Type[]?)_args[GenericTypesPosition]; } }
|
||||
internal object? ReturnValue { /*get { return args[ReturnValuePosition]; }*/ set { _args[ReturnValuePosition] = value; } }
|
||||
}
|
||||
|
||||
private class ProxyAssembly
|
||||
{
|
||||
private readonly AssemblyBuilder _ab;
|
||||
private readonly ModuleBuilder _mb;
|
||||
private int _typeId;
|
||||
|
||||
// Maintain a MethodBase-->int, int-->MethodBase mapping to permit generated code
|
||||
// to pass methods by token
|
||||
private readonly Dictionary<MethodBase, int> _methodToToken = new Dictionary<MethodBase, int>();
|
||||
private readonly List<MethodBase> _methodsByToken = new List<MethodBase>();
|
||||
private readonly HashSet<string?> _ignoresAccessAssemblyNames = new HashSet<string?>();
|
||||
private ConstructorInfo? _ignoresAccessChecksToAttributeConstructor;
|
||||
|
||||
public ProxyAssembly()
|
||||
{
|
||||
_ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("ProxyBuilder"), AssemblyBuilderAccess.Run);
|
||||
_mb = _ab.DefineDynamicModule("testmod");
|
||||
}
|
||||
|
||||
// Gets or creates the ConstructorInfo for the IgnoresAccessChecksAttribute.
|
||||
// This attribute is both defined and referenced in the dynamic assembly to
|
||||
// allow access to internal types in other assemblies.
|
||||
internal ConstructorInfo IgnoresAccessChecksAttributeConstructor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ignoresAccessChecksToAttributeConstructor == null)
|
||||
{
|
||||
_ignoresAccessChecksToAttributeConstructor = IgnoreAccessChecksToAttributeBuilder.AddToModule(_mb);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public ProxyBuilder CreateProxy(string name, Type proxyBaseType)
|
||||
{
|
||||
int nextId = Interlocked.Increment(ref _typeId);
|
||||
TypeBuilder tb = _mb.DefineType(name + "_" + nextId, TypeAttributes.Public, proxyBaseType);
|
||||
return new ProxyBuilder(this, tb, proxyBaseType);
|
||||
}
|
||||
|
||||
// Generates an instance of the IgnoresAccessChecksToAttribute to
|
||||
// identify the given assembly as one which contains internal types
|
||||
// the dynamic assembly will need to reference.
|
||||
internal void GenerateInstanceOfIgnoresAccessChecksToAttribute(string assemblyName)
|
||||
{
|
||||
// Add this assembly level attribute:
|
||||
// [assembly: System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute(assemblyName)]
|
||||
ConstructorInfo attributeConstructor = IgnoresAccessChecksAttributeConstructor;
|
||||
CustomAttributeBuilder customAttributeBuilder =
|
||||
new CustomAttributeBuilder(attributeConstructor, new object[] { assemblyName });
|
||||
_ab.SetCustomAttribute(customAttributeBuilder);
|
||||
}
|
||||
|
||||
// Ensures the type we will reference from the dynamic assembly
|
||||
// is visible. Non-public types need to emit an attribute that
|
||||
// allows access from the dynamic assembly.
|
||||
internal void EnsureTypeIsVisible(Type type)
|
||||
{
|
||||
TypeInfo typeInfo = type.GetTypeInfo();
|
||||
if (!typeInfo.IsVisible)
|
||||
{
|
||||
string assemblyName = typeInfo.Assembly.GetName().Name!;
|
||||
if (!_ignoresAccessAssemblyNames.Contains(assemblyName))
|
||||
{
|
||||
GenerateInstanceOfIgnoresAccessChecksToAttribute(assemblyName);
|
||||
_ignoresAccessAssemblyNames.Add(assemblyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void GetTokenForMethod(MethodBase method, out Type type, out int token)
|
||||
{
|
||||
Debug.Assert(method.DeclaringType != null);
|
||||
type = method.DeclaringType!;
|
||||
token = 0;
|
||||
if (!_methodToToken.TryGetValue(method, out token))
|
||||
{
|
||||
_methodsByToken.Add(method);
|
||||
token = _methodsByToken.Count - 1;
|
||||
_methodToToken[method] = token;
|
||||
}
|
||||
}
|
||||
|
||||
internal MethodBase ResolveMethodToken(Type? type, int token)
|
||||
{
|
||||
Debug.Assert(token >= 0 && token < _methodsByToken.Count);
|
||||
return _methodsByToken[token];
|
||||
}
|
||||
}
|
||||
|
||||
private class ProxyBuilder
|
||||
{
|
||||
private static readonly MethodInfo s_delegateInvoke = typeof(Action<object[]>).GetTypeInfo().GetDeclaredMethod("Invoke")!;
|
||||
|
||||
private readonly ProxyAssembly _assembly;
|
||||
private readonly TypeBuilder _tb;
|
||||
private readonly Type _proxyBaseType;
|
||||
private readonly List<FieldBuilder> _fields;
|
||||
|
||||
internal ProxyBuilder(ProxyAssembly assembly, TypeBuilder tb, Type proxyBaseType)
|
||||
{
|
||||
_assembly = assembly;
|
||||
_tb = tb;
|
||||
_proxyBaseType = proxyBaseType;
|
||||
|
||||
_fields = new List<FieldBuilder>();
|
||||
_fields.Add(tb.DefineField("invoke", typeof(Action<object[]>), FieldAttributes.Private));
|
||||
}
|
||||
|
||||
private void Complete()
|
||||
{
|
||||
Type[] args = new Type[_fields.Count];
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
args[i] = _fields[i].FieldType;
|
||||
}
|
||||
|
||||
ConstructorBuilder cb = _tb.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, args);
|
||||
ILGenerator il = cb.GetILGenerator();
|
||||
|
||||
// chained ctor call
|
||||
ConstructorInfo? baseCtor = _proxyBaseType.GetTypeInfo().DeclaredConstructors.SingleOrDefault(c => c.IsPublic && c.GetParameters().Length == 0);
|
||||
Debug.Assert(baseCtor != null);
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Call, baseCtor!);
|
||||
|
||||
// store all the fields
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldarg, i + 1);
|
||||
il.Emit(OpCodes.Stfld, _fields[i]);
|
||||
}
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
internal Type CreateType()
|
||||
{
|
||||
this.Complete();
|
||||
return _tb.CreateTypeInfo()!.AsType();
|
||||
}
|
||||
|
||||
internal void AddInterfaceImpl(Type iface)
|
||||
{
|
||||
// If necessary, generate an attribute to permit visibility
|
||||
// to internal types.
|
||||
_assembly.EnsureTypeIsVisible(iface);
|
||||
|
||||
_tb.AddInterfaceImplementation(iface);
|
||||
|
||||
// AccessorMethods -> Metadata mappings.
|
||||
var propertyMap = new Dictionary<MethodInfo, PropertyAccessorInfo>(MethodInfoEqualityComparer.Instance);
|
||||
foreach (PropertyInfo pi in iface.GetRuntimeProperties())
|
||||
{
|
||||
var ai = new PropertyAccessorInfo(pi.GetMethod, pi.SetMethod);
|
||||
if (pi.GetMethod != null)
|
||||
propertyMap[pi.GetMethod] = ai;
|
||||
if (pi.SetMethod != null)
|
||||
propertyMap[pi.SetMethod] = ai;
|
||||
}
|
||||
|
||||
var eventMap = new Dictionary<MethodInfo, EventAccessorInfo>(MethodInfoEqualityComparer.Instance);
|
||||
foreach (EventInfo ei in iface.GetRuntimeEvents())
|
||||
{
|
||||
var ai = new EventAccessorInfo(ei.AddMethod, ei.RemoveMethod, ei.RaiseMethod);
|
||||
if (ei.AddMethod != null)
|
||||
eventMap[ei.AddMethod] = ai;
|
||||
if (ei.RemoveMethod != null)
|
||||
eventMap[ei.RemoveMethod] = ai;
|
||||
if (ei.RaiseMethod != null)
|
||||
eventMap[ei.RaiseMethod] = ai;
|
||||
}
|
||||
|
||||
foreach (MethodInfo mi in iface.GetRuntimeMethods())
|
||||
{
|
||||
// Skip regular/non-virtual instance methods, static methods, and methods that cannot be overriden
|
||||
// ("methods that cannot be overriden" includes default implementation of other interface methods).
|
||||
if (!mi.IsVirtual || mi.IsFinal)
|
||||
continue;
|
||||
|
||||
MethodBuilder mdb = AddMethodImpl(mi);
|
||||
if (propertyMap.TryGetValue(mi, out PropertyAccessorInfo? associatedProperty))
|
||||
{
|
||||
if (MethodInfoEqualityComparer.Instance.Equals(associatedProperty.InterfaceGetMethod, mi))
|
||||
associatedProperty.GetMethodBuilder = mdb;
|
||||
else
|
||||
associatedProperty.SetMethodBuilder = mdb;
|
||||
}
|
||||
|
||||
if (eventMap.TryGetValue(mi, out EventAccessorInfo? associatedEvent))
|
||||
{
|
||||
if (MethodInfoEqualityComparer.Instance.Equals(associatedEvent.InterfaceAddMethod, mi))
|
||||
associatedEvent.AddMethodBuilder = mdb;
|
||||
else if (MethodInfoEqualityComparer.Instance.Equals(associatedEvent.InterfaceRemoveMethod, mi))
|
||||
associatedEvent.RemoveMethodBuilder = mdb;
|
||||
else
|
||||
associatedEvent.RaiseMethodBuilder = mdb;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (PropertyInfo pi in iface.GetRuntimeProperties())
|
||||
{
|
||||
PropertyAccessorInfo ai = propertyMap[pi.GetMethod ?? pi.SetMethod!];
|
||||
|
||||
// If we didn't make an overriden accessor above, this was a static property, non-virtual property,
|
||||
// or a default implementation of a property of a different interface. In any case, we don't need
|
||||
// to redeclare it.
|
||||
if (ai.GetMethodBuilder == null && ai.SetMethodBuilder == null)
|
||||
continue;
|
||||
|
||||
PropertyBuilder pb = _tb.DefineProperty(pi.Name, pi.Attributes, pi.PropertyType, pi.GetIndexParameters().Select(p => p.ParameterType).ToArray());
|
||||
if (ai.GetMethodBuilder != null)
|
||||
pb.SetGetMethod(ai.GetMethodBuilder);
|
||||
if (ai.SetMethodBuilder != null)
|
||||
pb.SetSetMethod(ai.SetMethodBuilder);
|
||||
}
|
||||
|
||||
foreach (EventInfo ei in iface.GetRuntimeEvents())
|
||||
{
|
||||
EventAccessorInfo ai = eventMap[ei.AddMethod ?? ei.RemoveMethod!];
|
||||
|
||||
// If we didn't make an overriden accessor above, this was a static event, non-virtual event,
|
||||
// or a default implementation of an event of a different interface. In any case, we don't
|
||||
// need to redeclare it.
|
||||
if (ai.AddMethodBuilder == null && ai.RemoveMethodBuilder == null && ai.RaiseMethodBuilder == null)
|
||||
continue;
|
||||
|
||||
Debug.Assert(ei.EventHandlerType != null);
|
||||
EventBuilder eb = _tb.DefineEvent(ei.Name, ei.Attributes, ei.EventHandlerType!);
|
||||
if (ai.AddMethodBuilder != null)
|
||||
eb.SetAddOnMethod(ai.AddMethodBuilder);
|
||||
if (ai.RemoveMethodBuilder != null)
|
||||
eb.SetRemoveOnMethod(ai.RemoveMethodBuilder);
|
||||
if (ai.RaiseMethodBuilder != null)
|
||||
eb.SetRaiseMethod(ai.RaiseMethodBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
private MethodBuilder AddMethodImpl(MethodInfo mi)
|
||||
{
|
||||
ParameterInfo[] parameters = mi.GetParameters();
|
||||
Type[] paramTypes = ParamTypes(parameters, false);
|
||||
|
||||
MethodBuilder mdb = _tb.DefineMethod(mi.Name, MethodAttributes.Public | MethodAttributes.Virtual, mi.ReturnType, paramTypes);
|
||||
if (mi.ContainsGenericParameters)
|
||||
{
|
||||
Type[] ts = mi.GetGenericArguments();
|
||||
string[] ss = new string[ts.Length];
|
||||
for (int i = 0; i < ts.Length; i++)
|
||||
{
|
||||
ss[i] = ts[i].Name;
|
||||
}
|
||||
GenericTypeParameterBuilder[] genericParameters = mdb.DefineGenericParameters(ss);
|
||||
for (int i = 0; i < genericParameters.Length; i++)
|
||||
{
|
||||
genericParameters[i].SetGenericParameterAttributes(ts[i].GetTypeInfo().GenericParameterAttributes);
|
||||
}
|
||||
}
|
||||
ILGenerator il = mdb.GetILGenerator();
|
||||
|
||||
ParametersArray args = new ParametersArray(il, paramTypes);
|
||||
|
||||
// object[] args = new object[paramCount];
|
||||
il.Emit(OpCodes.Nop);
|
||||
GenericArray<object> argsArr = new GenericArray<object>(il, ParamTypes(parameters, true).Length);
|
||||
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
// args[i] = argi;
|
||||
bool isOutRef = parameters[i].IsOut && parameters[i].ParameterType.IsByRef && !parameters[i].IsIn;
|
||||
|
||||
if (!isOutRef)
|
||||
{
|
||||
argsArr.BeginSet(i);
|
||||
args.Get(i);
|
||||
argsArr.EndSet(parameters[i].ParameterType);
|
||||
}
|
||||
}
|
||||
|
||||
// object[] packed = new object[PackedArgs.PackedTypes.Length];
|
||||
GenericArray<object> packedArr = new GenericArray<object>(il, PackedArgs.PackedTypes.Length);
|
||||
|
||||
// packed[PackedArgs.DispatchProxyPosition] = this;
|
||||
packedArr.BeginSet(PackedArgs.DispatchProxyPosition);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
packedArr.EndSet(typeof(DispatchProxy));
|
||||
|
||||
// packed[PackedArgs.DeclaringTypePosition] = typeof(iface);
|
||||
MethodInfo Type_GetTypeFromHandle = typeof(Type).GetRuntimeMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) })!;
|
||||
_assembly.GetTokenForMethod(mi, out Type declaringType, out int methodToken);
|
||||
packedArr.BeginSet(PackedArgs.DeclaringTypePosition);
|
||||
il.Emit(OpCodes.Ldtoken, declaringType);
|
||||
il.Emit(OpCodes.Call, Type_GetTypeFromHandle);
|
||||
packedArr.EndSet(typeof(object));
|
||||
|
||||
// packed[PackedArgs.MethodTokenPosition] = iface method token;
|
||||
packedArr.BeginSet(PackedArgs.MethodTokenPosition);
|
||||
il.Emit(OpCodes.Ldc_I4, methodToken);
|
||||
packedArr.EndSet(typeof(int));
|
||||
|
||||
// packed[PackedArgs.ArgsPosition] = args;
|
||||
packedArr.BeginSet(PackedArgs.ArgsPosition);
|
||||
argsArr.Load();
|
||||
packedArr.EndSet(typeof(object[]));
|
||||
|
||||
// packed[PackedArgs.GenericTypesPosition] = mi.GetGenericArguments();
|
||||
if (mi.ContainsGenericParameters)
|
||||
{
|
||||
packedArr.BeginSet(PackedArgs.GenericTypesPosition);
|
||||
Type[] genericTypes = mi.GetGenericArguments();
|
||||
GenericArray<Type> typeArr = new GenericArray<Type>(il, genericTypes.Length);
|
||||
for (int i = 0; i < genericTypes.Length; ++i)
|
||||
{
|
||||
typeArr.BeginSet(i);
|
||||
il.Emit(OpCodes.Ldtoken, genericTypes[i]);
|
||||
il.Emit(OpCodes.Call, Type_GetTypeFromHandle);
|
||||
typeArr.EndSet(typeof(Type));
|
||||
}
|
||||
typeArr.Load();
|
||||
packedArr.EndSet(typeof(Type[]));
|
||||
}
|
||||
|
||||
// Call static DispatchProxyHelper.Invoke(object[])
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, _fields[InvokeActionFieldAndCtorParameterIndex]); // delegate
|
||||
packedArr.Load();
|
||||
il.Emit(OpCodes.Call, s_delegateInvoke);
|
||||
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (parameters[i].ParameterType.IsByRef)
|
||||
{
|
||||
args.BeginSet(i);
|
||||
argsArr.Get(i);
|
||||
args.EndSet(i, typeof(object));
|
||||
}
|
||||
}
|
||||
|
||||
if (mi.ReturnType != typeof(void))
|
||||
{
|
||||
packedArr.Get(PackedArgs.ReturnValuePosition);
|
||||
Convert(il, typeof(object), mi.ReturnType, false);
|
||||
}
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
_tb.DefineMethodOverride(mdb, mi);
|
||||
return mdb;
|
||||
}
|
||||
|
||||
private static Type[] ParamTypes(ParameterInfo[] parms, bool noByRef)
|
||||
{
|
||||
Type[] types = new Type[parms.Length];
|
||||
for (int i = 0; i < parms.Length; i++)
|
||||
{
|
||||
types[i] = parms[i].ParameterType;
|
||||
if (noByRef && types[i].IsByRef)
|
||||
types[i] = types[i].GetElementType()!;
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
// TypeCode does not exist in ProjectK or ProjectN.
|
||||
// This lookup method was copied from PortableLibraryThunks\Internal\PortableLibraryThunks\System\TypeThunks.cs
|
||||
// but returns the integer value equivalent to its TypeCode enum.
|
||||
private static int GetTypeCode(Type? type)
|
||||
{
|
||||
if (type == null)
|
||||
return 0; // TypeCode.Empty;
|
||||
|
||||
if (type == typeof(bool))
|
||||
return 3; // TypeCode.Boolean;
|
||||
|
||||
if (type == typeof(char))
|
||||
return 4; // TypeCode.Char;
|
||||
|
||||
if (type == typeof(sbyte))
|
||||
return 5; // TypeCode.SByte;
|
||||
|
||||
if (type == typeof(byte))
|
||||
return 6; // TypeCode.Byte;
|
||||
|
||||
if (type == typeof(short))
|
||||
return 7; // TypeCode.Int16;
|
||||
|
||||
if (type == typeof(ushort))
|
||||
return 8; // TypeCode.UInt16;
|
||||
|
||||
if (type == typeof(int))
|
||||
return 9; // TypeCode.Int32;
|
||||
|
||||
if (type == typeof(uint))
|
||||
return 10; // TypeCode.UInt32;
|
||||
|
||||
if (type == typeof(long))
|
||||
return 11; // TypeCode.Int64;
|
||||
|
||||
if (type == typeof(ulong))
|
||||
return 12; // TypeCode.UInt64;
|
||||
|
||||
if (type == typeof(float))
|
||||
return 13; // TypeCode.Single;
|
||||
|
||||
if (type == typeof(double))
|
||||
return 14; // TypeCode.Double;
|
||||
|
||||
if (type == typeof(decimal))
|
||||
return 15; // TypeCode.Decimal;
|
||||
|
||||
if (type == typeof(DateTime))
|
||||
return 16; // TypeCode.DateTime;
|
||||
|
||||
if (type == typeof(string))
|
||||
return 18; // TypeCode.String;
|
||||
|
||||
if (type.GetTypeInfo().IsEnum)
|
||||
return GetTypeCode(Enum.GetUnderlyingType(type));
|
||||
|
||||
return 1; // TypeCode.Object;
|
||||
}
|
||||
|
||||
private static readonly OpCode[] s_convOpCodes = new OpCode[] {
|
||||
OpCodes.Nop, //Empty = 0,
|
||||
OpCodes.Nop, //Object = 1,
|
||||
OpCodes.Nop, //DBNull = 2,
|
||||
OpCodes.Conv_I1, //Boolean = 3,
|
||||
OpCodes.Conv_I2, //Char = 4,
|
||||
OpCodes.Conv_I1, //SByte = 5,
|
||||
OpCodes.Conv_U1, //Byte = 6,
|
||||
OpCodes.Conv_I2, //Int16 = 7,
|
||||
OpCodes.Conv_U2, //UInt16 = 8,
|
||||
OpCodes.Conv_I4, //Int32 = 9,
|
||||
OpCodes.Conv_U4, //UInt32 = 10,
|
||||
OpCodes.Conv_I8, //Int64 = 11,
|
||||
OpCodes.Conv_U8, //UInt64 = 12,
|
||||
OpCodes.Conv_R4, //Single = 13,
|
||||
OpCodes.Conv_R8, //Double = 14,
|
||||
OpCodes.Nop, //Decimal = 15,
|
||||
OpCodes.Nop, //DateTime = 16,
|
||||
OpCodes.Nop, //17
|
||||
OpCodes.Nop, //String = 18,
|
||||
};
|
||||
|
||||
private static readonly OpCode[] s_ldindOpCodes = new OpCode[] {
|
||||
OpCodes.Nop, //Empty = 0,
|
||||
OpCodes.Nop, //Object = 1,
|
||||
OpCodes.Nop, //DBNull = 2,
|
||||
OpCodes.Ldind_I1, //Boolean = 3,
|
||||
OpCodes.Ldind_I2, //Char = 4,
|
||||
OpCodes.Ldind_I1, //SByte = 5,
|
||||
OpCodes.Ldind_U1, //Byte = 6,
|
||||
OpCodes.Ldind_I2, //Int16 = 7,
|
||||
OpCodes.Ldind_U2, //UInt16 = 8,
|
||||
OpCodes.Ldind_I4, //Int32 = 9,
|
||||
OpCodes.Ldind_U4, //UInt32 = 10,
|
||||
OpCodes.Ldind_I8, //Int64 = 11,
|
||||
OpCodes.Ldind_I8, //UInt64 = 12,
|
||||
OpCodes.Ldind_R4, //Single = 13,
|
||||
OpCodes.Ldind_R8, //Double = 14,
|
||||
OpCodes.Nop, //Decimal = 15,
|
||||
OpCodes.Nop, //DateTime = 16,
|
||||
OpCodes.Nop, //17
|
||||
OpCodes.Ldind_Ref, //String = 18,
|
||||
};
|
||||
|
||||
private static readonly OpCode[] s_stindOpCodes = new OpCode[] {
|
||||
OpCodes.Nop, //Empty = 0,
|
||||
OpCodes.Nop, //Object = 1,
|
||||
OpCodes.Nop, //DBNull = 2,
|
||||
OpCodes.Stind_I1, //Boolean = 3,
|
||||
OpCodes.Stind_I2, //Char = 4,
|
||||
OpCodes.Stind_I1, //SByte = 5,
|
||||
OpCodes.Stind_I1, //Byte = 6,
|
||||
OpCodes.Stind_I2, //Int16 = 7,
|
||||
OpCodes.Stind_I2, //UInt16 = 8,
|
||||
OpCodes.Stind_I4, //Int32 = 9,
|
||||
OpCodes.Stind_I4, //UInt32 = 10,
|
||||
OpCodes.Stind_I8, //Int64 = 11,
|
||||
OpCodes.Stind_I8, //UInt64 = 12,
|
||||
OpCodes.Stind_R4, //Single = 13,
|
||||
OpCodes.Stind_R8, //Double = 14,
|
||||
OpCodes.Nop, //Decimal = 15,
|
||||
OpCodes.Nop, //DateTime = 16,
|
||||
OpCodes.Nop, //17
|
||||
OpCodes.Stind_Ref, //String = 18,
|
||||
};
|
||||
|
||||
private static void Convert(ILGenerator il, Type source, Type target, bool isAddress)
|
||||
{
|
||||
Debug.Assert(!target.IsByRef);
|
||||
if (target == source)
|
||||
return;
|
||||
|
||||
TypeInfo sourceTypeInfo = source.GetTypeInfo();
|
||||
TypeInfo targetTypeInfo = target.GetTypeInfo();
|
||||
|
||||
if (source.IsByRef)
|
||||
{
|
||||
Debug.Assert(!isAddress);
|
||||
Type argType = source.GetElementType()!;
|
||||
Ldind(il, argType);
|
||||
Convert(il, argType, target, isAddress);
|
||||
return;
|
||||
}
|
||||
if (targetTypeInfo.IsValueType)
|
||||
{
|
||||
if (sourceTypeInfo.IsValueType)
|
||||
{
|
||||
OpCode opCode = s_convOpCodes[GetTypeCode(target)];
|
||||
Debug.Assert(!opCode.Equals(OpCodes.Nop));
|
||||
il.Emit(opCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(sourceTypeInfo.IsAssignableFrom(targetTypeInfo));
|
||||
il.Emit(OpCodes.Unbox, target);
|
||||
if (!isAddress)
|
||||
Ldind(il, target);
|
||||
}
|
||||
}
|
||||
else if (targetTypeInfo.IsAssignableFrom(sourceTypeInfo))
|
||||
{
|
||||
if (sourceTypeInfo.IsValueType || source.IsGenericParameter)
|
||||
{
|
||||
if (isAddress)
|
||||
Ldind(il, source);
|
||||
il.Emit(OpCodes.Box, source);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(sourceTypeInfo.IsAssignableFrom(targetTypeInfo) || targetTypeInfo.IsInterface || sourceTypeInfo.IsInterface);
|
||||
if (target.IsGenericParameter)
|
||||
{
|
||||
il.Emit(OpCodes.Unbox_Any, target);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Castclass, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Ldind(ILGenerator il, Type type)
|
||||
{
|
||||
OpCode opCode = s_ldindOpCodes[GetTypeCode(type)];
|
||||
if (!opCode.Equals(OpCodes.Nop))
|
||||
{
|
||||
il.Emit(opCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldobj, type);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Stind(ILGenerator il, Type type)
|
||||
{
|
||||
OpCode opCode = s_stindOpCodes[GetTypeCode(type)];
|
||||
if (!opCode.Equals(OpCodes.Nop))
|
||||
{
|
||||
il.Emit(opCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Stobj, type);
|
||||
}
|
||||
}
|
||||
|
||||
private class ParametersArray
|
||||
{
|
||||
private readonly ILGenerator _il;
|
||||
private readonly Type[] _paramTypes;
|
||||
internal ParametersArray(ILGenerator il, Type[] paramTypes)
|
||||
{
|
||||
_il = il;
|
||||
_paramTypes = paramTypes;
|
||||
}
|
||||
|
||||
internal void Get(int i)
|
||||
{
|
||||
_il.Emit(OpCodes.Ldarg, i + 1);
|
||||
}
|
||||
|
||||
internal void BeginSet(int i)
|
||||
{
|
||||
_il.Emit(OpCodes.Ldarg, i + 1);
|
||||
}
|
||||
|
||||
internal void EndSet(int i, Type stackType)
|
||||
{
|
||||
Debug.Assert(_paramTypes[i].IsByRef);
|
||||
Type argType = _paramTypes[i].GetElementType()!;
|
||||
Convert(_il, stackType, argType, false);
|
||||
Stind(_il, argType);
|
||||
}
|
||||
}
|
||||
|
||||
private class GenericArray<T>
|
||||
{
|
||||
private readonly ILGenerator _il;
|
||||
private readonly LocalBuilder _lb;
|
||||
internal GenericArray(ILGenerator il, int len)
|
||||
{
|
||||
_il = il;
|
||||
_lb = il.DeclareLocal(typeof(T[]));
|
||||
|
||||
il.Emit(OpCodes.Ldc_I4, len);
|
||||
il.Emit(OpCodes.Newarr, typeof(T));
|
||||
il.Emit(OpCodes.Stloc, _lb);
|
||||
}
|
||||
|
||||
internal void Load()
|
||||
{
|
||||
_il.Emit(OpCodes.Ldloc, _lb);
|
||||
}
|
||||
|
||||
internal void Get(int i)
|
||||
{
|
||||
_il.Emit(OpCodes.Ldloc, _lb);
|
||||
_il.Emit(OpCodes.Ldc_I4, i);
|
||||
_il.Emit(OpCodes.Ldelem_Ref);
|
||||
}
|
||||
|
||||
internal void BeginSet(int i)
|
||||
{
|
||||
_il.Emit(OpCodes.Ldloc, _lb);
|
||||
_il.Emit(OpCodes.Ldc_I4, i);
|
||||
}
|
||||
|
||||
internal void EndSet(Type stackType)
|
||||
{
|
||||
Convert(_il, stackType, typeof(T), false);
|
||||
_il.Emit(OpCodes.Stelem_Ref);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PropertyAccessorInfo
|
||||
{
|
||||
public MethodInfo? InterfaceGetMethod { get; }
|
||||
public MethodInfo? InterfaceSetMethod { get; }
|
||||
public MethodBuilder? GetMethodBuilder { get; set; }
|
||||
public MethodBuilder? SetMethodBuilder { get; set; }
|
||||
|
||||
public PropertyAccessorInfo(MethodInfo? interfaceGetMethod, MethodInfo? interfaceSetMethod)
|
||||
{
|
||||
InterfaceGetMethod = interfaceGetMethod;
|
||||
InterfaceSetMethod = interfaceSetMethod;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class EventAccessorInfo
|
||||
{
|
||||
public MethodInfo? InterfaceAddMethod { get; }
|
||||
public MethodInfo? InterfaceRemoveMethod { get; }
|
||||
public MethodInfo? InterfaceRaiseMethod { get; }
|
||||
public MethodBuilder? AddMethodBuilder { get; set; }
|
||||
public MethodBuilder? RemoveMethodBuilder { get; set; }
|
||||
public MethodBuilder? RaiseMethodBuilder { get; set; }
|
||||
|
||||
public EventAccessorInfo(MethodInfo? interfaceAddMethod, MethodInfo? interfaceRemoveMethod, MethodInfo? interfaceRaiseMethod)
|
||||
{
|
||||
InterfaceAddMethod = interfaceAddMethod;
|
||||
InterfaceRemoveMethod = interfaceRemoveMethod;
|
||||
InterfaceRaiseMethod = interfaceRaiseMethod;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class MethodInfoEqualityComparer : EqualityComparer<MethodInfo>
|
||||
{
|
||||
public static readonly MethodInfoEqualityComparer Instance = new MethodInfoEqualityComparer();
|
||||
|
||||
private MethodInfoEqualityComparer() { }
|
||||
|
||||
public sealed override bool Equals(MethodInfo? left, MethodInfo? right)
|
||||
{
|
||||
if (ReferenceEquals(left, right))
|
||||
return true;
|
||||
|
||||
if (left == null)
|
||||
return right == null;
|
||||
else if (right == null)
|
||||
return false;
|
||||
|
||||
// This assembly should work in netstandard1.3,
|
||||
// so we cannot use MemberInfo.MetadataToken here.
|
||||
// Therefore, it compares honestly referring ECMA-335 I.8.6.1.6 Signature Matching.
|
||||
if (!Equals(left.DeclaringType, right.DeclaringType))
|
||||
return false;
|
||||
|
||||
if (!Equals(left.ReturnType, right.ReturnType))
|
||||
return false;
|
||||
|
||||
if (left.CallingConvention != right.CallingConvention)
|
||||
return false;
|
||||
|
||||
if (left.IsStatic != right.IsStatic)
|
||||
return false;
|
||||
|
||||
if (left.Name != right.Name)
|
||||
return false;
|
||||
|
||||
Type[] leftGenericParameters = left.GetGenericArguments();
|
||||
Type[] rightGenericParameters = right.GetGenericArguments();
|
||||
if (leftGenericParameters.Length != rightGenericParameters.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < leftGenericParameters.Length; i++)
|
||||
{
|
||||
if (!Equals(leftGenericParameters[i], rightGenericParameters[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
ParameterInfo[] leftParameters = left.GetParameters();
|
||||
ParameterInfo[] rightParameters = right.GetParameters();
|
||||
if (leftParameters.Length != rightParameters.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < leftParameters.Length; i++)
|
||||
{
|
||||
if (!Equals(leftParameters[i].ParameterType, rightParameters[i].ParameterType))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public sealed override int GetHashCode(MethodInfo obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return 0;
|
||||
|
||||
Debug.Assert(obj.DeclaringType != null);
|
||||
int hashCode = obj.DeclaringType!.GetHashCode();
|
||||
hashCode ^= obj.Name.GetHashCode();
|
||||
foreach (ParameterInfo parameter in obj.GetParameters())
|
||||
{
|
||||
hashCode ^= parameter.ParameterType.GetHashCode();
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
101
Vendor/Prise.Proxy/runtime/IngoreAccessChecksToAttributeBuilder.cs
vendored
Normal file
101
Vendor/Prise.Proxy/runtime/IngoreAccessChecksToAttributeBuilder.cs
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
// Original File: https://github.com/dotnet/runtime/blob/6072e4d3a7a2a1493f514cdf4be75a3d56580e84/src/libraries/Common/src/System/Reflection/Emit/IgnoreAccessChecksToAttributeBuilder.cs
|
||||
using System.Linq;
|
||||
|
||||
namespace System.Reflection.Emit
|
||||
{
|
||||
internal static class IgnoreAccessChecksToAttributeBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Generate the declaration for the IgnoresAccessChecksToAttribute type.
|
||||
/// This attribute will be both defined and used in the dynamic assembly.
|
||||
/// Each usage identifies the name of the assembly containing non-public
|
||||
/// types the dynamic assembly needs to access. Normally those types
|
||||
/// would be inaccessible, but this attribute allows them to be visible.
|
||||
/// It works like a reverse InternalsVisibleToAttribute.
|
||||
/// This method returns the ConstructorInfo of the generated attribute.
|
||||
/// </summary>
|
||||
public static ConstructorInfo AddToModule(ModuleBuilder mb)
|
||||
{
|
||||
TypeBuilder attributeTypeBuilder =
|
||||
mb.DefineType("System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute",
|
||||
TypeAttributes.Public | TypeAttributes.Class,
|
||||
typeof(Attribute));
|
||||
|
||||
// Create backing field as:
|
||||
// private string assemblyName;
|
||||
FieldBuilder assemblyNameField =
|
||||
attributeTypeBuilder.DefineField("assemblyName", typeof(string), FieldAttributes.Private);
|
||||
|
||||
// Create ctor as:
|
||||
// public IgnoresAccessChecksToAttribute(string)
|
||||
ConstructorBuilder constructorBuilder = attributeTypeBuilder.DefineConstructor(MethodAttributes.Public,
|
||||
CallingConventions.HasThis,
|
||||
new Type[] { assemblyNameField.FieldType });
|
||||
|
||||
ILGenerator il = constructorBuilder.GetILGenerator();
|
||||
|
||||
// Create ctor body as:
|
||||
// this.assemblyName = {ctor parameter 0}
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldarg, 1);
|
||||
il.Emit(OpCodes.Stfld, assemblyNameField);
|
||||
|
||||
// return
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
// Define property as:
|
||||
// public string AssemblyName {get { return this.assemblyName; } }
|
||||
_ = attributeTypeBuilder.DefineProperty(
|
||||
"AssemblyName",
|
||||
PropertyAttributes.None,
|
||||
CallingConventions.HasThis,
|
||||
returnType: typeof(string),
|
||||
parameterTypes: null);
|
||||
|
||||
MethodBuilder getterMethodBuilder = attributeTypeBuilder.DefineMethod(
|
||||
"get_AssemblyName",
|
||||
MethodAttributes.Public,
|
||||
CallingConventions.HasThis,
|
||||
returnType: typeof(string),
|
||||
parameterTypes: null);
|
||||
|
||||
// Generate body:
|
||||
// return this.assemblyName;
|
||||
il = getterMethodBuilder.GetILGenerator();
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, assemblyNameField);
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
// Generate the AttributeUsage attribute for this attribute type:
|
||||
// [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
TypeInfo attributeUsageTypeInfo = typeof(AttributeUsageAttribute).GetTypeInfo();
|
||||
|
||||
// Find the ctor that takes only AttributeTargets
|
||||
ConstructorInfo attributeUsageConstructorInfo =
|
||||
attributeUsageTypeInfo.DeclaredConstructors
|
||||
.Single(c => c.GetParameters().Length == 1 &&
|
||||
c.GetParameters()[0].ParameterType == typeof(AttributeTargets));
|
||||
|
||||
// Find the property to set AllowMultiple
|
||||
PropertyInfo allowMultipleProperty =
|
||||
attributeUsageTypeInfo.DeclaredProperties
|
||||
.Single(f => string.Equals(f.Name, "AllowMultiple"));
|
||||
|
||||
// Create a builder to construct the instance via the ctor and property
|
||||
CustomAttributeBuilder customAttributeBuilder =
|
||||
new CustomAttributeBuilder(attributeUsageConstructorInfo,
|
||||
new object[] { AttributeTargets.Assembly },
|
||||
new PropertyInfo[] { allowMultipleProperty },
|
||||
new object[] { true });
|
||||
|
||||
// Attach this attribute instance to the newly defined attribute type
|
||||
attributeTypeBuilder.SetCustomAttribute(customAttributeBuilder);
|
||||
|
||||
// Make the TypeInfo real so the constructor can be used.
|
||||
return attributeTypeBuilder.CreateTypeInfo()!.DeclaredConstructors.Single();
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Vendor/Prise.ReverseProxy/Prise.ReverseProxy.csproj
vendored
Normal file
61
Vendor/Prise.ReverseProxy/Prise.ReverseProxy.csproj
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Title>Prise.ReverseProxy</Title>
|
||||
<PackageId>Prise.ReverseProxy</PackageId>
|
||||
<PackageDescription>Adds support for sharing services between a Prise Host and a Prise Plugin</PackageDescription>
|
||||
<Authors>Maarten Merken</Authors>
|
||||
<Company>MRKN</Company>
|
||||
<PackageTags>plugin;framework;prise;decoupling;assembly;dispatchproxy;proxy</PackageTags>
|
||||
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>default</LangVersion>
|
||||
<PackageLicenseUrl>https://raw.githubusercontent.com/merken/Prise/master/LICENSE</PackageLicenseUrl>
|
||||
<PackageProjectUrl>https://github.com/merken/Prise</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/merken/Prise.git</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target DependsOnTargets="ResolveReferences" Name="CopyProjectReferencesToPackage">
|
||||
<ItemGroup>
|
||||
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Text.Json" Version="4.6.1" />
|
||||
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="../Prise.Proxy/runtime/DispatchProxy.cs" Link="DispatchProxy.cs" />
|
||||
<Compile Include="../Prise.Proxy/runtime/DispatchProxyGenerator.cs" Link="DispatchProxyGenerator.cs" />
|
||||
<Compile Include="../Prise.Proxy/runtime/IngoreAccessChecksToAttributeBuilder.cs" Link="IngoreAccessChecksToAttributeBuilder.cs" />
|
||||
<Compile Include="../Prise.Proxy/PriseProxyException.cs" Link="PriseProxyException.cs" />
|
||||
<Compile Include="../Prise.Proxy/Infrastructure/IParameterConverter.cs" Link="IParameterConverter.cs" />
|
||||
<Compile Include="../Prise.Proxy/Infrastructure/IResultConverter.cs" Link="IResultConverter.cs" />
|
||||
<Compile Include="../Prise.Proxy/PassthroughParameterConverter.cs" Link="PassthroughParameterConverter.cs" />
|
||||
<Compile Include="../Prise.Proxy/PassthroughResultConverter.cs" Link="PassthroughResultConverter.cs" />
|
||||
<Compile Include="../Prise.Proxy/PriseProxy.cs" Link="PriseProxy.cs" />
|
||||
<Compile Include="../Prise.Proxy/Method.cs" Link="Method.cs" />
|
||||
<Compile Include="../Prise.Proxy/Parameter.cs" Link="Parameter.cs" />
|
||||
<Compile Include="../Prise.Proxy/MethodFindingStrategy.cs" Link="MethodFindingStrategy.cs" />
|
||||
<Compile Include="../Prise.Proxy/ResultConverter.cs" Link="ResultConverter.cs" />
|
||||
<Compile Include="../Prise.Proxy/TaskCompletionSource.cs" Link="TaskCompletionSource.cs" />
|
||||
<Compile Include="../Prise/Infrastructure/JsonSerializerParameterConverter.cs" Link="JsonSerializerParameterConverter.cs" />
|
||||
<Compile Include="../Prise/Infrastructure/JsonSerializerResultConverter.cs" Link="JsonSerializerResultConverter.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../icon.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath></PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
53
Vendor/Prise.ReverseProxy/ReverseProxy.cs
vendored
Normal file
53
Vendor/Prise.ReverseProxy/ReverseProxy.cs
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Prise.Infrastructure;
|
||||
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
/// <summary>
|
||||
/// The ReverseProxy is a base class that will provide a proxy to a Host Service from the Plugin (in reverse).
|
||||
/// </summary>
|
||||
public abstract class ReverseProxy
|
||||
{
|
||||
protected object hostService;
|
||||
protected ReverseProxy(object hostService)
|
||||
{
|
||||
this.hostService = hostService;
|
||||
}
|
||||
|
||||
private MethodBase GetCallingMethod() => new StackTrace().GetFrame(2).GetMethod();
|
||||
|
||||
/// <summary>
|
||||
/// This handles void proxy calls to the hostService
|
||||
/// </summary>
|
||||
/// <param name="parameters">The list of method parameters</param>
|
||||
protected void InvokeOnHostService(params object[] parameters)
|
||||
{
|
||||
var callingMethod = GetCallingMethod();
|
||||
var methodInfo = PriseProxy.FindMethodOnObject(callingMethod as MethodInfo, this);
|
||||
if (methodInfo.GetParameters().Count() != parameters.Count())
|
||||
throw new ReverseProxyException($"The number of parameters provided to this ReverseProxy {parameters?.Count()} do not match the actual parameter count of the hostService method ({methodInfo.GetParameters().Count()}). Did you forget to provide the correct number of parameters?");
|
||||
|
||||
this.Invoke(hostService, methodInfo, parameters ?? new object[] { });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This handles proxy calls to the hostService
|
||||
/// </summary>
|
||||
/// <param name="parameters">The list of method parameters</param>
|
||||
/// <typeparam name="T">Return reference type of the calling method</typeparam>
|
||||
/// <returns>The response of the invocation on the host object</returns>
|
||||
protected T InvokeOnHostService<T>(params object[] parameters)
|
||||
{
|
||||
var callingMethod = GetCallingMethod();
|
||||
var methodInfo = PriseProxy.FindMethodOnObject(callingMethod as MethodInfo, this);
|
||||
if (methodInfo.GetParameters().Count() != parameters.Count())
|
||||
throw new ReverseProxyException($"The number of parameters provided to this ReverseProxy {parameters?.Count()} do not match the actual parameter count of the hostService method ({methodInfo.GetParameters().Count()}). Did you forget to provide the correct number of parameters?");
|
||||
|
||||
return (T)this.Invoke(hostService, methodInfo, parameters ?? new object[] { });
|
||||
}
|
||||
|
||||
private object Invoke(object hostService, MethodInfo methodInfo, object[] parameters) => PriseProxy.Invoke(hostService, methodInfo, parameters ?? new object[] { }, new JsonSerializerParameterConverter(), new JsonSerializerResultConverter());
|
||||
}
|
||||
}
|
||||
13
Vendor/Prise.ReverseProxy/ReverseProxyException.cs
vendored
Normal file
13
Vendor/Prise.ReverseProxy/ReverseProxyException.cs
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Prise.Proxy
|
||||
{
|
||||
[System.Serializable]
|
||||
public class ReverseProxyException : System.Exception
|
||||
{
|
||||
public ReverseProxyException() { }
|
||||
public ReverseProxyException(string message) : base(message) { }
|
||||
public ReverseProxyException(string message, System.Exception inner) : base(message, inner) { }
|
||||
protected ReverseProxyException(
|
||||
System.Runtime.Serialization.SerializationInfo info,
|
||||
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
|
||||
}
|
||||
}
|
||||
29
Vendor/Prise.Testing/Prise.Testing.csproj
vendored
Normal file
29
Vendor/Prise.Testing/Prise.Testing.csproj
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Title>Prise.Testing</Title>
|
||||
<PackageId>Prise.Testing</PackageId>
|
||||
<PackageDescription>Testing support for your Prise Plugins!</PackageDescription>
|
||||
<Authors>Maarten Merken</Authors>
|
||||
<Company>MRKN</Company>
|
||||
<PackageTags>plugin;framework;prise;decoupling;assembly;dispatchproxy;proxy</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Prise.Plugin/Prise.Plugin.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../icon.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath></PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
35
Vendor/Prise.Testing/Testing.cs
vendored
Normal file
35
Vendor/Prise.Testing/Testing.cs
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Prise
|
||||
{
|
||||
public static class Testing
|
||||
{
|
||||
public static T CreateTestPluginInstance<T>(params object[] pluginServices)
|
||||
{
|
||||
var pluginType = typeof(T);
|
||||
var pluginInstance = typeof(T).Assembly.CreateInstance(typeof(T).FullName);
|
||||
var services = pluginType.GetTypeInfo().DeclaredFields.Where(f => f.CustomAttributes.Any(c => c.AttributeType.Name == typeof(Prise.Plugin.PluginServiceAttribute).Name));
|
||||
foreach (var service in services)
|
||||
{
|
||||
var serviceType = service.FieldType;
|
||||
var pluginService = pluginServices.FirstOrDefault(p => serviceType.IsAssignableFrom(p.GetType()));
|
||||
if (pluginService == null)
|
||||
throw new ArgumentException($"A pluginService of type {serviceType.Name} is required for activating plugin {pluginType.Name}.");
|
||||
|
||||
pluginInstance
|
||||
.GetType()
|
||||
.GetTypeInfo()
|
||||
.DeclaredFields
|
||||
.First(f => f.Name == service.Name)
|
||||
.SetValue(pluginInstance, pluginService);
|
||||
|
||||
var activationMethod = pluginType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).FirstOrDefault(m => m.CustomAttributes.Any(c => c.AttributeType.Name == typeof(Prise.Plugin.PluginActivatedAttribute).Name));
|
||||
activationMethod.Invoke(pluginInstance, null);
|
||||
}
|
||||
|
||||
return (T)pluginInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Vendor/Prise.Tests.Integration/.vscode/launch.json
vendored
Normal file
19
Vendor/Prise.Tests.Integration/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "IntegrationTestHost",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Prise.IntegrationTestsHost/bin/Debug/netcoreapp3.1/Prise.IntegrationTestsHost.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/Prise.IntegrationTestsHost",
|
||||
"console": "externalTerminal",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
]
|
||||
}
|
||||
17
Vendor/Prise.Tests.Integration/.vscode/tasks.json
vendored
Normal file
17
Vendor/Prise.Tests.Integration/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Prise.IntegrationTestsHost/Prise.IntegrationTestsHost.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
23
Vendor/Prise.Tests.Integration/FileBulkDateChanger/FileBulkDateChanger.deps.json
vendored
Normal file
23
Vendor/Prise.Tests.Integration/FileBulkDateChanger/FileBulkDateChanger.deps.json
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v3.1",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v3.1": {
|
||||
"FileBulkDateChanger/1.0.0": {
|
||||
"runtime": {
|
||||
"FileBulkDateChanger.dll": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"FileBulkDateChanger/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Vendor/Prise.Tests.Integration/FileBulkDateChanger/FileBulkDateChanger.dll
vendored
Normal file
BIN
Vendor/Prise.Tests.Integration/FileBulkDateChanger/FileBulkDateChanger.dll
vendored
Normal file
Binary file not shown.
BIN
Vendor/Prise.Tests.Integration/FileBulkDateChanger/FileBulkDateChanger.exe
vendored
Normal file
BIN
Vendor/Prise.Tests.Integration/FileBulkDateChanger/FileBulkDateChanger.exe
vendored
Normal file
Binary file not shown.
BIN
Vendor/Prise.Tests.Integration/FileBulkDateChanger/FileBulkDateChanger.pdb
vendored
Normal file
BIN
Vendor/Prise.Tests.Integration/FileBulkDateChanger/FileBulkDateChanger.pdb
vendored
Normal file
Binary file not shown.
10
Vendor/Prise.Tests.Integration/FileBulkDateChanger/FileBulkDateChanger.runtimeconfig.dev.json
vendored
Normal file
10
Vendor/Prise.Tests.Integration/FileBulkDateChanger/FileBulkDateChanger.runtimeconfig.dev.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"additionalProbingPaths": [
|
||||
"C:\\Users\\alper\\.dotnet\\store\\|arch|\\|tfm|",
|
||||
"C:\\Users\\alper\\.nuget\\packages",
|
||||
"C:\\Microsoft\\Xamarin\\NuGet",
|
||||
"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder"
|
||||
]
|
||||
}
|
||||
}
|
||||
9
Vendor/Prise.Tests.Integration/FileBulkDateChanger/FileBulkDateChanger.runtimeconfig.json
vendored
Normal file
9
Vendor/Prise.Tests.Integration/FileBulkDateChanger/FileBulkDateChanger.runtimeconfig.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "netcoreapp3.1",
|
||||
"framework": {
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "3.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
18
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/DomainForPluginC/IDiscount.cs
vendored
Normal file
18
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/DomainForPluginC/IDiscount.cs
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
namespace DomainForPluginC
|
||||
{
|
||||
public interface IDiscount
|
||||
{
|
||||
decimal Amount { get; }
|
||||
}
|
||||
|
||||
public class Discount : IDiscount
|
||||
{
|
||||
private readonly decimal amount;
|
||||
public Discount(decimal amount)
|
||||
{
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public decimal Amount => amount;
|
||||
}
|
||||
}
|
||||
23
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/DomainForPluginC/IDiscountService.cs
vendored
Normal file
23
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/DomainForPluginC/IDiscountService.cs
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace DomainForPluginC
|
||||
{
|
||||
public interface IDiscountService
|
||||
{
|
||||
decimal ApplyDiscount(decimal result);
|
||||
}
|
||||
|
||||
public class DiscountService : IDiscountService
|
||||
{
|
||||
private readonly IDiscount discount;
|
||||
public DiscountService(IDiscount discount)
|
||||
{
|
||||
this.discount = discount;
|
||||
|
||||
}
|
||||
public decimal ApplyDiscount(decimal result)
|
||||
{
|
||||
return result * this.discount.Amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginA/AdditionCalculationPlugin.cs
vendored
Normal file
64
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginA/AdditionCalculationPlugin.cs
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Prise.IntegrationTestsContract;
|
||||
using Prise.Plugin;
|
||||
|
||||
namespace PluginA
|
||||
{
|
||||
/// <summary>
|
||||
/// This plugin does not require any 3rd party dependencies or dependency injection,
|
||||
/// as long as a default parameterless constructor is present (implicitly or explicitly), this plugin will get loaded.
|
||||
/// </summary>
|
||||
[Plugin(PluginType = typeof(ICalculationPlugin))]
|
||||
public class AdditionCalculationPlugin : ICalculationPlugin
|
||||
{
|
||||
public string Name => nameof(AdditionCalculationPlugin);
|
||||
public string Description => "This plugin performs addition";
|
||||
public int Calculate(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public decimal Calculate(decimal a, decimal b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public decimal CalculateComplex(CalculationContext context)
|
||||
{
|
||||
return context.A + context.B;
|
||||
}
|
||||
|
||||
public CalculationResult CalculateComplexResult(CalculationContext context)
|
||||
{
|
||||
return new CalculationResult
|
||||
{
|
||||
Result = context.A + context.B
|
||||
};
|
||||
}
|
||||
|
||||
public ComplexCalculationResult CalculateMutiple(ComplexCalculationContext context)
|
||||
{
|
||||
var results = new List<CalculationResult>();
|
||||
results.AddRange(context.Calculations.Select(c => new CalculationResult { Result = c.A + c.B }));
|
||||
return new ComplexCalculationResult
|
||||
{
|
||||
Results = results.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ComplexCalculationResult> CalculateMutipleAsync(ComplexCalculationContext context)
|
||||
{
|
||||
var results = new List<CalculationResult>();
|
||||
results.AddRange(context.Calculations.Select(c => new CalculationResult { Result = c.A + c.B }));
|
||||
|
||||
await Task.Delay(2500);
|
||||
|
||||
return new ComplexCalculationResult
|
||||
{
|
||||
Results = results.ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginA/PluginA.csproj
vendored
Normal file
12
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginA/PluginA.csproj
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Prise.Plugin\Prise.Plugin.csproj" />
|
||||
<ProjectReference Include="..\..\Prise.IntegrationTestsContract\Prise.IntegrationTestsContract.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
13
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginA/PluginA.net2.csproj
vendored
Normal file
13
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginA/PluginA.net2.csproj
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AssemblyName>PluginA</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Prise.Plugin\Prise.Plugin.csproj" />
|
||||
<ProjectReference Include="..\..\Prise.IntegrationTestsContract\Prise.IntegrationTestsContract.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -0,0 +1,65 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Prise.IntegrationTestsContract;
|
||||
using Prise.Plugin;
|
||||
|
||||
namespace PluginA
|
||||
{
|
||||
/// <summary>
|
||||
/// By default, only the first plugin is loaded, the first in alphabethical order
|
||||
/// In order to execute the ZAdditionPlusOneCalculationPlugin, you need to have an IEnumerable<ICalculationPlugin> injected
|
||||
/// or inject a IPluginLoader<ICalculationPlugin> and call the .LoadAll() method.
|
||||
/// </summary>
|
||||
[Plugin(PluginType = typeof(ICalculationPlugin))]
|
||||
public class ZAdditionPlusOneCalculationPlugin : ICalculationPlugin
|
||||
{
|
||||
public string Name => nameof(ZAdditionPlusOneCalculationPlugin);
|
||||
public string Description => "This plugin performs addition +1";
|
||||
public int Calculate(int a, int b)
|
||||
{
|
||||
return a + b + 1;
|
||||
}
|
||||
|
||||
public decimal Calculate(decimal a, decimal b)
|
||||
{
|
||||
return a + b + 1;
|
||||
}
|
||||
|
||||
public decimal CalculateComplex(CalculationContext context)
|
||||
{
|
||||
return context.A + context.B + 1;
|
||||
}
|
||||
|
||||
public CalculationResult CalculateComplexResult(CalculationContext context)
|
||||
{
|
||||
return new CalculationResult
|
||||
{
|
||||
Result = context.A + context.B + 1
|
||||
};
|
||||
}
|
||||
|
||||
public ComplexCalculationResult CalculateMutiple(ComplexCalculationContext context)
|
||||
{
|
||||
var results = new List<CalculationResult>();
|
||||
results.AddRange(context.Calculations.Select(c => new CalculationResult { Result = c.A + c.B + 1 }));
|
||||
return new ComplexCalculationResult
|
||||
{
|
||||
Results = results.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ComplexCalculationResult> CalculateMutipleAsync(ComplexCalculationContext context)
|
||||
{
|
||||
var results = new List<CalculationResult>();
|
||||
results.AddRange(context.Calculations.Select(c => new CalculationResult { Result = c.A + c.B + 1 }));
|
||||
|
||||
await Task.Delay(2500);
|
||||
|
||||
return new ComplexCalculationResult
|
||||
{
|
||||
Results = results.ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginA/prise.plugin.json
vendored
Normal file
6
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginA/prise.plugin.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"publishDir": "../../_dist",
|
||||
"configuration": "Debug",
|
||||
"nuspecFile": null,
|
||||
"includeProjectNameInPublishDir": true
|
||||
}
|
||||
11
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginB/PluginB.csproj
vendored
Normal file
11
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginB/PluginB.csproj
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Prise.Plugin\Prise.Plugin.csproj" />
|
||||
<ProjectReference Include="..\..\Prise.IntegrationTestsContract\Prise.IntegrationTestsContract.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
12
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginB/PluginB.net2.csproj
vendored
Normal file
12
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginB/PluginB.net2.csproj
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AssemblyName>PluginB</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Prise.Plugin\Prise.Plugin.csproj" />
|
||||
<ProjectReference Include="..\..\Prise.IntegrationTestsContract\Prise.IntegrationTestsContract.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
65
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginB/SubtractionCalculationPlugin.cs
vendored
Normal file
65
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginB/SubtractionCalculationPlugin.cs
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Prise.IntegrationTestsContract;
|
||||
using Prise.Plugin;
|
||||
|
||||
namespace PluginB
|
||||
{
|
||||
// This class does not implement the ICalculationPlugin interface
|
||||
// Since all the methods are present, it will continue to work because the PluginAttribute is still present and the interface contract is respected
|
||||
// This improves backwards compatibility.
|
||||
[Plugin(PluginType = typeof(ICalculationPlugin))]
|
||||
public class SubtractionCalculationPlugin
|
||||
{
|
||||
public string Name => nameof(SubtractionCalculationPlugin);
|
||||
|
||||
// Property Description will not be implemented in this plugin, all other methods can still be called
|
||||
// public string Description => "This plugin performs subtraction";
|
||||
// However, you could expose it this way:
|
||||
// public string get_Description() => "This plugin performs subtraction";
|
||||
public int Calculate(int a, int b)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
|
||||
public decimal Calculate(decimal a, decimal b)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
|
||||
public decimal CalculateComplex(CalculationContext context)
|
||||
{
|
||||
return context.A - context.B;
|
||||
}
|
||||
|
||||
public CalculationResult CalculateComplexResult(CalculationContext context)
|
||||
{
|
||||
return new CalculationResult { Result = context.A - context.B };
|
||||
}
|
||||
|
||||
public ComplexCalculationResult CalculateMutiple(ComplexCalculationContext context)
|
||||
{
|
||||
var results = new List<CalculationResult>();
|
||||
results.AddRange(context.Calculations.Select(c => new CalculationResult { Result = c.A - c.B }));
|
||||
return new ComplexCalculationResult
|
||||
{
|
||||
Results = results.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ComplexCalculationResult> CalculateMutipleAsync(ComplexCalculationContext context)
|
||||
{
|
||||
var results = new List<CalculationResult>();
|
||||
results.AddRange(context.Calculations.Select(c => new CalculationResult { Result = c.A - c.B }));
|
||||
|
||||
await Task.Delay(2500);
|
||||
|
||||
return new ComplexCalculationResult
|
||||
{
|
||||
Results = results.ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginB/prise.plugin.json
vendored
Normal file
6
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginB/prise.plugin.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"publishDir": "../../_dist",
|
||||
"configuration": "Debug",
|
||||
"nuspecFile": null,
|
||||
"includeProjectNameInPublishDir": true
|
||||
}
|
||||
31
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginC/Calculations/ICanCalculate.cs
vendored
Normal file
31
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginC/Calculations/ICanCalculate.cs
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
using DomainForPluginC;
|
||||
|
||||
namespace PluginC.Calculations
|
||||
{
|
||||
public interface ICanCalculate
|
||||
{
|
||||
decimal DoCalculation(decimal a, decimal b);
|
||||
}
|
||||
|
||||
public class DivideCalculation : ICanCalculate
|
||||
{
|
||||
public decimal DoCalculation(decimal a, decimal b)
|
||||
{
|
||||
return a / b;
|
||||
}
|
||||
}
|
||||
|
||||
public class MultiplyCalculation : ICanCalculate
|
||||
{
|
||||
private readonly IDiscountService discountService;
|
||||
public MultiplyCalculation(IDiscountService discountService)
|
||||
{
|
||||
this.discountService = discountService;
|
||||
}
|
||||
|
||||
public decimal DoCalculation(decimal a, decimal b)
|
||||
{
|
||||
return this.discountService.ApplyDiscount((a * b));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using DomainForPluginC;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using PluginC.Calculations;
|
||||
using Prise.Plugin;
|
||||
|
||||
namespace PluginC
|
||||
{
|
||||
[PluginBootstrapper(PluginType = typeof(DivideOrMultiplyCalculationPlugin))]
|
||||
public class DivideOrMultiplyCalculationBootstrapper : IPluginBootstrapper
|
||||
{
|
||||
public IServiceCollection Bootstrap(IServiceCollection services)
|
||||
{
|
||||
// Discount and DiscountService come from a third party assembly called Domain
|
||||
// Add a fixed discount of 10%
|
||||
services.AddSingleton<IDiscount>(new Discount(1.10m));
|
||||
services.AddScoped<IDiscountService, DiscountService>();
|
||||
|
||||
// Randomly choose what service to use
|
||||
// var random = new Random();
|
||||
// if (random.Next() % 2 == 0)
|
||||
// services.AddScoped<ICanCalculate, DivideCalculation>();
|
||||
// else
|
||||
// services.AddScoped<ICanCalculate, MultiplyCalculation>();
|
||||
|
||||
services.AddScoped<ICanCalculate, MultiplyCalculation>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Prise.IntegrationTestsContract;
|
||||
using PluginC.Calculations;
|
||||
using Prise.Plugin;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PluginC
|
||||
{
|
||||
// This plugin will Divide or Multiple, who knows, it's always a guess.
|
||||
// The decision is made in a service, which dependend a discount from a third party dependency that no other plugin shares
|
||||
[Plugin(PluginType = typeof(ICalculationPlugin))]
|
||||
public class DivideOrMultiplyCalculationPlugin : ICalculationPlugin
|
||||
{
|
||||
public string Name => nameof(DivideOrMultiplyCalculationPlugin);
|
||||
|
||||
public string Description => $"This plugin performs division OR multiplication, check out {nameof(DivideOrMultiplyCalculationBootstrapper)} for more details";
|
||||
private readonly ICanCalculate calculation;
|
||||
|
||||
internal DivideOrMultiplyCalculationPlugin(ICanCalculate calculation)
|
||||
{
|
||||
this.calculation = calculation;
|
||||
}
|
||||
|
||||
public int Calculate(int a, int b)
|
||||
{
|
||||
return (int)this.calculation.DoCalculation(a, b);
|
||||
}
|
||||
|
||||
public decimal Calculate(decimal a, decimal b)
|
||||
{
|
||||
return this.calculation.DoCalculation(a, b);
|
||||
}
|
||||
|
||||
public decimal CalculateComplex(CalculationContext context)
|
||||
{
|
||||
return this.calculation.DoCalculation(context.A, context.B);
|
||||
}
|
||||
|
||||
public CalculationResult CalculateComplexResult(CalculationContext context)
|
||||
{
|
||||
return new CalculationResult { Result = this.calculation.DoCalculation(context.A, context.B) };
|
||||
}
|
||||
|
||||
public ComplexCalculationResult CalculateMutiple(ComplexCalculationContext context)
|
||||
{
|
||||
var results = new List<CalculationResult>();
|
||||
results.AddRange(context.Calculations.Select(c => new CalculationResult { Result = this.calculation.DoCalculation(c.A, c.B) }));
|
||||
return new ComplexCalculationResult
|
||||
{
|
||||
Results = results.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ComplexCalculationResult> CalculateMutipleAsync(ComplexCalculationContext context)
|
||||
{
|
||||
var results = new List<CalculationResult>();
|
||||
results.AddRange(context.Calculations.Select(c => new CalculationResult { Result = this.calculation.DoCalculation(c.A, c.B) }));
|
||||
|
||||
await Task.Delay(2500);
|
||||
|
||||
return new ComplexCalculationResult
|
||||
{
|
||||
Results = results.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
[PluginFactory]
|
||||
public static ICalculationPlugin ThisNameDoesNotMatterFactoryMethod(IServiceProvider serviceProvider)
|
||||
{
|
||||
return new DivideOrMultiplyCalculationPlugin((ICanCalculate)serviceProvider.GetService(typeof(ICanCalculate)));
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginC/PluginC.csproj
vendored
Normal file
12
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginC/PluginC.csproj
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Prise.Plugin\Prise.Plugin.csproj" />
|
||||
<ProjectReference Include="..\..\Prise.IntegrationTestsContract\Prise.IntegrationTestsContract.csproj" />
|
||||
<ProjectReference Include="..\DomainForPluginC\DomainForPluginC.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
13
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginC/PluginC.net2.csproj
vendored
Normal file
13
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginC/PluginC.net2.csproj
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AssemblyName>PluginC</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Prise.Plugin\Prise.Plugin.csproj" />
|
||||
<ProjectReference Include="..\..\Prise.IntegrationTestsContract\Prise.IntegrationTestsContract.csproj" />
|
||||
<ProjectReference Include="..\DomainForPluginC\DomainForPluginC.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
6
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginC/prise.plugin.json
vendored
Normal file
6
Vendor/Prise.Tests.Integration/IntegrationTestsPlugins/PluginC/prise.plugin.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"publishDir": "../../_dist",
|
||||
"configuration": "Debug",
|
||||
"nuspecFile": null,
|
||||
"includeProjectNameInPublishDir": true
|
||||
}
|
||||
12
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/AppHostCollection.cs
vendored
Normal file
12
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/AppHostCollection.cs
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Prise.IntegrationTests
|
||||
{
|
||||
[CollectionDefinition("AppHost collection")]
|
||||
public class AppHostCollection : ICollectionFixture<AppHostWebApplicationFactory>
|
||||
{
|
||||
// This class has no code, and is never created. Its purpose is simply
|
||||
// to be the place to apply [CollectionDefinition] and all the
|
||||
// ICollectionFixture<> interfaces.
|
||||
}
|
||||
}
|
||||
27
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/AppHostWebApplicationFactory.cs
vendored
Normal file
27
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/AppHostWebApplicationFactory.cs
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Prise.IntegrationTestsHost;
|
||||
|
||||
namespace Prise.IntegrationTests
|
||||
{
|
||||
public class CommandLineArgumentsLazy : ICommandLineArguments
|
||||
{
|
||||
public bool UseLazyService { get; set; }
|
||||
public bool UseCollectibleAssemblies { get; set; }
|
||||
}
|
||||
|
||||
public partial class AppHostWebApplicationFactory
|
||||
: WebApplicationFactory<Prise.IntegrationTestsHost.Startup>
|
||||
{
|
||||
internal static AppHostWebApplicationFactory _instance = new AppHostWebApplicationFactory(new CommandLineArgumentsLazy(), null);
|
||||
internal static AppHostWebApplicationFactory Default() => _instance;
|
||||
|
||||
private readonly Dictionary<string, string> settings;
|
||||
private readonly ICommandLineArguments commandLineArguments;
|
||||
public AppHostWebApplicationFactory(ICommandLineArguments commandLineArguments, Dictionary<string, string> settings = null)
|
||||
{
|
||||
this.commandLineArguments = commandLineArguments;
|
||||
this.settings = settings;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/AppHostWebApplicationFactory.netcore2.cs
vendored
Normal file
24
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/AppHostWebApplicationFactory.netcore2.cs
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
using Prise.IntegrationTestsHost;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Prise.IntegrationTests
|
||||
{
|
||||
public partial class AppHostWebApplicationFactory
|
||||
: WebApplicationFactory<Prise.IntegrationTestsHost.Startup>
|
||||
{
|
||||
#if NETCORE2_1
|
||||
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||
{
|
||||
builder.ConfigureAppConfiguration((c, b) => b.AddInMemoryCollection(this.settings));
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
services.AddSingleton<ICommandLineArguments>((s) => this.commandLineArguments);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
27
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/AppHostWebApplicationFactory.netcore3.cs
vendored
Normal file
27
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/AppHostWebApplicationFactory.netcore3.cs
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
using Prise.IntegrationTestsHost;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Prise.IntegrationTests
|
||||
{
|
||||
public partial class AppHostWebApplicationFactory
|
||||
: WebApplicationFactory<Prise.IntegrationTestsHost.Startup>
|
||||
{
|
||||
#if NETCORE3_1
|
||||
protected override IHostBuilder CreateHostBuilder()
|
||||
{
|
||||
return Host.CreateDefaultBuilder()
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
services.AddSingleton<ICommandLineArguments>((s) => this.commandLineArguments))
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Prise.IntegrationTestsHost.Startup>();
|
||||
webBuilder.ConfigureAppConfiguration(builder => builder.AddInMemoryCollection(this.settings));
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
30
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/BreakTheServerTests.cs
vendored
Normal file
30
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/BreakTheServerTests.cs
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Prise.IntegrationTests
|
||||
{
|
||||
#if NETCORE3_1
|
||||
public class BreakTheServerTests : CalculationPluginTestsBase
|
||||
{
|
||||
public BreakTheServerTests() : base(AppHostWebApplicationFactory.Default()) { }
|
||||
|
||||
[Fact]
|
||||
public async Task BreakWithLoop()
|
||||
{
|
||||
var tasks = new List<Task<string>>();
|
||||
for (var i = 0; i < 250; i++)
|
||||
{
|
||||
tasks.Add(GetRaw(_client, "PluginA", "/disco"));
|
||||
}
|
||||
|
||||
var results = await Task.WhenAll(tasks.ToArray());
|
||||
|
||||
Assert.All<string>(results, s => Assert.Equal("AdditionCalculationPlugin,ZAdditionPlusOneCalculationPlugin", s));
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
36
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/CalculationPluginTestsBase.cs
vendored
Normal file
36
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/CalculationPluginTestsBase.cs
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Prise.IntegrationTests
|
||||
{
|
||||
public abstract class CalculationPluginTestsBase : PluginTestBase
|
||||
{
|
||||
protected CalculationPluginTestsBase(AppHostWebApplicationFactory factory) : base(factory) { }
|
||||
|
||||
protected async Task<T> Post<T>(HttpClient client, string pluginType, string endpoint, object content)
|
||||
{
|
||||
client.DefaultRequestHeaders.Add("PluginType", pluginType);
|
||||
var response = await client.PostAsync(endpoint, new StringContent(JsonConvert.SerializeObject(content), Encoding.UTF8,
|
||||
"application/json"));
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new Exception("Result was not success!");
|
||||
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
return JsonConvert.DeserializeObject<T>(responseContent);
|
||||
}
|
||||
|
||||
protected async Task<string> GetRaw(HttpClient client, string pluginType, string endpoint)
|
||||
{
|
||||
client.DefaultRequestHeaders.Add("PluginType", pluginType);
|
||||
var response = await client.GetAsync(endpoint);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new Exception("Result was not success!");
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
329
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/CalculationTests.cs
vendored
Normal file
329
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/CalculationTests.cs
vendored
Normal file
@ -0,0 +1,329 @@
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Prise.IntegrationTestsHost.Models;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Prise.IntegrationTests
|
||||
{
|
||||
public class CalculationTests : CalculationPluginTestsBase
|
||||
{
|
||||
public CalculationTests() : base(AppHostWebApplicationFactory.Default()) { }
|
||||
|
||||
[Fact]
|
||||
public async Task PluginA_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 100,
|
||||
B = 150
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginA", "/calculation", payload);
|
||||
|
||||
// Assert 100 + 150
|
||||
Assert.Equal(250, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginB_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 150,
|
||||
B = 50
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginB", "/calculation", payload);
|
||||
|
||||
// Assert 150 - 50
|
||||
Assert.Equal(100, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginC_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 50,
|
||||
B = 2
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginC", "/calculation", payload);
|
||||
|
||||
// Assert 50 * 2 + 10% discount
|
||||
Assert.Equal(110, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginA_int_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 100,
|
||||
B = 150
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginA", "/calculation/int", payload);
|
||||
|
||||
// Assert 100 + 150
|
||||
Assert.Equal(250, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginB_int_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 150,
|
||||
B = 50
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginB", "/calculation/int", payload);
|
||||
|
||||
// Assert 150 - 50
|
||||
Assert.Equal(100, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginC_int_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 50,
|
||||
B = 2
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginC", "/calculation/int", payload);
|
||||
|
||||
// Assert 50 * 2 + 10% discount
|
||||
Assert.Equal(110, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginA_complex_input_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 100,
|
||||
B = 150
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginA", "/calculation/complex-input", payload);
|
||||
|
||||
// Assert 100 + 150
|
||||
Assert.Equal(250, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginB_complex_input_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 150,
|
||||
B = 50
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginB", "/calculation/complex-input", payload);
|
||||
|
||||
// Assert 150 - 50
|
||||
Assert.Equal(100, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginC_complex_input_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 50,
|
||||
B = 2
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginC", "/calculation/complex-input", payload);
|
||||
|
||||
// Assert 50 * 2 + 10% discount
|
||||
Assert.Equal(110, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginA_complex_output_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 100,
|
||||
B = 150
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginA", "/calculation/complex-output", payload);
|
||||
|
||||
// Assert 100 + 150
|
||||
Assert.Equal(250, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginB_complex_output_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 150,
|
||||
B = 50
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginB", "/calculation/complex-output", payload);
|
||||
|
||||
// Assert 150 - 50
|
||||
Assert.Equal(100, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginC_complex_output_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 50,
|
||||
B = 2
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginC", "/calculation/complex-output", payload);
|
||||
|
||||
// Assert 50 * 2 + 10% discount
|
||||
Assert.Equal(110, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginA_multi_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestMultiModel
|
||||
{
|
||||
Calculations = new[]
|
||||
{
|
||||
new CalculationRequestModel
|
||||
{
|
||||
A = 100,
|
||||
B = 150
|
||||
},
|
||||
new CalculationRequestModel
|
||||
{
|
||||
A = 100,
|
||||
B = 150
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginA", "/calculation/multi", payload);
|
||||
|
||||
// Assert 100 + 150 + 100 + 150
|
||||
Assert.Equal(500, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginB_multi_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestMultiModel
|
||||
{
|
||||
Calculations = new[]
|
||||
{
|
||||
new CalculationRequestModel
|
||||
{
|
||||
A = 50,
|
||||
B = 5
|
||||
},
|
||||
new CalculationRequestModel
|
||||
{
|
||||
A = 40,
|
||||
B = 5
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginB", "/calculation/multi", payload);
|
||||
|
||||
// Assert (50 - 5) + (40 - 5)
|
||||
Assert.Equal(80, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginC_multi_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestMultiModel
|
||||
{
|
||||
Calculations = new[]
|
||||
{
|
||||
new CalculationRequestModel
|
||||
{
|
||||
A = 50,
|
||||
B = 2
|
||||
},
|
||||
new CalculationRequestModel
|
||||
{
|
||||
A = 40,
|
||||
B = 2
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginC", "/calculation/multi", payload);
|
||||
|
||||
// Assert (50 * 2 + 10% discount) + (40 * 2 + 10% discount)
|
||||
Assert.Equal(198, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginC_multi_async_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestMultiModel
|
||||
{
|
||||
Calculations = new[]
|
||||
{
|
||||
new CalculationRequestModel
|
||||
{
|
||||
A = 50,
|
||||
B = 2
|
||||
},
|
||||
new CalculationRequestModel
|
||||
{
|
||||
A = 40,
|
||||
B = 2
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginC", "/calculation/multi-async", payload);
|
||||
|
||||
// Assert (50 * 2 + 10% discount) + (40 * 2 + 10% discount)
|
||||
Assert.Equal(198, result.Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/DiscoTests.cs
vendored
Normal file
61
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/DiscoTests.cs
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Prise.IntegrationTests
|
||||
{
|
||||
public class DiscoTests : CalculationPluginTestsBase
|
||||
{
|
||||
public DiscoTests() : base(AppHostWebApplicationFactory.Default()) { }
|
||||
|
||||
[Fact]
|
||||
public async Task PluginA_Works()
|
||||
{
|
||||
// Arrange, Act
|
||||
var result = await GetRaw(_client, "PluginA", "/disco");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("AdditionCalculationPlugin,ZAdditionPlusOneCalculationPlugin", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginA_Description_Works()
|
||||
{
|
||||
// Arrange, Act
|
||||
|
||||
var result = await GetRaw(_client, "PluginA", "/disco/description");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("This plugin performs addition,This plugin performs addition +1", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginB_Works()
|
||||
{
|
||||
// Arrange, Act
|
||||
var result = await GetRaw(_client, "PluginB", "/disco");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("SubtractionCalculationPlugin", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginC_Works()
|
||||
{
|
||||
// Arrange, Act
|
||||
var result = await GetRaw(_client, "PluginC", "/disco");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("DivideOrMultiplyCalculationPlugin", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginC_Description_Works()
|
||||
{
|
||||
// Arrange, Act
|
||||
var result = await GetRaw(_client, "PluginC", "/disco/description");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("This plugin performs division OR multiplication, check out DivideOrMultiplyCalculationBootstrapper for more details", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/MultipleTests.cs
vendored
Normal file
64
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/MultipleTests.cs
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Prise.IntegrationTestsHost.Models;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Prise.IntegrationTests
|
||||
{
|
||||
public class MultipleTests : CalculationPluginTestsBase
|
||||
{
|
||||
public MultipleTests() : base(AppHostWebApplicationFactory.Default()) { }
|
||||
|
||||
[Fact]
|
||||
public async Task PluginA_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 100,
|
||||
B = 150
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginA", "/multiple", payload);
|
||||
|
||||
// Assert (100 + 150) + (100 + 150 + 1)
|
||||
Assert.Equal(501, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginB_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 150,
|
||||
B = 50
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginB", "/multiple", payload);
|
||||
|
||||
// Assert 150 - 50
|
||||
Assert.Equal(100, result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginC_Works()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 50,
|
||||
B = 2
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await Post<CalculationResponseModel>(_client, "PluginC", "/multiple", payload);
|
||||
|
||||
// Assert 50 * 2 + 10% discount
|
||||
Assert.Equal(110, result.Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/PluginTestBase.cs
vendored
Normal file
37
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/PluginTestBase.cs
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Newtonsoft.Json;
|
||||
using Prise.IntegrationTestsContract;
|
||||
|
||||
namespace Prise.IntegrationTests
|
||||
{
|
||||
public abstract class PluginTestBase
|
||||
{
|
||||
protected readonly HttpClient _client;
|
||||
protected readonly AppHostWebApplicationFactory _factory;
|
||||
|
||||
protected PluginTestBase(
|
||||
AppHostWebApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
var local = Environment.GetEnvironmentVariable("LOCAL") == "true";
|
||||
if (local)
|
||||
{
|
||||
_client = new HttpClient();
|
||||
_client.BaseAddress = new Uri("https://localhost:5001");
|
||||
}
|
||||
else
|
||||
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
|
||||
{
|
||||
AllowAutoRedirect = false,
|
||||
BaseAddress = new Uri("https://localhost:5001")
|
||||
});
|
||||
_client.Timeout = new TimeSpan(0, 5, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/Prise.IntegrationTests.csproj
vendored
Normal file
25
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/Prise.IntegrationTests.csproj
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<DefineConstants>NETCORE3_1</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
||||
<PackageReference Include="Xunit.SkippableFact" Version="1.3.12" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Prise.IntegrationTestsHost\Prise.IntegrationTestsHost.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
10
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/Properties/launchSettings.json
vendored
Normal file
10
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/Properties/launchSettings.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Prise.IntegrationTests": {
|
||||
"commandName": "Project",
|
||||
"environmentVariables": {
|
||||
"LOCAL": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/SadPathTests.cs
vendored
Normal file
45
Vendor/Prise.Tests.Integration/Prise.IntegrationTests/SadPathTests.cs
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Prise.IntegrationTestsHost.Models;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Prise.IntegrationTests
|
||||
{
|
||||
public class SadPathTests : CalculationPluginTestsBase
|
||||
{
|
||||
public SadPathTests() : base(AppHostWebApplicationFactory.Default()) { }
|
||||
|
||||
[Fact]
|
||||
public async Task PluginZ_DoesNotExists()
|
||||
{
|
||||
// Arrange
|
||||
var payload = new CalculationRequestModel
|
||||
{
|
||||
A = 100,
|
||||
B = 150
|
||||
};
|
||||
|
||||
//Act
|
||||
#if NETCORE3_1
|
||||
await Assert.ThrowsAsync<System.IO.DirectoryNotFoundException>(async () => await Post<CalculationResponseModel>(_client, "PluginZ", "/calculation", payload));
|
||||
#endif
|
||||
#if NETCORE2_1
|
||||
await Assert.ThrowsAsync<System.Exception>(async () => await Post<CalculationResponseModel>(_client, "PluginZ", "/calculation", payload));
|
||||
#endif
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PluginB_Description_Does_Not_Work()
|
||||
{
|
||||
// Arrange, Act
|
||||
#if NETCORE3_1
|
||||
await Assert.ThrowsAsync<Prise.Proxy.PriseProxyException>(async () => await GetRaw(_client, "PluginB", "/disco/description"));
|
||||
#endif
|
||||
#if NETCORE2_1
|
||||
await Assert.ThrowsAsync<System.Exception>(async () => await GetRaw(_client, "PluginB", "/disco/description"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user