Skip to content

Commit cdc930c

Browse files
authored
feat(ISocketDataConverter): add ISocketDataConverter interface (#6420)
* feat: 增加数据转换器设计 * refactor: 数据处理器增加数据转化器适配 * test: 增加单元测试 * refactor: 更新判断转换成功逻辑
1 parent 4fffebd commit cdc930c

File tree

6 files changed

+87
-47
lines changed

6 files changed

+87
-47
lines changed

src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,18 @@ public static void SetDataPackageAdapter(this ITcpSocketClient client, IDataPack
7676
}
7777

7878
/// <summary>
79-
/// Configures the specified <see cref="ITcpSocketClient"/> to use a custom data package adapter and a callback
80-
/// function for processing received data.
79+
/// Configures the specified <see cref="ITcpSocketClient"/> to use a data package adapter and a callback function
80+
/// for processing received data.
8181
/// </summary>
82-
/// <remarks>This method sets up the <paramref name="client"/> to use the provided <paramref
83-
/// name="adapter"/> for handling incoming data. The adapter processes the raw data received by the client and
84-
/// attempts to convert it into an instance of <typeparamref name="TEntity"/>. If the conversion is successful, the
85-
/// <paramref name="callback"/> is invoked with the converted entity; otherwise, it is invoked with <see
86-
/// langword="null"/>.</remarks>
87-
/// <typeparam name="TEntity">The type of the entity that the data package adapter will attempt to convert the received data into.</typeparam>
88-
/// <param name="client">The <see cref="ITcpSocketClient"/> instance to configure.</param>
89-
/// <param name="adapter">The <see cref="IDataPackageAdapter"/> instance responsible for handling and processing incoming data.</param>
90-
/// <param name="callback">A callback function to be invoked with the processed data of type <typeparamref name="TEntity"/>. The callback
91-
/// receives <see langword="null"/> if the data cannot be converted to <typeparamref name="TEntity"/>.</param>
92-
public static void SetDataPackageAdapter<TEntity>(this ITcpSocketClient client, IDataPackageAdapter adapter, Func<TEntity?, Task> callback)
82+
/// <remarks>This method sets up the <paramref name="client"/> to process incoming data using the
83+
/// specified <paramref name="adapter"/> and <paramref name="socketDataConverter"/>. The <paramref
84+
/// name="callback"/> is called with the converted entity whenever data is received.</remarks>
85+
/// <typeparam name="TEntity">The type of the entity that the data will be converted to.</typeparam>
86+
/// <param name="client">The TCP socket client to configure.</param>
87+
/// <param name="adapter">The data package adapter responsible for handling incoming data.</param>
88+
/// <param name="socketDataConverter">The converter used to transform the received data into the specified entity type.</param>
89+
/// <param name="callback">The callback function to be invoked with the converted entity.</param>
90+
public static void SetDataPackageAdapter<TEntity>(this ITcpSocketClient client, IDataPackageAdapter adapter, ISocketDataConverter<TEntity> socketDataConverter, Func<TEntity?, Task> callback)
9391
{
9492
// 设置 ITcpSocketClient 的回调函数
9593
client.ReceivedCallBack = async buffer =>
@@ -102,12 +100,9 @@ public static void SetDataPackageAdapter<TEntity>(this ITcpSocketClient client,
102100
adapter.ReceivedCallBack = async buffer =>
103101
{
104102
TEntity? ret = default;
105-
if (adapter.TryConvertTo(buffer, out var t))
103+
if (socketDataConverter.TryConvertTo(buffer, out var t))
106104
{
107-
if (t is TEntity entity)
108-
{
109-
ret = entity;
110-
}
105+
ret = t;
111106
}
112107
await callback(ret);
113108
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
5+
6+
namespace BootstrapBlazor.Components;
7+
8+
/// <summary>
9+
/// Defines a method to convert raw socket data into a specified entity type.
10+
/// </summary>
11+
/// <typeparam name="TEntity">The type of entity to convert the data into.</typeparam>
12+
public interface ISocketDataConverter<TEntity>
13+
{
14+
/// <summary>
15+
/// Attempts to convert the specified data to an instance of <typeparamref name="TEntity"/>.
16+
/// </summary>
17+
/// <remarks>This method does not throw an exception if the conversion fails. Instead, it returns <see
18+
/// langword="false"/> and sets <paramref name="entity"/> to <see langword="null"/>.</remarks>
19+
/// <param name="data">The data to be converted, represented as a read-only memory block of bytes.</param>
20+
/// <param name="entity">When this method returns, contains the converted <typeparamref name="TEntity"/> if the conversion succeeded;
21+
/// otherwise, <see langword="null"/>.</param>
22+
/// <returns><see langword="true"/> if the conversion was successful; otherwise, <see langword="false"/>.</returns>
23+
bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out TEntity? entity);
24+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
5+
6+
namespace BootstrapBlazor.Components;
7+
8+
/// <summary>
9+
/// Provides a base class for converting socket data into a specified entity type.
10+
/// </summary>
11+
/// <typeparam name="TEntity">The type of entity to convert the socket data into.</typeparam>
12+
public abstract class SocketDataConverterBase<TEntity> : ISocketDataConverter<TEntity>
13+
{
14+
/// <summary>
15+
/// <inheritdoc/>
16+
/// </summary>
17+
/// <param name="data"></param>
18+
/// <param name="entity"></param>
19+
/// <returns></returns>
20+
public abstract bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out TEntity? entity);
21+
}

src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,18 @@ public virtual async ValueTask HandlerAsync(ReadOnlyMemory<byte> data, Cancellat
4848
/// <inheritdoc/>
4949
/// </summary>
5050
/// <param name="data"></param>
51+
/// <param name="socketDataConverter"></param>
5152
/// <param name="entity"></param>
5253
/// <returns></returns>
53-
public virtual bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out object? entity)
54+
public virtual bool TryConvertTo<TEntity>(ReadOnlyMemory<byte> data, ISocketDataConverter<TEntity> socketDataConverter, out TEntity? entity)
5455
{
55-
entity = null;
56-
return false;
56+
entity = default;
57+
var ret = socketDataConverter.TryConvertTo(data, out var v);
58+
if (ret)
59+
{
60+
entity = v;
61+
}
62+
return ret;
5763
}
5864

5965
/// <summary>

src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@ public interface IDataPackageAdapter
4141
ValueTask HandlerAsync(ReadOnlyMemory<byte> data, CancellationToken token = default);
4242

4343
/// <summary>
44-
/// Attempts to convert the specified binary data into an object representation.
44+
/// Attempts to convert the specified byte data into an entity of type <typeparamref name="TEntity"/>.
4545
/// </summary>
46-
/// <remarks>This method does not throw an exception if the conversion fails. Instead, it returns <see
47-
/// langword="false"/> and sets <paramref name="entity"/> to <see langword="null"/>.</remarks>
48-
/// <param name="data">The binary data to be converted. Must not be empty.</param>
49-
/// <param name="entity">When this method returns <see langword="true"/>, contains the converted object. When this method returns <see
50-
/// langword="false"/>, contains <see langword="null"/>.</param>
46+
/// <remarks>This method does not throw an exception if the conversion fails. Instead, it returns <see
47+
/// langword="false"/> and sets <paramref name="entity"/> to its default value.</remarks>
48+
/// <typeparam name="TEntity">The type of the entity to convert the data to.</typeparam>
49+
/// <param name="data">The byte data to be converted.</param>
50+
/// <param name="socketDataConverter">The converter used to transform the byte data into an entity.</param>
51+
/// <param name="entity">When this method returns, contains the converted entity if the conversion was successful; otherwise, the default
52+
/// value for the type of the entity.</param>
5153
/// <returns><see langword="true"/> if the conversion was successful; otherwise, <see langword="false"/>.</returns>
52-
bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out object? entity);
54+
bool TryConvertTo<TEntity>(ReadOnlyMemory<byte> data, ISocketDataConverter<TEntity> socketDataConverter, out TEntity? entity);
5355
}

test/UnitTest/Services/TcpSocketFactoryTest.cs

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -651,11 +651,11 @@ public async Task TryConvertTo_Ok()
651651
MockEntity? entity = null;
652652

653653
// 设置数据适配器
654-
var adapter = new MockEntityDataPackageAdapter
654+
var adapter = new DataPackageAdapter
655655
{
656656
DataPackageHandler = new FixLengthDataPackageHandler(7),
657657
};
658-
client.SetDataPackageAdapter<MockEntity>(adapter, t =>
658+
client.SetDataPackageAdapter(adapter, new MockEntitySocketDataConverter(), t =>
659659
{
660660
entity = t;
661661
tcs.SetResult();
@@ -676,9 +676,10 @@ public async Task TryConvertTo_Ok()
676676

677677
// 测试异常流程
678678
var adapter2 = new DataPackageAdapter();
679-
var result = adapter2.TryConvertTo(data, out var t);
680-
Assert.False(result);
681-
Assert.Null(t);
679+
var result = adapter2.TryConvertTo(data, new MockEntitySocketDataConverter(), out var t);
680+
Assert.True(result);
681+
Assert.NotNull(t);
682+
Assert.Equal([1, 2, 3, 4, 5], entity.Header);
682683
}
683684

684685
[Fact]
@@ -691,11 +692,11 @@ public async Task TryConvertTo_Null()
691692
MockEntity? entity = null;
692693

693694
// 设置数据适配器
694-
var adapter = new MockErrorEntityDataPackageAdapter
695+
var adapter = new DataPackageAdapter
695696
{
696697
DataPackageHandler = new FixLengthDataPackageHandler(7),
697698
};
698-
client.SetDataPackageAdapter<MockEntity>(adapter, t =>
699+
client.SetDataPackageAdapter(adapter, new MockEntitySocketDataConverter(), t =>
699700
{
700701
entity = t;
701702
tcs.SetResult();
@@ -710,7 +711,7 @@ public async Task TryConvertTo_Null()
710711
await client.SendAsync(data);
711712
await tcs.Task;
712713

713-
Assert.Null(entity);
714+
Assert.NotNull(entity);
714715
}
715716

716717
private static TcpListener StartTcpServer(int port, Func<TcpClient, Task> handler)
@@ -1080,9 +1081,9 @@ public void SetReceive(bool state)
10801081
}
10811082
}
10821083

1083-
class MockEntityDataPackageAdapter : DataPackageAdapter
1084+
class MockEntitySocketDataConverter : SocketDataConverterBase<MockEntity>
10841085
{
1085-
public override bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out object? entity)
1086+
public override bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out MockEntity? entity)
10861087
{
10871088
entity = new MockEntity
10881089
{
@@ -1093,15 +1094,6 @@ public override bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)]
10931094
}
10941095
}
10951096

1096-
class MockErrorEntityDataPackageAdapter : DataPackageAdapter
1097-
{
1098-
public override bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out object? entity)
1099-
{
1100-
entity = new Foo();
1101-
return true;
1102-
}
1103-
}
1104-
11051097
class MockEntity
11061098
{
11071099
public byte[]? Header { get; set; }

0 commit comments

Comments
 (0)