diff --git a/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs b/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs index cf58a2b31ad..feafc612089 100644 --- a/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs +++ b/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs @@ -76,20 +76,18 @@ public static void SetDataPackageAdapter(this ITcpSocketClient client, IDataPack } /// - /// Configures the specified to use a custom data package adapter and a callback - /// function for processing received data. + /// Configures the specified to use a data package adapter and a callback function + /// for processing received data. /// - /// This method sets up the to use the provided for handling incoming data. The adapter processes the raw data received by the client and - /// attempts to convert it into an instance of . If the conversion is successful, the - /// is invoked with the converted entity; otherwise, it is invoked with . - /// The type of the entity that the data package adapter will attempt to convert the received data into. - /// The instance to configure. - /// The instance responsible for handling and processing incoming data. - /// A callback function to be invoked with the processed data of type . The callback - /// receives if the data cannot be converted to . - public static void SetDataPackageAdapter(this ITcpSocketClient client, IDataPackageAdapter adapter, Func callback) + /// This method sets up the to process incoming data using the + /// specified and . The is called with the converted entity whenever data is received. + /// The type of the entity that the data will be converted to. + /// The TCP socket client to configure. + /// The data package adapter responsible for handling incoming data. + /// The converter used to transform the received data into the specified entity type. + /// The callback function to be invoked with the converted entity. + public static void SetDataPackageAdapter(this ITcpSocketClient client, IDataPackageAdapter adapter, ISocketDataConverter socketDataConverter, Func callback) { // 设置 ITcpSocketClient 的回调函数 client.ReceivedCallBack = async buffer => @@ -102,12 +100,9 @@ public static void SetDataPackageAdapter(this ITcpSocketClient client, adapter.ReceivedCallBack = async buffer => { TEntity? ret = default; - if (adapter.TryConvertTo(buffer, out var t)) + if (socketDataConverter.TryConvertTo(buffer, out var t)) { - if (t is TEntity entity) - { - ret = entity; - } + ret = t; } await callback(ret); }; diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs new file mode 100644 index 00000000000..1d5f3f6b0d7 --- /dev/null +++ b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License +// See the LICENSE file in the project root for more information. +// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone + +namespace BootstrapBlazor.Components; + +/// +/// Defines a method to convert raw socket data into a specified entity type. +/// +/// The type of entity to convert the data into. +public interface ISocketDataConverter +{ + /// + /// Attempts to convert the specified data to an instance of . + /// + /// This method does not throw an exception if the conversion fails. Instead, it returns and sets to . + /// The data to be converted, represented as a read-only memory block of bytes. + /// When this method returns, contains the converted if the conversion succeeded; + /// otherwise, . + /// if the conversion was successful; otherwise, . + bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out TEntity? entity); +} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterBase.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterBase.cs new file mode 100644 index 00000000000..2375587a94c --- /dev/null +++ b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterBase.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License +// See the LICENSE file in the project root for more information. +// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone + +namespace BootstrapBlazor.Components; + +/// +/// Provides a base class for converting socket data into a specified entity type. +/// +/// The type of entity to convert the socket data into. +public abstract class SocketDataConverterBase : ISocketDataConverter +{ + /// + /// + /// + /// + /// + /// + public abstract bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out TEntity? entity); +} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs b/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs index a1b7e98e940..77733014514 100644 --- a/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs +++ b/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs @@ -48,12 +48,18 @@ public virtual async ValueTask HandlerAsync(ReadOnlyMemory data, Cancellat /// /// /// + /// /// /// - public virtual bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out object? entity) + public virtual bool TryConvertTo(ReadOnlyMemory data, ISocketDataConverter socketDataConverter, out TEntity? entity) { - entity = null; - return false; + entity = default; + var ret = socketDataConverter.TryConvertTo(data, out var v); + if (ret) + { + entity = v; + } + return ret; } /// diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs b/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs index f603c528460..e37d9272495 100644 --- a/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs +++ b/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs @@ -41,13 +41,15 @@ public interface IDataPackageAdapter ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default); /// - /// Attempts to convert the specified binary data into an object representation. + /// Attempts to convert the specified byte data into an entity of type . /// - /// This method does not throw an exception if the conversion fails. Instead, it returns and sets to . - /// The binary data to be converted. Must not be empty. - /// When this method returns , contains the converted object. When this method returns , contains . + /// This method does not throw an exception if the conversion fails. Instead, it returns and sets to its default value. + /// The type of the entity to convert the data to. + /// The byte data to be converted. + /// The converter used to transform the byte data into an entity. + /// When this method returns, contains the converted entity if the conversion was successful; otherwise, the default + /// value for the type of the entity. /// if the conversion was successful; otherwise, . - bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out object? entity); + bool TryConvertTo(ReadOnlyMemory data, ISocketDataConverter socketDataConverter, out TEntity? entity); } diff --git a/test/UnitTest/Services/TcpSocketFactoryTest.cs b/test/UnitTest/Services/TcpSocketFactoryTest.cs index b552daedf06..72bab657d70 100644 --- a/test/UnitTest/Services/TcpSocketFactoryTest.cs +++ b/test/UnitTest/Services/TcpSocketFactoryTest.cs @@ -651,11 +651,11 @@ public async Task TryConvertTo_Ok() MockEntity? entity = null; // 设置数据适配器 - var adapter = new MockEntityDataPackageAdapter + var adapter = new DataPackageAdapter { DataPackageHandler = new FixLengthDataPackageHandler(7), }; - client.SetDataPackageAdapter(adapter, t => + client.SetDataPackageAdapter(adapter, new MockEntitySocketDataConverter(), t => { entity = t; tcs.SetResult(); @@ -676,9 +676,10 @@ public async Task TryConvertTo_Ok() // 测试异常流程 var adapter2 = new DataPackageAdapter(); - var result = adapter2.TryConvertTo(data, out var t); - Assert.False(result); - Assert.Null(t); + var result = adapter2.TryConvertTo(data, new MockEntitySocketDataConverter(), out var t); + Assert.True(result); + Assert.NotNull(t); + Assert.Equal([1, 2, 3, 4, 5], entity.Header); } [Fact] @@ -691,11 +692,11 @@ public async Task TryConvertTo_Null() MockEntity? entity = null; // 设置数据适配器 - var adapter = new MockErrorEntityDataPackageAdapter + var adapter = new DataPackageAdapter { DataPackageHandler = new FixLengthDataPackageHandler(7), }; - client.SetDataPackageAdapter(adapter, t => + client.SetDataPackageAdapter(adapter, new MockEntitySocketDataConverter(), t => { entity = t; tcs.SetResult(); @@ -710,7 +711,7 @@ public async Task TryConvertTo_Null() await client.SendAsync(data); await tcs.Task; - Assert.Null(entity); + Assert.NotNull(entity); } private static TcpListener StartTcpServer(int port, Func handler) @@ -1080,9 +1081,9 @@ public void SetReceive(bool state) } } - class MockEntityDataPackageAdapter : DataPackageAdapter + class MockEntitySocketDataConverter : SocketDataConverterBase { - public override bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out object? entity) + public override bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out MockEntity? entity) { entity = new MockEntity { @@ -1093,15 +1094,6 @@ public override bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] } } - class MockErrorEntityDataPackageAdapter : DataPackageAdapter - { - public override bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out object? entity) - { - entity = new Foo(); - return true; - } - } - class MockEntity { public byte[]? Header { get; set; }