Zongsoft.Core 7.76.0

dotnet add package Zongsoft.Core --version 7.76.0
                    
NuGet\Install-Package Zongsoft.Core -Version 7.76.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.Core" Version="7.76.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Zongsoft.Core" Version="7.76.0" />
                    
Directory.Packages.props
<PackageReference Include="Zongsoft.Core" />
                    
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.Core --version 7.76.0
                    
#r "nuget: Zongsoft.Core, 7.76.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.Core@7.76.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.Core&version=7.76.0
                    
Install as a Cake Addin
#tool nuget:?package=Zongsoft.Core&version=7.76.0
                    
Install as a Cake Tool

Zongsoft Core

License NuGet Version NuGet Downloads GitHub Stars

English | 简体中文


Overview

Zongsoft.Core is the foundation package of the Zongsoft Framework. It provides the shared abstractions, base types, utility APIs, and infrastructure building blocks used by the rest of the Zongsoft ecosystem.

The current project targets net8.0, net9.0, and net10.0, uses the root namespace Zongsoft, and is packaged as the Zongsoft.Core NuGet package. It integrates with the Microsoft.Extensions.* stack for dependency injection, configuration, options, hosting abstractions, object pooling, and memory caching.

Installation

dotnet add package Zongsoft.Core

What's Included

The package is intentionally broad: it is the common layer that higher-level Zongsoft packages build on. The main areas are:

  • Common utilities (Zongsoft.Common)

    Conversion helpers, random generation, sequences, predicates, timers, bit vectors, string/type/URI extensions, timestamps, locking helpers, and validation contracts. Variable sources include the IVariables contract, the case-insensitive in-memory Variables dictionary, live dictionary views through Variables.Wrap() / ToVariables(), and live environment variables through Variables.Environments().

  • Collections (Zongsoft.Collections)

    Hierarchical nodes, category trees, parameter bags, synchronized collections, queues, object pools, and collection/dictionary extensions.

  • Components (Zongsoft.Components)

    Command infrastructure, command-line parsing, command option variables, event exchange, feature pipelines, retry/fallback/breaker/throttle/timeout features, state machines, workers, supervisors, handlers, executors, filters, converters, and identifiers.

  • Configuration (Zongsoft.Configuration)

    Settings and connection settings, configuration binding and recognition, XML configuration providers and processing instructions, model-backed configuration, Profile/INI parsing, directive extensions and variable views, and options integration with Microsoft.Extensions.Options.

  • Data abstractions (Zongsoft.Data)

    Query criteria, conditions, ranges, paging, sorting, operands, model descriptors, data access/service contracts, operation options and events, metadata models, archiving contracts, and transaction primitives.

  • Services (Zongsoft.Services)

    Application context and modules, service registration/discovery helpers, dependency metadata, modular services, service accessors, and distributed-lock abstractions.

  • Caching (Zongsoft.Caching)

    In-memory cache wrappers, eviction/change events, cache scanner, distributed-cache contract, and the Spooler<T> batching helper.

  • Communication and messaging (Zongsoft.Communication, Zongsoft.Messaging)

    Network address and protocol values, channel, listener, sender, receiver, requester/responder, transmitter, packetizer, notifier, message queue, producer/consumer, poller, and queue option abstractions.

  • Diagnostics and telemetry (Zongsoft.Diagnostics)

    Logging contracts, console/text/XML loggers and formatters, diagnostic configuration, telemetry meters, metric descriptors, and exporter launcher contracts.

  • IO and hardware (Zongsoft.IO)

    Virtual file-system contracts and local implementation, path parsing, Searcher local wildcard search, MIME helpers, compression helpers, binary/text reader extensions, and hardware profile models.

  • Security (Zongsoft.Security)

    Claims helpers, credentials, certificates, secret/signature contracts, password utilities, authentication/authorization flows, users, roles, privileges, and privilege evaluators.

  • Serialization (Zongsoft.Serialization)

    Serializer contracts, JSON serializer helpers, serialization options, naming conventions, member attributes, and System.Text.Json converters.

  • Expressions (Zongsoft.Expressions)

    Lexer/tokenizer infrastructure, expression evaluator contracts, and syntax exceptions.

  • Text and templates (Zongsoft.Text, Zongsoft.Text.Templating)

    Regular-text processing, template contracts, and text template evaluation, member navigation, formatting and diagnostics.

  • Reflection (Zongsoft.Reflection)

    High-performance reflection helpers and member-expression parsing/evaluation.

  • Runtime helpers (Zongsoft.Resources, Zongsoft.Scheduling, Zongsoft.Versioning, Zongsoft.Terminals)

    Resource lookup, trigger abstractions, semantic version parsing, and terminal/console command execution.

Password Hashing

Password uses PBKDF2 with SHA256 by default. Its Factor ranges from 0 to 255 and defaults to 10. Zero disables additional iterations by performing PBKDF2 once; positive factors map to Factor × 65,535 iterations. The default factor 10 means 655,350 iterations; factor 127 means 8,322,945 iterations. Execution time depends on the hardware, hash algorithm, and load.

The text format is algorithm#factor:nonce|value; #factor may be omitted when the factor is zero. Password digests must use the iteration rules above.

Password implements ISpanParsable<Password> and IParsable<Password>, with public Parse and TryParse overloads for strings and character spans. The text format is culture-independent and the interface's format provider is ignored. Null or empty input represents an empty password. Invalid text returns false and an empty result from TryParse; Parse throws FormatException instead.

User password services expose Passworder.SetAsync to set or clear a password and two VerifyAsync overloads to verify by user identifier or account. Account verification returns the user identifier on success and an empty identifier for a password mismatch; missing or disabled accounts throw AuthenticationException with the corresponding reason code. Verification by user identifier returns false for a missing user.

A null or empty stored password accepts only null or empty-string input for sign-in and old-password verification. Nonempty input, including whitespace-only strings, fails. Users can supply an empty old password to set a new one. Missing or disabled accounts and malformed stored passwords still fail verification. Use Zongsoft.Common.Randomizer.GenerateSecret for random plaintext secrets.

Service Registration

Use ServiceCollectionExtension.Register to register services from an assembly. Public types annotated with [Service] participate in automatic registration; implement IServiceRegistration when an application needs custom registrations. Registration failures propagate to the caller.

Event Dispatch

Each event has one descriptor: use EventDescriptor<TArgument> for a single argument contract or non-generic EventDescriptor to declare multiple contracts. Single and collection handlers can share a plugin path, and local and remote delivery preserve the declared type. Use RaiseAsync to raise events and await their handlers. Bound event handlers must return an awaitable result. EventExchanger supports transaction-aware delivery, and applications can add conditions through IFeatureProvider or configure policies through EventDispatcher.Pipeline. See event dispatch and ambient features.

Data service write events

Explicitly register DataServiceBase<TModel>.EventFilterBase to reuse event data preparation and single/collection callbacks for insert, update, delete, upsert, and import. Business modules retain event declarations and publication; reads are excluded. See data service write events for usage, Returning requirements, and input lifetime.

Condition lookup

Match and Find locate the first condition with a matching field name, ignoring case and following depth-first collection order. Matches and FindAll include every matching condition, including nested collections. These methods search the condition tree; they do not evaluate its logic or infer a single field value. Matching an IN condition preserves its operator and collection value, and an OR branch remains searchable.

The ConditionExtension overloads accept maxDepth. Depth starts at zero and increases only when a nested collection changes the AND/OR combination; nesting the same combination does not increase depth. A positive limit excludes that depth and deeper levels, so FindAll("Id", 1) stays within the root combination. Zero and negative limits search all depths. Matches also accepts maxCount, which limits matches across the entire tree; zero and negative values mean unlimited.

Use criteria.TryGetValue("Id", out object value) or criteria.TryGetValue<int>("Id", out var id) when a consumer needs a definite equality constant. Only standalone conditions and AND chains qualify; OR branches, IN lists, ranges and expressions do not supply values. Constant operands are unwrapped. Repeated equality constants must agree under Object.Equals; conflicting constants return false, without guessing database conversions or collation. Strings and byte arrays remain single values. The generic overload converts only after determining the constant; null succeeds for reference and nullable types, but fails for non-nullable value types. These methods neither query the database nor change the condition tree.

var criteria = Condition.Equal("TenantId", 17U) & Condition.In("BuildingId", new uint[] { 101, 102 });
criteria.TryGetValue<uint>("TenantId", out var tenantId); // true: 17
criteria.TryGetValue<uint>("BuildingId", out var buildingId); // false: multiple values

Asynchronous data operations

IDataAccess and IDataService expose asynchronous database methods. Scalar and write operations return ValueTask; queries return IAsyncEnumerable<T>, with cancellation tokens last. Consume queries with await foreach; leaving enumeration early still releases reader resources. Use await using for accessors you own, and leave shared accessors to their owner.

InsertManyAsync, UpsertManyAsync, UpdateManyAsync and accessor ImportAsync accept both in-memory collections and IAsyncEnumerable<T>. Event handlers accept a cancellation token and return ValueTask. Await database and network work inside handlers.

UpdateAsync uses TryGetValue to copy definite primary-key equality constants into the model. IN, range, inequality, expression, conflicting equality, and OR conditions do not supply a single key value. Both public and protected update entry points follow this rule. DeleteAsync passes the complete validated criteria to the data accessor without extracting a single key.

The Getted event for a single-record query exposes an already obtained Result snapshot; reading it never queries again. IPageable.Paginated completes before the first record is yielded, allowing pagination response metadata to be set in time. Template export awaits GetModelAsync to obtain its model.

State machines use await using and await machine.RunAsync(state, description, cancellation). Wait for each run before starting the next or disposing the machine; disposal waits for state persistence and transaction completion.

DataDictionary.GetDictionariesAsync<T>(items, cancellation) converts an in-memory collection to an asynchronous sequence of data dictionaries. For asynchronous input, use GetDictionariesAsync<T, TSource>(items, cancellation). To prepare each item, pass a Func<IDataDictionary<T>, CancellationToken, ValueTask> callback before the token. Enumeration awaits each callback in input order, skips null items, and does not buffer the entire collection.

Data Validation and Result Processing

Implement IDataValidator.ValidateAsync for query conditions and OnImportAsync, OnInsertAsync, and OnUpdateAsync for field values. Field validation returns DataValidatorResult: default leaves the value unchanged; HasValue = true supplies a replacement, including null. Use IDataValueBinder.BindAsync when the replacement depends on the record being bound.

Pageable.FilterAsync and MapAsync accept asynchronous callbacks over IAsyncEnumerable<T>. They process items when enumerated, preserve paging information, and pass the enumeration cancellation token to the callback. A filter returns Pageable.FilterResult<T>: Accepted = true emits Data, including a replacement or null; default discards the item.

IDataMutateContextBase.Returning is owned by one delete, insert, update, or upsert operation. Filters add required columns before execution and read the current operation's rows after success. It is independent of Options.Returning: it does not replace that object, extend its columns, or expose additional fields through its rows. The engine combines the SQL projection and distributes separate results. Unsupported required kinds fail; driver limitations still apply. Keep both requests unchanged during execution. Import does not support Returning; service imports participate in the existing service-filter pipeline through their access context.

Returning.Columns.TryGetValue(name, kind, out column) looks up a column by case-insensitive name and ReturningKind. The overload without kind, or a null kind, uses the collection's fixed kind, falling back to Older when none is fixed. An empty name, incompatible kind, or missing column returns false with a default column.

Returning.Rows.Populate(record) reads the current IDataRecord and appends a row; it does not advance or dispose the reader. Rows.AddRange(rows) appends the contents of another row collection.

Rows resolve values by case-insensitive name and ReturningKind. Later changes to Columns do not change existing rows' name mappings. Row[int] and Values retain the positions of that row's values. TryGetValue returns false for missing columns and true with a null value for database nulls. Collections do not support concurrent mutation.

row.ToDictionary(kind) and row.ToDictionary<T>(kind) convert one row to IDataDictionary or IDataDictionary<T>. Explicitly select ReturningKind.Older or ReturningKind.Newer. Field names are case-insensitive and omit the @Older / @Newer suffix. Only available fields are included; nulls are preserved and missing fields are not filled with model defaults. Each conversion creates an independent dictionary container without deep-copying field values. Call AsModel() when a model is needed. HasChanges denotes field presence, not a comparison between old and new values.

returning.Rows.ToDictionaries<T>(kind) converts rows lazily; its non-generic overload returns non-generic dictionaries. It does not access the database, modify rows, or clear results. A row without available fields of the selected kind produces an empty dictionary; an empty collection produces an empty sequence. Each enumeration creates fresh dictionaries. Materialize once with ToArray() for a stable batch event argument. An undefined kind or a kind conflicting with a row's fixed kind throws ArgumentOutOfRangeException; collection conversion errors occur during enumeration.

var deleted = options.Returning.Rows
	.ToDictionaries<Building>(ReturningKind.Older)
	.ToArray();

Here options belongs to a completed delete operation and Building is the caller's model. Operations append returned rows; when reusing options, capture the initial row count and select only rows added by the current operation.

Transactions

Transaction exposes CommitAsync, RollbackAsync, and DisposeAsync. Manage its scope with await using; disposal rolls back a transaction that has not committed. Rolling back a nested transaction makes the root transaction rollback-only. Cancellation is checked before accepting a commit; accepted completion and cleanup wait until finished. Repeated calls observe the same completion result, including failures.

RollbackAsync(exception) returns normally when rollback and completion notifications succeed. On failure, it throws an AggregateException containing the supplied business exception and completion errors. Await rollback in a catch block, then decide whether to propagate the original exception with throw;:

catch(Exception exception)
{
	await transaction.RollbackAsync(exception);
	throw; // Omit this line to handle the business error after successful rollback.
}

Batch updates, batch role and user deletion, and state-machine stopping preserve both errors when the operation and transaction completion fail. When only the operation fails, its original exception propagates.

The exception is accepted only when initiating rollback. Once commit or rollback has started, subsequent calls observe the existing result without repeating rollback or adding exceptions. Passing null propagates only completion errors. Use RollbackAsync() without arguments or pass an explicitly typed cancellation token; RollbackAsync(default) is ambiguous between the overloads.

TransactionContext.Completed accepts handlers returning ValueTask and awaits every handler in subscription order. Notification failures do not change an established transaction outcome. An uncertain outcome, such as a connection failure during commit, remains Undetermined; applications must not assume the write rolled back or retry it automatically. Handlers must not await the transaction completion task that includes themselves.

Application Lifetime

ApplicationContext participates in the host's awaited startup and shutdown phases. Exit() and Exit(exitCode) request host shutdown; they do not terminate the process. DisposeAsync() waits for owned worker cleanup before releasing context resources. Application builders expose BuildAsync() so initialization failure can await asynchronous disposal. Container-resolved initializers and borrowed service-provider views retain their original owner.

Terminal Input

ITerminal.Input uses TextReader, accepting StreamReader and StringReader directly. Terminal.Console.Input provides shared line input with process lifetime: only one caller can wait, and cancellation preserves the read and its result for a subsequent call. Executors do not dispose input. Injected readers follow their own cancellation and ownership contracts. See Terminal input.

ITerminalExecutor runs its command loop through three RunAsync overloads and returns the exit code when the loop ends. Callers must await the task.

ITerminal.Abort() synchronously notifies interruption and returns whether it was handled; it does not stop the executor or process. Hosts own process-signal policy. Core registers no global console signal handlers.

Logging

Synchronous methods such as Error(...), Log(...), and Flush() invoke loggers immediately and return without waiting for asynchronous work. Logging does not buffer records or track outstanding operations. Logger failures are observed but are not propagated to synchronous callers; each logger owns its buffering, completion, and failure reporting.

Use awaited asynchronous methods when completion or error propagation is required. FlushAsync() delegates to the registered loggers and does not wait for earlier unawaited logging calls. Before releasing loggers, stop producing records, await owned asynchronous logging operations, and await FlushAsync(). A logger's processing completion does not necessarily mean persistence.

Periodic Work

Common.Timer executes its constructor callback serially and accepts a TimeProvider. Start() schedules periodic execution. Startup, stopping, and disposal accept no cancellation tokens. Stop() requests internal cancellation without waiting for callbacks or cleanup; StopAsync() waits for shutdown. Dispose() permanently closes the timer without waiting, while DisposeAsync() awaits cleanup even after synchronous disposal. Callers must avoid using business operations after disposal or awaiting completion from within the same run. Failed reports run failures, and Stopped reports that the callback and resource cleanup have ended. Asynchronous stopping and disposal wait for notifications to return and propagate run and notification failures. See Asynchronous periodic timers.

Spooler<T> serializes batch callbacks triggered by a period, a full-buffer write, or an explicit flush. It supports consuming enumeration and inheritance. PutAsync admits data; FlushAsync waits for an active flush and processes remaining data once. Disposal is asynchronous: DisposeAsync stops admission and waits for active flushes and cleanup without draining the buffer. Periodic errors raise Failed and propagate from asynchronous disposal; the buffer does not write logs.

Both constructors accept an optional TimeProvider. The Zongsoft.Caching.Spooler meter exposes admission, queue, flush duration, full-buffer wait, and failure metrics for application-selected export. FileLogger supports only asynchronous disposal and releases its resources after active file writes finish.

Communication Lifetimes

Await StartAsync, StopAsync, PauseAsync and ResumeAsync on workers. Enabled controls whether startup is allowed; changing it does not stop an active worker. Check CanPauseAndContinue before requesting pause or resume. A successfully stopped worker can start again; a disposed worker cannot.

Use await using or await DisposeAsync for owned workers, listeners, queues, pollers and distributed locks. Container-owned services are released by their container. Await channel closure through CloseAsync; a closed channel cannot be reopened. Lifecycle callbacks must not wait for their own shutdown or disposal.

Read request responses with await foreach over IRequestToken.GetResponsesAsync. Await message confirmation through Message.AcknowledgeAsync; a completed handler does not automatically imply confirmation.

Worker commands accept --timeout to limit waiting. The default is five seconds, non-positive values use five seconds, and the maximum is thirty seconds. A timeout raises TimeoutException while the worker operation continues.

The Windows shell command waits for process exit and redirected output. Its timeout defaults to one second; non-positive values use thirty seconds. A timeout returns -1, and caller cancellation propagates. Neither terminates the launched process.

Repository Layout

Zongsoft.Core/
  src/               Main library source code.
  test/unit/         xUnit unit tests.
  test/integration/  Separate terminal input and process integration tests.
  samples/           Console samples for MemoryCache, Spooler, Superviser, and EventExchanger.
  benchmark/         BenchmarkDotNet benchmarks for reflection, data model helpers, and Spooler batching.

Build and Test

dotnet restore Zongsoft.Core.slnx
dotnet build Zongsoft.Core.slnx -c Release
dotnet test test/unit/Zongsoft.Core.Tests.csproj -c Release

The repository also includes a Cake script:

dotnet cake build.cake --target=test --edition=Release

The main solution and the default Cake target run only test/unit. Run integration tests explicitly:

dotnet test test/integration/Zongsoft.Core.Tests.slnx -c Release -p:GeneratePackageOnBuild=false
# Or: dotnet cake build.cake --target=integration-test --edition=Release

Samples

The samples directory contains small console applications that exercise real APIs from this package:

  • memorycache

    MemoryCache, expiration scanning, limit handling, and terminal output.

  • spooler

    Spooler<T> batching under high write volume.

  • superviser

    Superviser, Supervisable, worker state reporting, and terminal commands.

  • eventexchanger

    EventExchanger channels and application context integration.

License

Zongsoft.Core is released under the LGPL-3.0-or-later license.

Identifiers

Typed identifiers support logical names, CLR types, composite keys, business codes, display metadata and registered shorthand. String/span parsing, JSON and TypeConverter share one protocol. See Identifiers.

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 (57)

Showing the top 5 NuGet packages that depend on Zongsoft.Core:

Package Downloads
Zongsoft.Web

This is a base class library that provides web development.

Zongsoft.Plugins

This is a framework about plugin application development.

Zongsoft.Data

This is an Object/Relational Mapping(ORM) data engine.

Zongsoft.Externals.Redis

This is a library about Redis SDK.

Zongsoft.Security

This is a class library about security development.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
7.76.0 44 10/31/2026
7.75.0 260 10/25/2026
7.74.0 143 10/24/2026
7.73.0 214 10/24/2026
7.72.0 284 10/22/2026
7.71.0 157 10/22/2026
7.70.0 486 10/22/2026
7.68.0 334 10/20/2026
7.67.0 325 10/20/2026
7.66.0 155 10/17/2026
7.65.0 160 10/17/2026
7.64.0 876 10/16/2026
7.63.1 98 10/11/2026
7.63.0 986 10/10/2026
7.62.1 113 10/5/2026
7.62.0 217 9/30/2026
7.61.0 146 9/28/2026
7.60.1 105 9/27/2026
7.60.0 128 9/26/2026
7.59.1 113 9/24/2026
Loading failed

统一 Identifier 标识契约,支持复合键、业务编号、元数据、简写登记及状态机解析;统一 JSON 和类型转换,完善安全身份桥接。