Zongsoft.Plugins 7.22.0

dotnet add package Zongsoft.Plugins --version 7.22.0
                    
NuGet\Install-Package Zongsoft.Plugins -Version 7.22.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Zongsoft.Plugins" Version="7.22.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Zongsoft.Plugins" Version="7.22.0" />
                    
Directory.Packages.props
<PackageReference Include="Zongsoft.Plugins" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Zongsoft.Plugins --version 7.22.0
                    
#r "nuget: Zongsoft.Plugins, 7.22.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Zongsoft.Plugins@7.22.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Zongsoft.Plugins&version=7.22.0
                    
Install as a Cake Addin
#tool nuget:?package=Zongsoft.Plugins&version=7.22.0
                    
Install as a Cake Tool

Zongsoft.Plugins Plugin Framework

License NuGet Version NuGet Downloads GitHub Stars

English | 简体中文


Overview

Zongsoft.Plugins is the application composition layer of the Zongsoft framework. A small host loads declarative plugin manifests, builds an extension tree, registers services, and starts application modules. Business libraries can remain independent of the plugin runtime; only the host and composition package need to understand plugin loading.

The framework supports terminal applications, background services, Web hosts, and rich clients. It is designed for systems whose features must be packaged, enabled, replaced, or deployed independently.

Core Concepts

Plugin Manifest

A *.plugin file is an XML composition contract. It identifies a plugin, declares assembly and plugin dependencies, and contributes objects to extension paths. The format is described by Zongsoft.Plugins.xsd. Extensions are optional and repeatable. The manifest, builders and parsers sections are each optional and may occur at most once. When present, builders must contain at least one builder, and parsers at least one parser. Sections follow the order manifest → builders → parsers → extension. The schema validates standard declarations and nested object/expose components while allowing dynamic properties and custom builders. Missing-schema warnings for member elements such as QueueServer.Storages indicate the limits of static validation; see the validation boundary.

Plugin Tree

Extensions are organized as a path-addressable tree. Framework services are commonly mounted under /Workbench; for example, the main manifest exposes application modules, services, the file-system registry, events, the command executor, connection-setting drivers, and diagnostic loggers.

Builders and Parsers

Builders create nodes such as object and expose. Parsers resolve values such as path, type, static, option, service, and resource. A value expression is evaluated when its node is built, so dependency order and path context matter. Property assignment resolves expressions or referenced nodes before converting the result with the target member type and converter. This also applies to string and object properties; expressions such as {option:...} are not assigned as raw text. The resolved value may already be cached before the member type and converter are known, so assignment still attempts conversion. A null result is preserved rather than converted to a value type's default, which could mask a failed conversion.

Application Context

PluginApplicationContext represents the running application and exposes its environment, modules, services, events, workers, and current principal. Host-specific packages provide the concrete context while business modules interact through Core application abstractions.

Installation

dotnet add package Zongsoft.Plugins

A deployable host also needs the main plugin manifest. The NuGet package includes the standard deployment artifacts; place deployed *.plugin files under the application's plugins directory.

Creating a Host

The hosting helpers integrate plugin initialization with .NET Generic Host:

using Microsoft.Extensions.Hosting;
using Zongsoft.Plugins.Hosting;

await (await Application.TerminalAsync(args)).RunAsync();

Use Application.DaemonAsync(args) for a background service. Web applications use the companion Zongsoft.Plugins.Web package.

The builder loads application configuration, discovers the plugin directory, builds the plugin tree, registers modules and services, initializes the application context, and then starts the host.

Terminal signals and shutdown

The default lifetime used by Application.TerminalAsync registers SIGINT, SIGQUIT and SIGTERM during startup and removes registrations when the host is asynchronously disposed. Core terminals only provide the ITerminal.Abort() notification.

  • Ctrl+C / SIGINT first notifies the running executor's terminal. Setting Aborting arguments to Cancel = true keeps the host running; an unhandled request stops it. ReactiveAsync uses this notification to finish its wait, await cleanup and resume accepting commands.
  • Ctrl+Break / SIGQUIT and SIGTERM directly request host shutdown and cannot be vetoed by Aborting. Shutdown cancels the terminal run token and waits for the input caller, command cleanup and workers to finish. The process-wide standard input read does not delay host shutdown.
  • Repeated signals during shutdown neither repeat abort notifications nor force immediate termination. Abort handlers must return promptly and must not wait synchronously for their own host to stop. Handler failures request shutdown and propagate through StopAsync and asynchronous disposal.

An explicitly registered IHostLifetime is preserved and owns its signal policy. One active host should coordinate console signals for the process. Status messages use the Microsoft.Hosting.Lifetime logging category and honor application logging providers, filters and ConsoleLifetimeOptions.SuppressStatusMessages. The terminal host does not change the application's console input mode.

Use RunAsync, or explicitly call StartAsync, StopAsync and asynchronously dispose the host, to await cleanup. Forced operating-system termination is outside cooperative shutdown.

Declaring a Plugin

Use the real Discussions business manifest. This excerpt shows assemblies, dependencies and module nodes; it is not the complete manifest. Preserve the original validators, data filters and identity extensions when deploying:

<manifest>
	<assemblies>
		<assembly name="Zongsoft.Discussions" />
	</assemblies>
	<dependencies>
		<dependency name="Zongsoft.Data" />
		<dependency name="Zongsoft.Security" />
	</dependencies>
</manifest>

<extension path="/Workbench/Modules">
	<object name="Discussions" value="{static:Zongsoft.Discussions.Module.Current, Zongsoft.Discussions}">
		<expose name="Accessor" value="{path:../@Accessor}">
			<expose name="Filters" value="{path:../@Filters}" />
		</expose>
		<expose name="Events" value="{path:../@Events}" />
		<expose name="Properties" value="{path:../@Properties}" />
	</object>
</extension>

The plugin is named Zongsoft.Discussions; its module is Discussions. These names are not interchangeable. See the project file and Module.cs. The business library references Core contracts; runtime plugin dependencies supply data-engine and security implementations.

🚨 Do not replace the complete manifest with this excerpt: that would omit site constraints, post filtering and identity transformation. A successful build does not prove that dependency plugins, assemblies and configuration were deployed correctly.

Plugin Deployment: From Host to Running Features

Three Separate Responsibilities

Step What it does What it does not do
NuGet/project reference Compiles the host or a business library against APIs Does not assemble the host's plugin directory
Deployment Copies manifests, assemblies, dependencies, options, mappings and resources to their runtime locations Does not start the application or initialize a database
Plugin loading Reads manifests, resolves dependencies, builds nodes and registers services in the host Does not download missing packages

A host is a launcher, not a business module. Ready-made terminal, daemon and Web launchers are maintained in Zongsoft/hosting. Their deployment manifests compose features independently of the host's source code.

Deployment Composition from Real Projects

Main.plugin and Terminal.plugin are the framework's existing base manifests. Discussions ships its forum module through the domain deployment manifest and Web deployment manifest.

Add this composition fragment to a compatible existing test host's deployment plan. It specifies destinations for the two business packages, not the host's complete manifest:

[plugins zongsoft discussions]
nuget:Zongsoft.Discussions

[plugins zongsoft discussions web]
nuget:Zongsoft.Discussions.Web

The domain package contains Zongsoft.Discussions.dll and matching .plugin, .option, and .mapping files. The Web package contains Zongsoft.Discussions.Web.dll, its manifest and archive templates. Their package-root .deploy files copy from artifacts/ and lib/${Framework}/; these are package paths, not source-directory paths.

Adding the Business Plugin

The host owns startup, Discussions owns the forum domain, and the Web package owns HTTP adaptation. Explicitly compose Data, Security, a database driver and the selected file-storage provider too; the two business-package entries alone are not a complete dependency set. Connection, site and storage prerequisites are explained in the real Data workflow.

Filesystem nesting differs from logical extension paths such as /Workbench/Modules. Preserve deployment layout and manifest dependencies. After startup, inspect the plugin tree and the Discussions module before exercising actual APIs; do not hard-code business dependencies into the host entry point.

💡 Use an isolated deployment, test database and test identity. Startup and deployment commands belong to the existing host guide and deployment tool; this guide does not introduce a host project absent from the repository.

Deployment Manifest versus Source Build

A package's root .deploy normally refers to package-relative artifacts/ and lib/${Framework}/. Execute it through a nuget:Package entry; copying this text into a source directory does not make those package paths exist.

For local debugging, stop the test host, build the matching configuration/TFM, then copy the changed assembly plus matching manifest/options/mappings/resources to its existing plugin location and restart. A local application .deploy may instead reference explicit source build output paths. Do not run a whole deployment merely to replace one debug DLL: it may overwrite local configuration or remove manually installed files.

The deployer prefers a package-root .deploy when no package-internal path is specified. It selects compatible TFM assets and processes nested entries. Its default dependency downloader skips System.*, Microsoft.Extensions.* and Zongsoft.*; therefore explicitly compose required Zongsoft plugins and keep the host runtime dependencies compatible.

Configuration Is Part of the Composition

The plugin configuration provider selects one entry beside each manifest: first the .option with the same basename as the .plugin file, then <plugin name="…">.option only if the first file is absent. For example, Feature-daemon.plugin with plugin name Feature.Daemon selects Feature-daemon.option, falling back to Feature.Daemon.option. If neither exists, no configuration is loaded. The entries are not merged; an empty or invalid first entry does not trigger fallback. Invalid XML fails loading.

The provider does not scan suffixes, environment names, host names, sites or subdirectories. Fragments require explicit <?import path="…" required="false"?> instructions, with paths relative to the declaring file. Imports at the end override earlier keys; optional imports permit missing files, not parse errors. Host LoadConfiguration("web") loads only web.option, respecting the configured file provider and base path. After adding or removing a plugin entry, call the provider's Load() or restart to select it again. Existing entry and imported-file changes use the XML provider's notifications. See the provider source.

Initial loading and explicit Load() include every loaded descendant plugin. Loading, unloading, full reloads and reloads of associated files notify the host configuration and supply a fresh token for the next change. Replacing, unloading or disposing providers detaches old file subscriptions. A failed full reload clears providers created during that attempt; callers must handle the exception before retrying. Serialize full reloads, plugin tree loading/unloading and disposal. Notifications do not make multiple file updates atomic, add listeners or replace assemblies.

Service scanning runs after the complete plugin tree and its options have loaded. Loaded entry references, the entry assembly and plugin manifests share one deduplication set, so each assembly registers once per host build. IServiceRegistration.Register can read descendant plugin configuration. Protocol extensions can therefore register before the host creates its network endpoint snapshot.

Deployment variables and runtime options are different. --site:daemon passed to the deployer can select a *-daemon.plugin artifact; site=daemon passed to the host remains an application context setting and does not select option files. Some plugins use that extra manifest to start workers. Merely installing the primary DLL does not enable such workers.

Do not depend on an incidental ordering of multiple plugins defining the same configuration key. Give environment/site settings a clear owner and verify the effective value without printing secrets.

To diagnose conflicts, read each plugin's effective value through PluginConfigurationProvider.Providers. Each entry is a CompositeConfigurationProvider containing one XML provider for the selected entry and its imports. For a FileConfigurationProvider named file, obtain the entry's local path from file.Source.FileProvider.GetFileInfo(file.Source.Path).PhysicalPath. This identifies the entry, not the imported file supplying an individual key. Compare values after imports have been applied within each plugin, so normal overrides are not reported as conflicts. Plugins preserves generic entry information; the Web host consumer owns network-key conflict rules and must not rely solely on the final merged value.

<?zongsoft.tools.binding ...?> declares exposure for delivery tools. Plugins does not evaluate its templates, generate proxy configuration or start listeners because of this instruction.

Verification and Operational Risks

  1. Check that the intended host content root contains plugins/Main.plugin and each feature's manifest.
  2. Check plugin names, dependencies, assembly versions, TFM and native runtime architecture.
  3. Inspect plugin.list/plugin.tree, then verify the named service, driver, command or Web endpoint contributed by that plugin.
  4. Verify effective configuration and only then connect to an authorized test service.

🚨 Deployment copies and may overwrite files; manifests can also include delete entries. Back up local configuration, inspect the resolved destination and stop the host before replacement. Plugins run with the host's privileges and are not a security sandbox. Loading configuration with file-change support is not a guarantee of safe DLL hot replacement.

See the host deployment examples and deployer syntax and options for larger multi-plugin applications.

Real Consumer Plugin: Discussions Modules and Services

Resolve Public Contracts through the Module Container

Module.cs declares the assembly's ApplicationModule(Module.NAME) and defines the module singleton. These are actual members excerpted from that class:

public const string NAME = nameof(Discussions);
public static readonly Module Current = new();

public Module() : base(NAME) { }

private IDataAccess _accessor;
public IDataAccess Accessor => _accessor ??=
	this.Services.ResolveRequired<IDataAccessProvider>().GetAccessor(this.Name);

The module resolves Core's IDataAccessProvider through its own Services and selects an accessor by module name, without constructing a database driver. The full module also defines an event registry; this member excerpt is not a complete class file. See Core for module-first lookup, shared fallback and ownership.

Separate Manifests, Configuration and Mappings

  • The business manifest composes the module, validators, filters and identity extensions.
  • The business options provide general.siteId and general.basePath under /Discussions; XML attributes become scalar configuration keys. Repository site values and storage paths are not production configuration.
  • The data mapping defines real forum entities and relationships; database scripts define database structures.
  • ForumService registers through [Service] and uses Core's data-service base; ForumController consumes that domain service.

Trace an Actual Call

For moderator lookup, the controller reads the request's data schema and calls ForumService.GetModerators. The service queries the ForumUser relationship through IDataAccess.Select<UserProfile>. Host deployment and configuration supply the data engine, connection and driver; business objects do not own concrete database clients. Security and site handling require the complete plugin chain, not an isolated query excerpt.

Verification and Ownership

Use the route templates in the existing .http requests, substituting identifiers from your test site and forum. Do not copy identities or service addresses. Check plugin loading, container resolution, configuration, mappings, database and permissions before interpreting results.

Windows terminal automation needs valid console handles. Start from the deployment directory and preserve dependencies such as runtimes/. Individual consumers do not dispose shared container services. Restart the isolated host after assembly changes, and clean only resources created for your test.

Loading and Lifecycle

The host loads manifests, assemblies, the plugin tree and options before registering services and initializing the application context. The workbench visits ordinary extensions, then reads Startup and registers workers when opened. Constructing an instance and starting a worker are separate stages.

BuilderManager coordinates optional OnBuilding callbacks, instance creation, child assembly, optional OnBuilded callbacks, owner appending, and completion state. The owner's builder supplies IAppender. An expose node does not append itself but can receive child objects. Initial construction and cached traversal follow the same appending rules.

BuilderSettings contains only a type hint. IBuilderLifecycle is an optional interface implemented by BuilderBase; its OnBuilding and OnBuilded hooks run around creation and child assembly without repeating on completed cache reuse. Concrete builders set context.AppendToOwner and context.BuildChildren during Build; the manager preserves those decisions with the returned instance. Result is read-only to consumers. Module lookup creates only the instance; normal Auto access completes assembly later. After partial assembly fails with a published instance, subsequent Auto reads rethrow the failure.

ObtainMode.Alway updates the node cache without appending the result and does not replace old references in owner collections. Unloading and host shutdown do not imply object-graph rollback or assembly hot replacement. Avoid startup side effects in constructors. See Architecture and implementation for plugin discovery, tree and object relationships, construction diagrams, expressions, configuration, services, host lifecycle and cleanup boundaries.

Configuration

PluginConfigurationSource makes plugin options available through Microsoft configuration. Keep responsibilities distinct:

  • *.plugin composes plugins and extension nodes.
  • *.option supplies application options.
  • *.mapping describes Data entities and commands.
  • *.deploy controls package deployment.

💡 Start from plugins/Main.plugin for the standard workbench tree and plugins/Terminal.plugin for terminal-specific composition.

Diagnostics

  • Use the plugin Tree, List, and Find commands to inspect the effective extension tree.
  • Check the first missing dependency or assembly error rather than later component failures.
  • Treat paths as contextual: relative path expressions are evaluated from the current node.
  • Verify that configuration and deployment artifacts were copied next to the plugin manifest.
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Zongsoft.Plugins:

Package Downloads
Zongsoft.Plugins.Web

This is a framework about plugin web application development.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
7.22.0 88 10/25/2026
7.21.0 159 10/22/2026
7.20.0 102 10/20/2026
7.19.0 108 10/20/2026
7.18.0 105 10/17/2026
7.17.0 122 10/16/2026
7.16.0 349 5/5/2026
7.15.0 185 4/29/2026
7.14.2 162 4/28/2026
7.14.1 171 4/6/2026
7.14.0 242 1/11/2026
7.13.0 207 12/31/2025
7.12.1 280 12/25/2025
7.12.0 293 10/10/2025
7.11.0 397 8/28/2025
7.10.0 352 8/4/2025
7.9.0 730 7/22/2025
7.8.0 484 7/21/2025
7.7.0 492 6/11/2025
7.6.0 323 5/30/2025
Loading failed

暴露事件交换器的远程发布筛选规则,支持通过插件配置事件发布范围。