-
Notifications
You must be signed in to change notification settings - Fork 1k
Implement dependency injection for application instance, server and n… #3022
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
6feb171
to
357cb10
Compare
357cb10
to
c326401
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #3022 +/- ##
==========================================
+ Coverage 56.64% 56.65% +0.01%
==========================================
Files 356 359 +3
Lines 68435 68477 +42
Branches 14072 14080 +8
==========================================
+ Hits 38765 38797 +32
- Misses 25491 25493 +2
- Partials 4179 4187 +8 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
/azp run |
Pull request contains merge conflicts. |
c326401
to
7dfa024
Compare
/azp run |
Azure Pipelines successfully started running 1 pipeline(s). |
ConfigSectionName = configSectionName, | ||
CertificatePasswordProvider = PasswordProvider | ||
}; | ||
var passwordProvider = new CertificatePasswordProvider(Password); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
previously LoadAsync created a new application instance, now it doesnt.
I need to check...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, now this object comes from the DI. This was the intension.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah but if you call load twice previously a new instance was created now the Same is reused. I need to Check If this could cause unwanted behaviour changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, for this shadow config use case, right? I think this works becauce LoadApplicationConfiguration sets the application configuration to null before relaoding it so it is always a complete new instance of the configuration.
But what is the shadow configuration use case for excatly? do you know?
6cce6c8
to
c4b74f7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this valuable additions. We really appreciate it.
Please add the new constructors as an option in addition to the existing ones. We cannot break all existing implementations with this PR.
@wbtoms I will gradually introduce these changes with multiple PRs in a more compatible manner. Thank you for all the work you put into this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces dependency injection for core server components to improve testability and enable easier integration of the UA-.NETStandard library. The changes add interfaces for key server classes and provide DI service registration methods.
- Introduces interfaces for major server components (IStandardServer, IServerInternal, IApplicationInstance, etc.)
- Adds service collection extension methods for registering server services
- Updates tests to use dependency injection for server instantiation
Reviewed Changes
Copilot reviewed 49 out of 50 changed files in this pull request and generated 6 comments.
Show a summary per file
File | Description |
---|---|
Multiple test files | Updated to use DI container for server instantiation |
IStandardServer.cs | New interface for StandardServer |
IApplicationInstance.cs | New interface for ApplicationInstance |
IServerInternal.cs | Enhanced interface with additional methods |
ServiceCollectionExtensions.cs | DI registration methods |
StandardServer.cs | Implements IStandardServer, requires DI parameters |
Various other interface files | New interfaces for core components |
NodeId[] testSet = CommonTestWorkers.NodeIdTestSetStatic.Select(n => ExpandedNodeId.ToNodeId(n, namespaceUris)).ToArray(); | ||
transferRequestHeader.Timestamp = DateTime.UtcNow; | ||
var subscriptionIds = CommonTestWorkers.CreateSubscriptionForTransfer(serverTestServices, transferRequestHeader, testSet, kQueueSize, -1); | ||
var subscriptionIds = CommonTestWorkers.CreateSubscriptionForTransfer(serverTestServices, transferRequestHeader, testSet, kQueueSize); |
Copilot
AI
Jul 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method call is missing a parameter. The original call had 5 parameters including a -1 value, but now only 4 parameters are provided. This suggests the method signature changed but this call wasn't fully updated.
var subscriptionIds = CommonTestWorkers.CreateSubscriptionForTransfer(serverTestServices, transferRequestHeader, testSet, kQueueSize); | |
var subscriptionIds = CommonTestWorkers.CreateSubscriptionForTransfer(serverTestServices, transferRequestHeader, testSet, kQueueSize, -1); |
Copilot uses AI. Check for mistakes.
m_nodeManagerFactories = new List<INodeManagerFactory>(); | ||
m_applicationInstance = applicationInstance ?? throw new ArgumentNullException(nameof(applicationInstance)); | ||
m_serverInternal = serverInternal ?? throw new ArgumentNullException(nameof(serverInternal)); | ||
m_mainNodeManagerFactory = mainNodeManagerFactory ?? throw new ArgumentNullException(nameof(mainNodeManagerFactory)); ; |
Copilot
AI
Jul 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an extra semicolon at the end of this line. The double semicolon should be reduced to a single semicolon.
m_mainNodeManagerFactory = mainNodeManagerFactory ?? throw new ArgumentNullException(nameof(mainNodeManagerFactory)); ; | |
m_mainNodeManagerFactory = mainNodeManagerFactory ?? throw new ArgumentNullException(nameof(mainNodeManagerFactory)); |
Copilot uses AI. Check for mistakes.
public void CreateServerObject(EventManager eventManager, ResourceManager resourceManager, RequestManager requestManager); | ||
|
||
/// <summary> | ||
/// Stores the MasterNodeManager and the CoreNodeManager | ||
/// </summary> | ||
/// <param name="nodeManager">The node manager.</param> | ||
public void SetNodeManager(IMasterNodeManager nodeManager); | ||
|
||
/// <summary> | ||
/// Stores the SessionManager, the SubscriptionManager in the datastore. | ||
/// </summary> | ||
/// <param name="sessionManager">The session manager.</param> | ||
/// <param name="subscriptionManager">The subscription manager.</param> | ||
public void SetSessionManager(SessionManager sessionManager, SubscriptionManager subscriptionManager); |
Copilot
AI
Jul 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This interface method should not use the 'public' access modifier within an interface definition. Interface methods are implicitly public.
public void CreateServerObject(EventManager eventManager, ResourceManager resourceManager, RequestManager requestManager); | |
/// <summary> | |
/// Stores the MasterNodeManager and the CoreNodeManager | |
/// </summary> | |
/// <param name="nodeManager">The node manager.</param> | |
public void SetNodeManager(IMasterNodeManager nodeManager); | |
/// <summary> | |
/// Stores the SessionManager, the SubscriptionManager in the datastore. | |
/// </summary> | |
/// <param name="sessionManager">The session manager.</param> | |
/// <param name="subscriptionManager">The subscription manager.</param> | |
public void SetSessionManager(SessionManager sessionManager, SubscriptionManager subscriptionManager); | |
void CreateServerObject(EventManager eventManager, ResourceManager resourceManager, RequestManager requestManager); | |
/// <summary> | |
/// Stores the MasterNodeManager and the CoreNodeManager | |
/// </summary> | |
/// <param name="nodeManager">The node manager.</param> | |
void SetNodeManager(IMasterNodeManager nodeManager); | |
/// <summary> | |
/// Stores the SessionManager, the SubscriptionManager in the datastore. | |
/// </summary> | |
/// <param name="sessionManager">The session manager.</param> | |
/// <param name="subscriptionManager">The subscription manager.</param> | |
void SetSessionManager(SessionManager sessionManager, SubscriptionManager subscriptionManager); |
Copilot uses AI. Check for mistakes.
public void CreateServerObject(EventManager eventManager, ResourceManager resourceManager, RequestManager requestManager); | ||
|
||
/// <summary> | ||
/// Stores the MasterNodeManager and the CoreNodeManager | ||
/// </summary> | ||
/// <param name="nodeManager">The node manager.</param> | ||
public void SetNodeManager(IMasterNodeManager nodeManager); | ||
|
||
/// <summary> | ||
/// Stores the SessionManager, the SubscriptionManager in the datastore. | ||
/// </summary> | ||
/// <param name="sessionManager">The session manager.</param> | ||
/// <param name="subscriptionManager">The subscription manager.</param> | ||
public void SetSessionManager(SessionManager sessionManager, SubscriptionManager subscriptionManager); |
Copilot
AI
Jul 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This interface method should not use the 'public' access modifier within an interface definition. Interface methods are implicitly public.
public void CreateServerObject(EventManager eventManager, ResourceManager resourceManager, RequestManager requestManager); | |
/// <summary> | |
/// Stores the MasterNodeManager and the CoreNodeManager | |
/// </summary> | |
/// <param name="nodeManager">The node manager.</param> | |
public void SetNodeManager(IMasterNodeManager nodeManager); | |
/// <summary> | |
/// Stores the SessionManager, the SubscriptionManager in the datastore. | |
/// </summary> | |
/// <param name="sessionManager">The session manager.</param> | |
/// <param name="subscriptionManager">The subscription manager.</param> | |
public void SetSessionManager(SessionManager sessionManager, SubscriptionManager subscriptionManager); | |
void CreateServerObject(EventManager eventManager, ResourceManager resourceManager, RequestManager requestManager); | |
/// <summary> | |
/// Stores the MasterNodeManager and the CoreNodeManager | |
/// </summary> | |
/// <param name="nodeManager">The node manager.</param> | |
void SetNodeManager(IMasterNodeManager nodeManager); | |
/// <summary> | |
/// Stores the SessionManager, the SubscriptionManager in the datastore. | |
/// </summary> | |
/// <param name="sessionManager">The session manager.</param> | |
/// <param name="subscriptionManager">The subscription manager.</param> | |
void SetSessionManager(SessionManager sessionManager, SubscriptionManager subscriptionManager); |
Copilot uses AI. Check for mistakes.
/// </summary> | ||
/// <param name="sessionManager">The session manager.</param> | ||
/// <param name="subscriptionManager">The subscription manager.</param> | ||
public void SetSessionManager(SessionManager sessionManager, SubscriptionManager subscriptionManager); |
Copilot
AI
Jul 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This interface method should not use the 'public' access modifier within an interface definition. Interface methods are implicitly public.
public void SetSessionManager(SessionManager sessionManager, SubscriptionManager subscriptionManager); | |
void SetSessionManager(SessionManager sessionManager, SubscriptionManager subscriptionManager); |
Copilot uses AI. Check for mistakes.
/// <summary> | ||
/// Stores the MonitoredItemQueueFactory in the datastore. | ||
/// </summary> | ||
/// <param name="monitoredItemQueueFactory">The MonitoredItemQueueFactory.</param> |
Copilot
AI
Jul 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] This method is missing documentation while other methods in the interface have XML documentation comments. For consistency, all public interface methods should be documented.
/// <param name="monitoredItemQueueFactory">The MonitoredItemQueueFactory.</param> | |
/// <param name="monitoredItemQueueFactory">The MonitoredItemQueueFactory.</param> | |
/// <summary> | |
/// Stores the MonitoredItemQueueFactory in the datastore. | |
/// </summary> | |
/// <param name="monitoredItemQueueFactory">The factory used to create monitored item queues.</param> |
Copilot uses AI. Check for mistakes.
Proposed changes
When integrating UA-.NETStandard, it is difficult to perform unit tests or even integration while you basically need to setup almost all objects from the application instance to the node managers with their actual implementations. No possibility to mock them or to create what is just necessary.
It is also diffiicult to replace an implementation of a given component by another custom implementation.
Therefore I propose this change which is, a first step, to introduce dependency injections for:
Related Issues
No related open issue found.
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.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.Further comments
If this proposition is interresting and at some point is merged, it might be interresting also to continue with orther types of object especially everything around NodeState. And of course, the client library should be treated the same way. But before going further, let see if this make sense for the audience.