-
-
Notifications
You must be signed in to change notification settings - Fork 362
doc(IDataPackageAdapter): add DataPackageAdapter sample code #6345
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
Conversation
Reviewer's GuideThis PR introduces a new DataPackageAdapter and interface, refactors the TCP socket client to remove built-in package handling in favor of the adapter pattern, updates the sample component and UI to demonstrate toggling and using the adapter for fixed-length data packets, simulates packet fragmentation in the mock server, and refines the IDataPackageHandler interface. Sequence diagram for data receiving with DataPackageAdaptersequenceDiagram
participant Client as TcpSocketClientBase
participant Adapter as DataPackageAdapter
participant Handler as FixLengthDataPackageHandler
participant UI as UI/Log
Client->>Adapter: OnReceivedAsync(data)
Adapter->>Handler: ReceiveAsync(data)
Handler->>Adapter: ReceivedCallBack(data) (when complete packet)
Adapter->>UI: ReceivedCallBack(data)
Class diagram for DataPackageAdapter and related interfacesclassDiagram
class IDataPackageHandler {
+ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token)
+ValueTask ReceiveAsync(ReadOnlyMemory<byte> data, CancellationToken token)
+Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack
}
class IDataPackageAdapter {
+Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack
+IDataPackageHandler? DataPackageHandler
+ValueTask ReceiveAsync(ReadOnlyMemory<byte> data, CancellationToken token)
}
class DataPackageAdapter {
+Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack
+IDataPackageHandler? DataPackageHandler
+ValueTask ReceiveAsync(ReadOnlyMemory<byte> data, CancellationToken token)
-ValueTask OnHandlerReceivedCallBack(ReadOnlyMemory<byte> data)
}
IDataPackageAdapter <|.. DataPackageAdapter
IDataPackageHandler <.. DataPackageAdapter: uses
IDataPackageHandler <.. IDataPackageAdapter: uses
Class diagram for refactored TcpSocketClientBaseclassDiagram
class ITcpSocketClient {
+Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack
+ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token)
+ValueTask ReceiveAsync(Memory<byte> buffer, CancellationToken token)
+ValueTask CloseAsync()
+ValueTask DisposeAsync()
}
class TcpSocketClientBase {
+Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack
+ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token)
+ValueTask ReceiveAsync(Memory<byte> buffer, CancellationToken token)
+ValueTask CloseAsync()
+ValueTask DisposeAsync()
}
ITcpSocketClient <|.. TcpSocketClientBase
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
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.
Hey @ArgoZhang - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `src/BootstrapBlazor/Services/TcpSocket/DataPackage/DataPackageAdapter.cs:25` </location>
<code_context>
+ /// <summary>
+ /// <inheritdoc/>
+ /// </summary>
+ public IDataPackageHandler? DataPackageHandler { get; set; }
+
+ /// <summary>
</code_context>
<issue_to_address>
DataPackageHandler is settable on the adapter, but only gettable on the interface.
This inconsistency may confuse consumers who expect to set DataPackageHandler via the interface.
</issue_to_address>
### Comment 2
<location> `src/BootstrapBlazor/Services/TcpSocket/DataPackage/DataPackageAdapter.cs:15` </location>
<code_context>
+/// common methods for sending, receiving, and handling data packages, as well as a property for accessing the
+/// associated data package handler. Derived classes should override the virtual methods to provide specific behavior
+/// for handling data packages.</remarks>
+public class DataPackageAdapter : IDataPackageAdapter
+{
+ /// <summary>
</code_context>
<issue_to_address>
Consider simplifying callback assignment and method logic by wiring the handler callback in the setter and streamlining ReceiveAsync and the protected callback method.
Here’s one way to keep exactly the same behavior but collapse the two-step “hook up callback on first receive” into a single, simpler setter and eliminate the nested null‐checks in `ReceiveAsync`:
```csharp
public class DataPackageAdapter : IDataPackageAdapter
{
public Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }
private IDataPackageHandler? _handler;
public IDataPackageHandler? DataPackageHandler
{
get => _handler;
set
{
_handler = value;
if (_handler != null)
{
// Wire up once at assignment time
_handler.ReceivedCallBack = OnHandlerReceivedCallBack;
}
}
}
public virtual ValueTask ReceiveAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
{
// Single-line forward, no extra async state machine if handler is null
if (DataPackageHandler is null)
return default;
return DataPackageHandler.ReceiveAsync(data, token);
}
protected virtual ValueTask OnHandlerReceivedCallBack(ReadOnlyMemory<byte> data)
{
// Null-conditional invoke
return ReceivedCallBack != null
? ReceivedCallBack(data)
: default;
}
}
```
Benefits:
- You assign the handler’s callback exactly once in the setter instead of on every receive.
- `ReceiveAsync` becomes a single-line forward, avoiding nested `if`/`await` and the extra generated state machine when there’s no handler.
- The protected callback leverages a null‐conditional return to remove the `async`/`await` boilerplate.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| /// <summary> | ||
| /// <inheritdoc/> | ||
| /// </summary> | ||
| public IDataPackageHandler? DataPackageHandler { get; set; } |
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: DataPackageHandler is settable on the adapter, but only gettable on the interface.
This inconsistency may confuse consumers who expect to set DataPackageHandler via the interface.
| /// common methods for sending, receiving, and handling data packages, as well as a property for accessing the | ||
| /// associated data package handler. Derived classes should override the virtual methods to provide specific behavior | ||
| /// for handling data packages.</remarks> | ||
| public class DataPackageAdapter : IDataPackageAdapter |
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.
issue (complexity): Consider simplifying callback assignment and method logic by wiring the handler callback in the setter and streamlining ReceiveAsync and the protected callback method.
Here’s one way to keep exactly the same behavior but collapse the two-step “hook up callback on first receive” into a single, simpler setter and eliminate the nested null‐checks in ReceiveAsync:
public class DataPackageAdapter : IDataPackageAdapter
{
public Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }
private IDataPackageHandler? _handler;
public IDataPackageHandler? DataPackageHandler
{
get => _handler;
set
{
_handler = value;
if (_handler != null)
{
// Wire up once at assignment time
_handler.ReceivedCallBack = OnHandlerReceivedCallBack;
}
}
}
public virtual ValueTask ReceiveAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
{
// Single-line forward, no extra async state machine if handler is null
if (DataPackageHandler is null)
return default;
return DataPackageHandler.ReceiveAsync(data, token);
}
protected virtual ValueTask OnHandlerReceivedCallBack(ReadOnlyMemory<byte> data)
{
// Null-conditional invoke
return ReceivedCallBack != null
? ReceivedCallBack(data)
: default;
}
}Benefits:
- You assign the handler’s callback exactly once in the setter instead of on every receive.
ReceiveAsyncbecomes a single-line forward, avoiding nestedif/awaitand the extra generated state machine when there’s no handler.- The protected callback leverages a null‐conditional return to remove the
async/awaitboilerplate.
Link issues
fixes #6344
Summary By Copilot
Regression?
Risk
Verification
Packaging changes reviewed?
☑️ Self Check before Merge
Summary by Sourcery
Add IDataPackageAdapter and DataPackageAdapter to abstract packet assembly, refactor the TCP client to use callback-based data handling, and update the Adapters sample page and mock server to showcase the adapter pattern.
New Features:
Enhancements:
Documentation: