- Move all channel-related types into `Stack/Opc.Ua.Core/Stack/Client/Channels/`
subfolder (#13).
- Un-obsolete `SessionReconnectHandler` and the SessionExtensions
`ReconnectAsync(connection, ct)` / `(channel, ct)` extensions; remove
all SRH-related `#pragma warning disable CS0618` suppressions from
applications and tests. AttachChannel/DetachChannel remain obsolete (#1, #4).
- Remove the shared-budget section from MigrationGuide.md (#3).
- Mark `ManagedSession.AttachChannel`/`DetachChannel` `[Obsolete]` so the
warning surfaces all the way up the supported API surface (#9).
- Break the `<see cref=…>` line in `Session.ChannelManager.cs` to stay under
140 chars (#10).
- Move `ChannelManagerSessionFactory` from `Opc.Ua.Gds.Client.Common` into
`Opc.Ua.Client` as a public, documented session-factory option (#11).
- Audit other client classes for `IClientChannelManager`-aware overloads;
add `ISessionFactory`-accepting overloads on `GlobalDiscoveryServerClient`,
`LocalDiscoveryServerClient`, `ServerPushConfigurationClient` so any
session factory (including `ChannelManagerSessionFactory`) works (#12).
- Refactor MCP server (`OpcUaSessionManager`, `Program`) to use
`ManagedSession` + DI-resolved `IClientChannelManager`; remove
`SessionReconnectHandler` and manual keep-alive reconnect (#2).
- ConnectionStateMachine code-style fixes: use named delegate types,
collapse multi-line callback properties to single lines, swap
`timeProvider`/`maxTotalReconnectTime` constructor param order,
fix multi-line declaration (#6, #7, #8).
- HTTPS resilience: confirmed `Microsoft.Extensions.Http` and
`Microsoft.Extensions.Http.Resilience` 10.6.0 support
`net472`/`net48`/`netstandard2.1`; removed `#if NET8_0_OR_GREATER`
gating from `ManagedSessionBuilder` and `OpcUaClientBuilderExtensions`
and HTTPS csproj package refs (#5).
Verification:
- `Opc.Ua.Client` + `Opc.Ua.Core` + `Opc.Ua.Gds.Client.Common` build
clean (0 warnings, 0 errors).
- 56/56 core channel-manager / retry-budget / HTTPS factory tests pass.
- 129/129 client (ManagedSession + legacy SRH back-compat + SessionExtensions)
tests pass.
Introduces `IClientChannelManager` as the central registry for client-side
transport channels. Sessions and discovery clients now share ref-counted
managed channels per (endpoint, reverse-connect identity); reconnect is
coalesced and notified to attached participants via a new
`IReconnectParticipant` callback interface; service calls are gated by a
three-state lifecycle (`TransportReconnecting` -> `TransportConnectedSessionReactivating`
-> `Ready`) until reactivation completes.
New types in `Stack/Opc.Ua.Core/Stack/Client/`:
- `ManagedChannelKey`, `ChannelState`, `ChannelStateChange`, `ParticipantReconnectResult`
- `IReconnectParticipant`, `IManagedTransportChannel`, `IClientChannelManager`
- `IChannelReconnectPolicy` + `ExponentialBackoffChannelReconnectPolicy`
- `IRetryBudget` + `RetryBudget` (shared retry deadline across the two layers)
- `ClientChannelManager.Managed/Entry/Lease/Metrics/Diagnostics/CertRotation.cs`
partials with refcount, coalesced reconnect, IMeter instruments, ActivitySource
+ EventSource, and automatic ReconnectAllAsync on certificate rotation.
Session integration (Libraries/Opc.Ua.Client):
- `Session.CreateAsync(IClientChannelManager, ...)` overload constructs a
Session that shares its channel with co-located participants.
- `Session` implements `IReconnectParticipant`; reactivation handler
distinguishes Reactivated / RequiresSessionRecreate / TransientFailure /
FatalForParticipant / FatalForChannel.
- `Session.ReconnectAsync(ct)` automatically delegates to the channel
manager when wired.
- `Session.RecreateInPlaceAsync` swaps managed-channel leases for
failover-to-different-endpoint; same-key recreates delegate to manager
reconnect; explicit channel/connection callers retain legacy behavior.
- KA failure routes through `IClientChannelManager.ReconnectAsync(channel)`.
ManagedSession integration:
- `WithChannelManager(...)` builder + ctor param.
- Two-level retry: channel-mgr handles transparent reconnect; outer
`ConnectionStateMachine` + `IReconnectPolicy` only triggers on terminal
channel `Faulted`. Outer-state churn suppressed during channel-mgr
cycles via `_channelReconnectInProgress` flag wired through
`IManagedTransportChannel.StateChanged`.
- New `ManagedSession.ChannelStateChanged` event surfaces transparent
reconnects to UI / health dashboards.
- `ConnectionStateChangedEventArgs.UnderlyingChannelState` populated when
outer reconnect was triggered by a channel fault.
- Shared `IRetryBudget` propagated to channel-mgr so the two layers
enforce a single max-total-time instead of compounding multiplicatively.
Discovery/Registration/GDS:
- Channel-manager-aware `CreateAsync` overloads on `DiscoveryClient`,
`RegistrationClient`, `ServerPushConfigurationClient`,
`LocalDiscoveryServerClient`, `GlobalDiscoveryServerClient`. Each
registers a minimal `IReconnectParticipant`; co-located sessions and
discovery probes targeting the same endpoint share a channel.
HTTPS transport (`Stack/Opc.Ua.Bindings.Https/`):
- New `IOpcUaHttpClientFactory` abstraction.
- HTTPS / OPC-HTTPS channels migrated to `IHttpClientFactory` +
`Microsoft.Extensions.Http.Resilience` (10.6.0, AOT-compatible on net10
via source generators; legacy direct-HTTP path retained for older TFMs
/ no-DI consumers).
DI:
- `AddOpcUa().AddClient(...)` registers `IClientChannelManager` and the
named HTTPS HttpClient + standard resilience handler automatically.
- `ManagedSessionBuilder` resolves the manager from DI when present.
Obsoletions (functional, `[Obsolete]` with guidance):
- `IClientBase.AttachChannel`/`DetachChannel`
- `SessionReconnectHandler`
- `SessionExtensions.ReconnectAsync(connection, ct)` and `(channel, ct)`
legacy overloads
Tests (~70 new unit + 4 live-server integration fixtures):
- Core: `ClientChannelManagerManagedTests`, `ClientChannelManagerCertRotationTests`,
`RetryBudgetTests`, `OpcUaHttpClientFactoryTests` (56/56 pass).
- Client: `ManagedSession*Tests` updated (129/129 pass) + back-compat
suites pass with `#pragma warning disable CS0618`.
- Sessions integration: `ChannelManagerSharing/TransparentReconnect/SessionLifecycle/CertRotation`
(6 pass, 2 skipped with documented blockers for faulted-entry reset and
RequiresSessionRecreate end-to-end plumbing).
Builds clean (0 warnings / 0 errors) across all six TFMs (net472, net48,
netstandard2.1, net8.0, net9.0, net10.0).
Docs:
- `Docs/Sessions.md` new section 4 documents the channel manager,
three-state gating, two-level retry with shared `IRetryBudget`, and
HTTPS resilience layering.
- `Docs/MigrationGuide.md` migration recipes for the four obsoleted API
groups.
- `Docs/DependencyInjection.md` channel-manager and HTTPS factory
registration.
# Description
Adopt
[`System.TimeProvider`](https://learn.microsoft.com/dotnet/api/system.timeprovider)
across the stack so that every place that creates timers, runs duration
math, or schedules periodic work is mockable, deterministic in tests,
and immune to wall-clock changes.
The migration touches Core, Core.Types, Server, Client, PubSub,
Configuration, Gds (server + client), Lds, and the Quickstarts/Console
reference apps. Per-component changes follow the same pattern:
1. Add a `private readonly TimeProvider m_timeProvider;` field.
2. Accept a nullable `TimeProvider? timeProvider = null` as the **last**
constructor parameter (new overload; legacy constructor stays for binary
compatibility and delegates with `timeProvider: null`).
3. Initialise the field with `m_timeProvider = timeProvider ??=
TimeProvider.System;`.
4. Replace `new Timer(...)`, `Stopwatch`, `HiResClock.*`,
`DateTime.UtcNow`-based duration math, and `Task.Delay(delay, ct)` in
timing loops with the matching `m_timeProvider` API (`CreateTimer`,
`GetTimestamp` / `GetElapsedTime`, `GetUtcNow().UtcDateTime`,
`Task.Delay(delay, m_timeProvider, ct)`).
5. Wire the provider through DI: `AddOpcUaServerBuilder` /
`AddOpcUaClientBuilder` register `TimeProvider.System` via
`TryAddSingleton<TimeProvider>()` so tests can swap in
`Microsoft.Extensions.Time.Testing.FakeTimeProvider`.
`HiResClock` itself is kept (so the
`ApplicationConfiguration.DisableHiResClock` schema flag still
round-trips), but **every public member** is now `[Obsolete]` with a
single shared message pointing to the new migration-guide section. The
few internal call sites that still need to set/read `HiResClock` are
wrapped with `#pragma warning disable CS0618`.
### Highlights per component
- **Core / Stack** — `Opc.Ua.Core`, `Opc.Ua.Core.Types`, channel
pipeline (`UaSCBinaryClientChannel`, `ChannelToken`,
`UaSCBinaryChannel`), `ApplicationConfiguration`, certificate
validators, `JwksIssuerKeyResolver`, `TimeProviderExtensions` polyfills
(`GetTimestampMilliseconds`, `GetTickCount`,
`CancellationTokenSource(timeout, timeProvider)`, `Task.Delay(delay,
provider, ct)` polyfill for older TFMs).
- **Server** — session lockout, subscription pipeline
(publish/sample/keep-alive/lifetime), `MonitoredItemQueue`, alarm
sampling rate, `IntervalRunner`, server diagnostics, configuration state
machines, hosted services.
- **Client** — `Session`, managed/V2 subscription engines, reconnect
handlers, channel keep-alive, periodic publishing, complex-type
discovery polling.
- **PubSub** — `IntervalRunner` pipeline, message-receive timers,
MQTT/UDP keep-alive, publishers/readers.
- **Gds / Lds** — `LdsServer`, `LinqApplicationsDatabase` expiry
housekeeping, GDS clients (push, pull), `DirectoryCertificateStore`,
`ConnectionStateMachine`, classic subscription engine.
- **Quickstarts / Console samples** — `SampleNodeManager`,
`ReferenceNodeManager`, `AlarmNodeManager`, `BoilerState`,
`MemoryBufferState`, `PublishedValuesWrites`. Samples resolve via
`(Server as ITimeProviderProvider)?.TimeProvider ?? TimeProvider.System`
so existing ctor signatures stay binary-compatible.
- **Tests** — `Opc.Ua.PubSub.Tests` adds `IntervalRunnerTests`; existing
`SubscriptionTests`, `UaPublisherTests`, and `TestDataSystem` move off
`HiResClock`. Added `Microsoft.Extensions.TimeProvider.Testing` package
for `FakeTimeProvider` (project-load warning surfaces on net48/net472
only — runtime is .NET 8+).
### Migration guide
New section [**Time and Timer abstraction
(`TimeProvider`)**](Docs/MigrationGuide.md#time-and-timer-abstraction-timeprovider)
documents:
- The canonical replacement table (`HiResClock.UtcNow` →
`timeProvider.GetUtcNow().UtcDateTime`, `HiResClock.TickCount64` →
`timeProvider.GetTimestamp()`, `new Timer` → `timeProvider.CreateTimer`,
`Task.Delay(delay, ct)` → `Task.Delay(delay, timeProvider, ct)`, …).
- The `TimeProvider? timeProvider = null` + `??=` constructor pattern.
- DI wiring and how to swap in `FakeTimeProvider` in tests.
- Before/after snippets for `HiResClock.TickCount64`,
`HiResClock.UtcNow`, and `new Timer(...)`.
## Related Issues
- Fixes#2458
## Checklist
- [x] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.
* New certificate and secrete abstraction with certificate infrastructure and certificate/secret stores.
* leak testing for certificate resources to validate new ref counting in every test assembly
* Migration guide updated and documentation added for all
* Remove IDisposable from Nodestate and UserIdentity.
* UserCertificateTokenHandler now just points into certificate registry/stores, no dispose required
* Fix CA2000 warnings and cleanup of other diagnostics, increase warning levels in .editorconfig
- Add new managed session, wrapping session and providing connection management and failover handling
- Add new Subscription engine that can act as alternative to current engine, but is based on channels with worker tasks
- Add a fluent builder for managed session clients
- Replace NodeCache with LruNodeCache, rename ILruNodeCache and LruNodeCache to INodeCache and NodeCache
- Update migration guide and document new functionality.
This pull request modernizes the OPC UA .NET codebase and tests, removes .NET Standard 2.0 support from certificate/security code, and updates test code to use C# 12 and .NET 8+ features for clarity and maintainability.
- Certificate and BouncyCastle interop files (e.g., CertificateBuilder.cs, X509Extensions.cs, X509SignatureFactory.cs) are refactored to drop NETSTANDARD2_0 support and use modern .NET APIs and conditional compilation.
- Test code across the codebase is updated to use default, strongly-typed Parse methods, collection expressions, and improved async/await patterns.
- Exception handling and assertions in tests are modernized, and test method naming is made consistent with Async suffixes.
- The project and build files are updated to clarify Roslynator analyzer usage and remove obsolete netstandard tags.
- General code and test cleanup includes improved null safety, type safety, and removal of deprecated or unused code.
Refactored client-side logic to support batching of service requests, ensuring compliance with operation limits and improving scalability. Added telemetry and activity tracing using `ActivityTraceFlags` for better observability, including metrics collection and logging. Enhanced error handling with descriptive exception messages and improved certificate validation.
Introduced a comprehensive suite of unit tests for `SessionClientBatchTests.cs` and `ClientBaseTests.cs`, covering batching, error scenarios, and edge cases. Added helper classes for testing logging, metrics, and activity tracing. Updated tests to align with modern C# practices, including nullable reference types and collection initializers.
Improved code consistency, readability, and maintainability by standardizing method signatures, simplifying redundant patterns, and removing legacy constructs. Updated XML documentation and added conditional compilation for .NET 8.0+ compatibility.
- Replaced `IList<INode>` with `List<INode>` in
`ComplexTypeSystem.cs` and `BaseComplexType.cs` for
better type safety.
- Updated string comparison in `ReverseConnectManager.cs`
to `StringComparison.Ordinal` for improved performance.
- Added a method to `ISession.cs`, `Session.cs`, and
`SessionAsync.cs` to trigger reloading instance certificates.
- Minor fixes in error handling in `Subscription.cs` and using pattern matching for type checks in several locations to improve code readability.
- Made minor adjustments to comments and documentation
for clarity and improved code quality.
- Updated `Variant` handling in preparation for immutable struct.
- Changed return types in `BaseObjectState.cs` and `BaseVariableTypeState.cs` to use `ObjectTypeIds` for iperf
- Cleaned up `ExpandedNodeId`
- Enhanced memory management error handling and readabilityand error handling in encoders
* Update SessionReconnectHandler.cs
There is chance that m_session.DetachChannel can clear the member "Endpoint" info of the session, once that happened, there will be crash when use member "Endpoint" of the session as parameter of m_reverseConnectManager.WaitForConnection.
* Update SessionReconnectHandler.cs
Add the reconnect exception text to the log message, otherwise the exception is filtered out.
Turn off the trace logging in tests, as it causes timeout issues on some ci VMs
reduce callbacks if client reconnect and call AddItems
Fix BadNothingToDo when Loading dictionaries
Revert benchmark.net to v13 due to weird build dependency issues with 14.x