Skip to content

Dynamic Complex Type System for OPC UA Server applications#3818

Draft
salihgoncu wants to merge 3 commits into
OPCFoundation:masterfrom
salihgoncu:feature/opc-complex-types-overhaul
Draft

Dynamic Complex Type System for OPC UA Server applications#3818
salihgoncu wants to merge 3 commits into
OPCFoundation:masterfrom
salihgoncu:feature/opc-complex-types-overhaul

Conversation

@salihgoncu
Copy link
Copy Markdown
Contributor

@salihgoncu salihgoncu commented May 28, 2026

Proposed changes

Describe the changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue.

Related Issues

  • Fixes #

Types of changes

What types of changes does your code introduce?
Put an x in the boxes that apply. You can also fill these out after creating the PR.

  • Bugfix (non-breaking change which fixes an issue)
  • Enhancement (non-breaking change which adds functionality)
  • Test enhancement (non-breaking change to increase test coverage)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected, requires version increase of Nuget packages)
  • Documentation Update (if none of the other choices apply)

Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.

  • I have read the CONTRIBUTING doc.
  • I have signed the CLA.
  • I ran tests locally with my changes, all passed.
  • I fixed all failing tests in the CI pipelines.
  • I fixed all introduced issues with CodeQL and LGTM.
  • I have added tests that prove my fix is effective or that my feature works and increased code coverage.
  • I have added necessary documentation (if appropriate).
  • Any dependent changes have been merged and published in downstream modules.

Further comments

We split the monolithic complex-types stack into a layered three-project architecture and added a server-side counterpart. The original Opc.Ua.Client.ComplexTypes package owned the Reflection.Emit type builders, the runtime base types (BaseComplexType, OptionalFieldsComplexType, UnionComplexType, attributes), and the DI surface, while the main Opc.Ua.Client library carried the interfaces (IComplexType{Resolver,Factory,Builder,FieldBuilder}), the AOT-friendly Default* builders, DataDictionary, DataTypeDefinitionExtension, and the exception types — meaning the complex-types domain was fragmented across two assemblies with no clean reuse boundary.

We extracted everything that isn't client-orchestration into a new Opc.Ua.ComplexTypes library under the Opc.Ua.ComplexTypes namespace, leaving Opc.Ua.Client.ComplexTypes as a thin client-side package that now contains only the session-bound orchestration (ComplexTypeSystem, NodeCacheResolver, ComplexTypeSystemFactory, ComplexTypesExtensions, and OpcUaComplexTypesBuilderExtensions), and we removed the complex-types code from Opc.Ua.Client entirely. On top of that foundation we created a new Opc.Ua.Server.ComplexTypes library with a working minimal implementation — ServerComplexTypeSystem (with RegisterEnumeration / RegisterStructure / multi-pass Flush for resolving fields whose DataTypes arrive out of order), a DI-injected ServerComplexTypeSystemFactory, and an IOpcUaServerBuilder.AddComplexTypes() hosting extension — so hosted servers can finally register custom Structure/Enumeration DataTypes on their IEncodeableFactory without each application reinventing that plumbing. As a result, Opc.Ua.Core → Opc.Ua.ComplexTypes → Opc.Ua.{Client,Server}.ComplexTypes is now a clean dependency chain where the heavy lifting (Emit, base types, schema parsing, exceptions) lives in one shared library, Opc.Ua.Client is leaner and no longer carries logic it doesn't own, server authors get a first-class story for custom complex types instead of being forced to depend on a client package.

@salihgoncu salihgoncu self-assigned this May 28, 2026
@salihgoncu salihgoncu added enhancement API or feature enhancement complex Complex Types labels May 28, 2026
@salihgoncu salihgoncu marked this pull request as draft May 28, 2026 07:53
@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

❌ Patch coverage is 6.66667% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.00%. Comparing base (ce9b2b4) to head (b4d325e).

Files with missing lines Patch % Lines
.../Hosting/OpcUaComplexTypesEmitBuilderExtensions.cs 0.00% 9 Missing ⚠️
...Ua.Client.ComplexTypes/ComplexTypeSystemFactory.cs 0.00% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3818      +/-   ##
==========================================
+ Coverage   70.91%   71.00%   +0.09%     
==========================================
  Files         750      751       +1     
  Lines      140301   140314      +13     
  Branches    23668    23670       +2     
==========================================
+ Hits        99490    99633     +143     
+ Misses      32694    32590     -104     
+ Partials     8117     8091      -26     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Libraries\Opc.Ua.Client\Opc.Ua.Client.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Client.ComplexTypes\Opc.Ua.Client.ComplexTypes.csproj" />
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary, Opc.Ua.Client.ComplexTypes project has its own test project (because legacy). Opc.Ua.ComplexTypes.csproj should already be brought in through Opc.Ua.CLient.csproj.

Comment thread Tests/Opc.Ua.Client.ComplexTypes.Tests/DataDictionaryTests.cs Outdated
@@ -41,6 +41,8 @@

using Opc.Ua.Core.TestFramework;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of empty lines above new usings, please remove (run roslynator fixer)


using Opc.Ua.Client;
using Opc.Ua.ComplexTypes;
using Opc.Ua.Client.ComplexTypes;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change. While I am not against it - I would prefer one namespace. Can we move the types in Opc.Ua.Client.ComplexTypes to Opc.Ua.Client, so you bring in Opc.Ua.ComplexTypes and Opc.Ua.Client and thats it? Same on server side?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, Opc.Ua.Client.ComplexTypes can collapse into Opc.Ua.Client and Opc.Ua.Server.ComplexTypes likewise into Opc.Ua.Server but I think the common stuff should stay in Opc.Ua.ComplexTypes. We cannot have a reference to Opc.Ua.Client from the Server implementations. It would be awkward and unintuitive.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, when we talk about namespace, I think Opc.Ua.ComplexTypes would be referencing all common pieces, Opc.Ua.Client the client side, Opc.Ua.Server the server stuff. And Opc.Ua.Client.ComplexTypes should reference the old reflection emit stuff.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see the need for Opc.Ua.Server.ComplexTypes, can you move the code under the Opc.Ua.Server project, and into a ComplexTypes folder?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See previous comment.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move those 3 files you moved here back to Opc.Ua.Client. The reason there is Opc.Ua.Client.ComplexTypes is strictly for back compat with 1.5 and to segregate the legacy reflection based type builders. These old reflection emit type builders should not pollute the 1.6 native AOT paths. This also extends to the pieces you moved to Opc.Ua.ComplexTypes, move the reflection.emit bits back to this project too (Opc.Ua.Client.ComplexTypes).

OR - if you absolutely need the reflection emit path on the server side, create a Opc.Ua.ComplexTypes.Reflection package that essentially replaces Opc.Ua.Client.ComplexTypes (today). We can then add a metadata nuget in nuget.spec to wrap it so it keeps its name.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you moved the reflection emit code back out, link this package from Opc.Ua.Server and Opc.Ua.Client, so comes "for free" when bringing either one in.

Also - I would suggest a new Opc.Ua.ComplexTypes.Tests project that covers the code in this new project in particular (but not by pulling in Opc.Ua.Client, or server, just the remaining code.

@marcschier
Copy link
Copy Markdown
Collaborator

@salihgoncu can you update the docs also? Both migrationguide and the complex types docs. And ideally with rational regarding the changes you are proposing..

@salihgoncu
Copy link
Copy Markdown
Contributor Author

Created the PR to be able to run the CI first and foremost. - I know there are things to fix and move around still and PR is marked as draft for this reason. Will resolve the issues that did not manifest locally and then resolve the comments.

@marcschier
Copy link
Copy Markdown
Collaborator

Key feedback: Project structure needs some work due to the historical component of it all:

The reflection.emit code should ideally remain where it is (because legacy), the client code goes into Opc.Ua.Client, the server code into Opc.Ua.Server. The common code into new Opc.Ua.ComplexType nuget. Optionally - if reflection emit should be in server (ideally not, as it has a lot of bugs and leaks memory), then a new Opc.Ua.ComplexType.Reflection / or .Legacy project with a metapackage to point the old Opc.Ua.Client.ComplexType project to it (which should then be removed). Again, ideally not.

Test wise - same - keep specific tests in the related test projects, e.g. there should be dedicated tests to test the new Opc.Ua.ComplexType project in Opc.Ua.ComplexType.Tests project. These should be unit tests for all files in the Opc.Ua.ComplexType project and can contain integration tests too. Server or client unit tests related to ComplexType code that is only in server/client projects should be int he server/client test projects.

@marcschier
Copy link
Copy Markdown
Collaborator

Ok, as discussed, Opc.Ua.ComplexTypes.Reflection.csproj should contain the old reflection.emit code currently in Opc.Ua.Client.ComplexTypes. Put both server and client code in there, do not worry about splitting. because it would only depend on Opc.Ua.ComplexTypes nuget, not on server/client. Then add a nuspec to generate the old Opc.Ua.Client.ComplexTypes by putting the Opc.Ua.ComplexTypes and .Reflection nugets into that "meta package".

<ProjectReference Include="..\..\Libraries\Opc.Ua.Client\Opc.Ua.Client.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Client.ComplexTypes\Opc.Ua.Client.ComplexTypes.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.ComplexTypes\Opc.Ua.ComplexTypes.csproj" />
</ItemGroup>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should pull this in via Opc.Ua.Client.csproj, not seperate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

complex Complex Types enhancement API or feature enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants