Skip to content

Bump OPC UA packages to 2.0.0-preview.2 and migrate the breaking API changes - #785

Merged
romanett merged 1 commit into
masterfrom
romanett/bump-nuget-2.0.0-preview.2
Aug 26, 2026
Merged

Bump OPC UA packages to 2.0.0-preview.2 and migrate the breaking API changes#785
romanett merged 1 commit into
masterfrom
romanett/bump-nuget-2.0.0-preview.2

Conversation

@romanett

@romanett romanett commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Targets master and stands alone — one commit, no dependency on #783. Merge this first; #783 then picks up the new version when master is merged into it.

Why CI is failing

The opcua-preview feed no longer publishes 2.0.158.59919-preview. 2.0.0-preview.2 is now the only version available, so nuget restore fails on any clean machine — including CI. Local builds keep working only because the old package is still in a developer's NuGet cache.

The bump alone does not fix it

2.0.0-preview.2 carries breaking API changes. With the old version both CodeQL solutions build with 0 errors; with only the version strings changed they produce ~21 and ~28 distinct errors. So this PR does both: the bump, and the API migration it forces.

Bump: all 43 .csproj files, covering all 14 OPCFoundation.NetStandard.Opc.Ua.* packages. Every one verified present at 2.0.0-preview.2 on the feed before changing anything.

API migrations

Change Sites Migration
SecurityPolicies no longer static — now sealed class SecurityPolicies : ISecurityPolicyRegistry 17 Route through SecurityPolicies.Default, documented as the registry for "code with no container in scope" — exactly these WinForms dialogs
New Opc.Ua.Decimal built-in shadows System.Decimal 4 Use the decimal keyword for the NumericUpDown bounds
INodeManager.TransferMonitoredItems gained MonitoredItemTransferOptions 3 classes Six-arg overload holds the logic, five-arg delegates — the same shape as the SDK's own CustomNodeManager2
DiagnosticsLock removed from Session/Subscription/IServerInternal; ISession.SessionDiagnostics gone 6 files ReadDiagnostics(d => …), which applies the projection while holding the lock the object owns
ISession.Save/RestoreHistoryContinuationPoint removed 3 files Session.ContinuationPoints.SaveHistory/RestoreHistory, taking IHistoryContinuationPoint
NodeBrowser.DataLock removed 6 Drop the lock — browsers are single-consumer and the base type documents that derived browsers are not expected to synchronize
NodeState.FindChild gained defaulted assignInstanceNodeIds 23 Overrides take it and forward to base (callers are unaffected; only overrides must match)

Judgment calls worth a look

  • TransferMonitoredItems honours DeferInitialValues rather than ignoring it: when set, SetupResendDataTrigger is skipped so the owning subscription drives it after committing the transfer. Passing the flag through would have been enough to compile; this makes the samples actually correct against the new contract.
  • The two Server.DiagnosticsLock blocks simply drop the lock. Neither touches the server diagnostics summary that lock guarded — one mutates HistoryServerCapabilities nodes during CreateAddressSpace, the other guards namespace-table mapping. UpdateServerDiagnostics is the documented replacement but is the wrong tool for both, and the lock is now private.
  • HistoryDataReader / HistoryReadRequest now implement IHistoryContinuationPoint and carry their own Id, replacing the separately generated Guid the old two-argument SaveHistoryContinuationPoint took.
  • The FindChild changes are in generated model files. They are edited in place rather than regenerated, matching what the current generator emits. Regenerating those models is a larger, separate job.

Verification

  • All five solutions in the repository build clean in Release from this branch alone: both CodeQL solutions (UA Quickstart Applications.sln, UA Sample Applications.sln) plus UA Aggregation.sln, UA Global Discovery Server.sln and UA Samples.slnx.
  • Both CodeQL solutions restore cleanly and resolve to 2.0.0-preview.2.
  • No 2.0.158.59919-preview reference remains in any project or props file.

Relationship to #783

#783 (sealing and config migration) adds SourceGeneration analyzer PackageReferences to nine projects. Those are not in this PR — they arrive with #783, and will need the same 2.0.0-preview.2 version when master is merged into that branch after this one lands.

🤖 Generated with Claude Code

@romanett
romanett force-pushed the romanett/bump-nuget-2.0.0-preview.2 branch from 43924ee to fe0a52b Compare August 26, 2026 05:11
@romanett
romanett changed the base branch from romanett/migrate-sample-configs-to-datatype to master August 26, 2026 05:11
…changes

The opcua-preview feed no longer publishes 2.0.158.59919-preview; 2.0.0-preview.2
is now the only version available, so restore fails on any clean machine and CI
cannot build. Bump all 43 project files across the 14 OPCFoundation packages.

The bump alone is not enough: 2.0.0-preview.2 carries breaking API changes that
produce ~21 compile errors in sample code. Migrate each to its documented
replacement.

- SecurityPolicies is no longer static; it is a sealed class implementing
  ISecurityPolicyRegistry. The 17 GetDisplayName/GetUri/GetDisplayNames call
  sites now go through SecurityPolicies.Default, which the API documents as the
  registry for code with no container in scope - exactly these WinForms dialogs.

- The new Opc.Ua.Decimal built-in shadows System.Decimal in files that import
  Opc.Ua. The affected NumericUpDown bounds now use the decimal keyword.

- INodeManager.TransferMonitoredItems gained a MonitoredItemTransferOptions
  parameter. CustomNodeManager, SampleNodeManager and QuickstartNodeManager get
  the six-argument overload holding the logic, with the old five-argument form
  delegating to it - the same shape the SDK's own CustomNodeManager2 uses. The
  implementation honours DeferInitialValues by skipping SetupResendDataTrigger,
  which the owning subscription then drives after it commits the transfer.

- Session, Subscription and IServerInternal no longer expose DiagnosticsLock,
  and ISession.SessionDiagnostics is gone. Reads go through ReadDiagnostics,
  which applies the projection while holding the lock the object owns. The two
  Server.DiagnosticsLock blocks did not touch the diagnostics summary that lock
  guarded - they used it as an incidental general lock - so they simply drop it.

- ISession.Save/RestoreHistoryContinuationPoint moved to
  Session.ContinuationPoints.SaveHistory/RestoreHistory, which take and return
  IHistoryContinuationPoint. HistoryDataReader and the two HistoryReadRequest
  holders implement that interface and carry their own Id, replacing the
  separately generated Guid.

- NodeBrowser.DataLock is gone: browsers are single-consumer and the base type
  documents that derived browsers are not expected to synchronize. The six
  Next() overrides drop the lock.

- NodeState.FindChild gained a defaulted assignInstanceNodeIds parameter.
  Callers are unaffected but overrides must match, so the 23 overrides in the
  generated model files take it and forward it to base.

Verified: all five solutions in the repository build clean in Release, including
both solutions CodeQL builds. The configuration work on the parent branch still
holds on the new packages - all nine ParseExtension call sites bind,
HistoricalAccess still reads ArchiveRoot ".\Archive", and the AlarmCondition
encoder still emits WriteEncodeableArray with no ExtensionObject wrapping.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@romanett
romanett merged commit 5dabd23 into master Aug 26, 2026
5 of 7 checks passed
@romanett
romanett deleted the romanett/bump-nuget-2.0.0-preview.2 branch August 26, 2026 05:13
romanett added a commit that referenced this pull request Aug 26, 2026
Merging master brought in the package bump from #785, but the nine
SourceGeneration analyzer references this branch adds exist only here, so the
merge left them pinned to 2.0.158.59919-preview - a version the opcua-preview
feed no longer publishes. Git could not flag it as a conflict because nothing
on master touches those lines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
romanett added a commit that referenced this pull request Aug 26, 2026
…onfigs (#783)

* Seal [DataType] config classes and migrate remaining [DataContract] configs

AreaConfiguration was a non-sealed partial class, so the source generator's
default StructureHandling.Auto resolved to ExtensionObject wrapping for both
AlarmConditionServerConfiguration.Areas and AreaConfiguration.SubAreas. The
shipped AlarmConditionServer.Config.xml stores plain nested <AreaConfiguration>
elements with no ExtensionObject/TypeId wrappers, so the area tree silently
decoded as empty (0 of 8 areas, no exception).

Auto resolves to inline encoding only when the field's type is sealed and has
no IEncodeable base; otherwise it wraps in an ExtensionObject to allow
subtyping. Sealing the config types is the fix, and none of them is subclassed
anywhere in the repo.

- Seal the 10 existing [DataType] types. AreaConfiguration, MemoryBufferInstance
  and PerformanceTestResultItem are used as property types, so only those change
  generated encoding; the rest are sealed for consistency so the same bug cannot
  reappear when a nested property is added.
- Migrate the 9 remaining [DataContract] server configs to [DataType] sealed
  partial classes and drop the DataContractSerializer-only [OnDeserializing]
  hooks and now-unused usings. All 9 are empty (no [DataMember] properties) and
  none is ever deserialized - each node manager hardcodes m_configuration = null
  and falls back to a new instance - so this is a format change only.
- Add the OPCFoundation.NetStandard.Opc.Ua.SourceGeneration analyzer reference
  to the 9 projects that need it. Analyzers do not flow through ProjectReference,
  and HistoricalAccess Server already carried [DataType] annotations that no
  generator ever acted on.

UA Sample Controls deliberately keeps no analyzer reference: PerformanceTestResult
is still round-tripped through DataContractSerializer in PerformanceTestDlg, and
its TestCaseResults is a private List<T> rather than ArrayOf<T>. Only the sealing
is applied there and its live serializer hooks are left intact.

Verified: AlarmCondition config now decodes all 8 areas across three levels of
nesting (0 before), the generated encoder switched from
WriteEncodeableArrayAsExtensionObjects to WriteEncodeableArray, and
ParseExtension<BoilerServerConfiguration> now returns an IEncodeable instance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Re-enable ParseExtension for the migrated sample server configurations

Now that these config types are source-generated IEncodeable implementations,
ParseExtension<T> can bind them again. It constrains T to IEncodeable, new(),
which is why these call sites were stubbed out to m_configuration = null during
the 2.0 migration.

Replace the stub with the real call in nine node managers: Boiler, DataAccess,
DataTypes, Empty, HistoricalEvents, Methods, PerfTest, SimpleEvents and
HistoricalAccess. The existing "use suitable defaults if no configuration
exists" fallback is left in place.

Two of them did not actually bind and needed their namespaces reconciled first;
both failed silently, falling back to defaults rather than raising an error:

- HistoricalAccess used a bare [DataType], so its namespace defaulted to
  urn:quickstarts.historicalaccessserver while the shipped config declares
  http://opcfoundation.org/Quickstarts/HistoricalAccess. This is the only one
  of the nine that carries real data, so ArchiveRoot silently stayed null
  instead of reading ".\Archive". Bound explicitly to Namespaces.HistoricalAccess.
- DataTypesServer.Config.xml declared xmlns="http://somecompany.com/DataTypes",
  which matches no constant in the project. Corrected to
  urn:localhost:somecompany.com:DataTypesServer, the value of
  Quickstarts.DataTypes.Namespaces.DataTypes and the server's own primary node
  namespace, making it consistent with the other eight samples.

AggregationServerConfiguration is deliberately untouched: it has no node manager
field, no consumer, and no element in Quickstarts.AggregationServer.Config.xml,
so there is no ParseExtension call to restore.

Verified by loading each shipped Config.xml and calling ParseExtension<T>: all
nine now bind and return an IEncodeable instance, and HistoricalAccess reads
ArchiveRoot = ".\Archive". Reverting either namespace fix reproduces the silent
null on that sample.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Pin the added analyzer references to 2.0.0-preview.2

Merging master brought in the package bump from #785, but the nine
SourceGeneration analyzer references this branch adds exist only here, so the
merge left them pinned to 2.0.158.59919-preview - a version the opcua-preview
feed no longer publishes. Git could not flag it as a conflict because nothing
on master touches those lines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant