-
-
Notifications
You must be signed in to change notification settings - Fork 362
feat(ISocketDataPropertyConverter): add ISocketDataPropertyConverter interface #6422
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
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
cdc930c
feat(ISocketDataConverter): add ISocketDataConverter interface (#6420)
ArgoZhang 9fb2e1f
test: 更新单元测试
ArgoZhang d8db8a3
feat: 增加 SocketDataConverter 标签
ArgoZhang b26bf7a
feat: 增加 Parse 转化器
ArgoZhang 4397d33
test: 增加单元测试
ArgoZhang fdc6d66
feat: 增加无符号数据类型转换
ArgoZhang 5f3a797
test: 增加单元测试
ArgoZhang 27b0b69
refactor: 精简代码
ArgoZhang 7f3db97
test: 更新单元测试
ArgoZhang 68431d3
feat: 增加防止死锁逻辑
ArgoZhang b862acd
test: 增加单元测试
ArgoZhang 97bf8e8
refactor: 重构代码
ArgoZhang cd7d33b
feat: 增加转换器
ArgoZhang d004845
feat: 增加 GetConverterByType 扩展方法
ArgoZhang f02e318
test: 更新单元测试
ArgoZhang 05ea295
test: 增加单元测试
ArgoZhang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
| // See the LICENSE file in the project root for more information. | ||
| // Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone | ||
|
|
||
| using System.Reflection; | ||
| using System.Runtime.Versioning; | ||
| using System.Text; | ||
|
|
||
|
|
@@ -76,20 +77,18 @@ public static void SetDataPackageAdapter(this ITcpSocketClient client, IDataPack | |
| } | ||
|
|
||
| /// <summary> | ||
| /// Configures the specified <see cref="ITcpSocketClient"/> to use a custom data package adapter and a callback | ||
| /// function for processing received data. | ||
| /// Configures the specified <see cref="ITcpSocketClient"/> to use a data package adapter and a callback function | ||
| /// for processing received data. | ||
| /// </summary> | ||
| /// <remarks>This method sets up the <paramref name="client"/> to use the provided <paramref | ||
| /// name="adapter"/> for handling incoming data. The adapter processes the raw data received by the client and | ||
| /// attempts to convert it into an instance of <typeparamref name="TEntity"/>. If the conversion is successful, the | ||
| /// <paramref name="callback"/> is invoked with the converted entity; otherwise, it is invoked with <see | ||
| /// langword="null"/>.</remarks> | ||
| /// <typeparam name="TEntity">The type of the entity that the data package adapter will attempt to convert the received data into.</typeparam> | ||
| /// <param name="client">The <see cref="ITcpSocketClient"/> instance to configure.</param> | ||
| /// <param name="adapter">The <see cref="IDataPackageAdapter"/> instance responsible for handling and processing incoming data.</param> | ||
| /// <param name="callback">A callback function to be invoked with the processed data of type <typeparamref name="TEntity"/>. The callback | ||
| /// receives <see langword="null"/> if the data cannot be converted to <typeparamref name="TEntity"/>.</param> | ||
| public static void SetDataPackageAdapter<TEntity>(this ITcpSocketClient client, IDataPackageAdapter adapter, Func<TEntity?, Task> callback) | ||
| /// <remarks>This method sets up the <paramref name="client"/> to process incoming data using the | ||
| /// specified <paramref name="adapter"/> and <paramref name="socketDataConverter"/>. The <paramref | ||
| /// name="callback"/> is called with the converted entity whenever data is received.</remarks> | ||
| /// <typeparam name="TEntity">The type of the entity that the data will be converted to.</typeparam> | ||
| /// <param name="client">The TCP socket client to configure.</param> | ||
| /// <param name="adapter">The data package adapter responsible for handling incoming data.</param> | ||
| /// <param name="socketDataConverter">The converter used to transform the received data into the specified entity type.</param> | ||
| /// <param name="callback">The callback function to be invoked with the converted entity.</param> | ||
| public static void SetDataPackageAdapter<TEntity>(this ITcpSocketClient client, IDataPackageAdapter adapter, ISocketDataConverter<TEntity> socketDataConverter, Func<TEntity?, Task> callback) | ||
| { | ||
| // 设置 ITcpSocketClient 的回调函数 | ||
| client.ReceivedCallBack = async buffer => | ||
|
|
@@ -102,14 +101,57 @@ public static void SetDataPackageAdapter<TEntity>(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); | ||
| }; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Configures the specified <see cref="ITcpSocketClient"/> to use a custom data package adapter and callback | ||
| /// function. | ||
| /// </summary> | ||
| /// <remarks>This method sets up the <paramref name="client"/> to use the specified <paramref | ||
| /// name="adapter"/> for handling incoming data. If the <typeparamref name="TEntity"/> type is decorated with a <see | ||
| /// cref="SocketDataConverterAttribute"/>, the associated converter is used to transform the data before invoking | ||
| /// the <paramref name="callback"/>. The callback is called with the converted entity or <see langword="null"/> if | ||
| /// conversion fails.</remarks> | ||
| /// <typeparam name="TEntity">The type of entity that the data package adapter will handle.</typeparam> | ||
| /// <param name="client">The TCP socket client to configure.</param> | ||
| /// <param name="adapter">The data package adapter responsible for processing incoming data.</param> | ||
| /// <param name="callback">The callback function to invoke with the processed entity of type <typeparamref name="TEntity"/>.</param> | ||
| public static void SetDataPackageAdapter<TEntity>(this ITcpSocketClient client, IDataPackageAdapter adapter, Func<TEntity?, Task> callback) | ||
| { | ||
| // 设置 ITcpSocketClient 的回调函数 | ||
| client.ReceivedCallBack = async buffer => | ||
| { | ||
| // 将接收到的数据传递给 DataPackageAdapter 进行数据处理合规数据触发 ReceivedCallBack 回调 | ||
| await adapter.HandlerAsync(buffer); | ||
| }; | ||
|
|
||
| var type = typeof(TEntity); | ||
| var converterType = type.GetCustomAttribute<SocketDataConverterAttribute>(); | ||
| if (converterType is { Type: not null }) | ||
| { | ||
| if (Activator.CreateInstance(converterType.Type) is ISocketDataConverter<TEntity> socketDataConverter) | ||
| { | ||
| // 设置 DataPackageAdapter 的回调函数 | ||
| adapter.ReceivedCallBack = async buffer => | ||
| { | ||
| TEntity? ret = default; | ||
| if (socketDataConverter.TryConvertTo(buffer, out var t)) | ||
| { | ||
| ret = t; | ||
| } | ||
| await callback(ret); | ||
| }; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| adapter.ReceivedCallBack = async buffer => await callback(default); | ||
| } | ||
| } | ||
| } | ||
24 changes: 24 additions & 0 deletions
24
src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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([email protected]) Website: https://www.blazor.zone | ||
|
|
||
| namespace BootstrapBlazor.Components; | ||
|
|
||
| /// <summary> | ||
| /// Defines a method to convert raw socket data into a specified entity type. | ||
| /// </summary> | ||
| /// <typeparam name="TEntity">The type of entity to convert the data into.</typeparam> | ||
| public interface ISocketDataConverter<TEntity> | ||
| { | ||
| /// <summary> | ||
| /// Attempts to convert the specified data to an instance of <typeparamref name="TEntity"/>. | ||
| /// </summary> | ||
| /// <remarks>This method does not throw an exception if the conversion fails. Instead, it returns <see | ||
| /// langword="false"/> and sets <paramref name="entity"/> to <see langword="null"/>.</remarks> | ||
| /// <param name="data">The data to be converted, represented as a read-only memory block of bytes.</param> | ||
| /// <param name="entity">When this method returns, contains the converted <typeparamref name="TEntity"/> if the conversion succeeded; | ||
| /// otherwise, <see langword="null"/>.</param> | ||
| /// <returns><see langword="true"/> if the conversion was successful; otherwise, <see langword="false"/>.</returns> | ||
| bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out TEntity? entity); | ||
| } |
18 changes: 18 additions & 0 deletions
18
src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterAttribute.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // 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([email protected]) Website: https://www.blazor.zone | ||
|
|
||
| namespace BootstrapBlazor.Components; | ||
|
|
||
| /// <summary> | ||
| /// | ||
| /// </summary> | ||
| [AttributeUsage(AttributeTargets.Class)] | ||
| public class SocketDataConverterAttribute : Attribute | ||
| { | ||
| /// <summary> | ||
| /// Gets or sets the type of the <see cref="ISocketDataConverter{TEntity}"/>. | ||
| /// </summary> | ||
| public Type? Type { get; set; } | ||
| } |
48 changes: 48 additions & 0 deletions
48
src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterBase.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| // 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([email protected]) Website: https://www.blazor.zone | ||
|
|
||
| using System.Reflection; | ||
|
|
||
| namespace BootstrapBlazor.Components; | ||
|
|
||
| /// <summary> | ||
| /// Provides a base class for converting socket data into a specified entity type. | ||
| /// </summary> | ||
| /// <typeparam name="TEntity">The type of entity to convert the socket data into.</typeparam> | ||
| public abstract class SocketDataConverterBase<TEntity> : ISocketDataConverter<TEntity> | ||
| { | ||
| /// <summary> | ||
| /// <inheritdoc/> | ||
| /// </summary> | ||
| /// <param name="data"></param> | ||
| /// <param name="entity"></param> | ||
| /// <returns></returns> | ||
| public abstract bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out TEntity? entity); | ||
|
|
||
| /// <summary> | ||
| /// 将字节数据转换为指定实体类型的实例。 | ||
| /// </summary> | ||
| /// <param name="data"></param> | ||
| /// <param name="entity"></param> | ||
| protected virtual bool Parse(ReadOnlyMemory<byte> data, TEntity entity) | ||
| { | ||
| // 使用 SocketDataFieldAttribute 特性获取数据转换规则 | ||
| var ret = false; | ||
| if (entity != null) | ||
| { | ||
| var properties = entity.GetType().GetProperties().Where(p => p.CanWrite).ToList(); | ||
| foreach (var p in properties) | ||
| { | ||
| var attr = p.GetCustomAttribute<SocketDataPropertyAttribute>(false); | ||
| if (attr != null) | ||
| { | ||
| p.SetValue(entity, attr.ConvertTo(data)); | ||
| } | ||
| } | ||
| ret = true; | ||
| } | ||
| return ret; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| // 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([email protected]) Website: https://www.blazor.zone | ||
|
|
||
| namespace BootstrapBlazor.Components; | ||
|
|
||
| static class SocketDataPropertyExtensions | ||
| { | ||
| public static ISocketDataPropertyConverter? GetConverter(this SocketDataPropertyAttribute attribute) | ||
| { | ||
| return attribute.GetConverterByType() ?? attribute.GetDefaultConverter(); | ||
| } | ||
|
|
||
| private static ISocketDataPropertyConverter? GetConverterByType(this SocketDataPropertyAttribute attribute) | ||
| { | ||
| ISocketDataPropertyConverter? converter = null; | ||
| var converterType = attribute.ConverterType; | ||
| if (converterType != null) | ||
| { | ||
| var converterParameters = attribute.ConverterParameters; | ||
| var c = Activator.CreateInstance(converterType, converterParameters); | ||
| if(c is ISocketDataPropertyConverter v) | ||
| { | ||
| converter = v; | ||
| } | ||
| } | ||
| return converter; | ||
| } | ||
|
|
||
| private static ISocketDataPropertyConverter? GetDefaultConverter(this SocketDataPropertyAttribute attribute) | ||
| { | ||
| ISocketDataPropertyConverter? converter = null; | ||
| var type = attribute.Type; | ||
| if (type != null) | ||
| { | ||
| if (type == typeof(byte[])) | ||
| { | ||
| converter = new SocketDataByteArrayConverter(); | ||
| } | ||
| else if (type == typeof(string)) | ||
| { | ||
| converter = new SocketDataStringConverter(attribute.EncodingName); | ||
| } | ||
| else if (type.IsEnum) | ||
| { | ||
| converter = new SocketDataEnumConverter(attribute.Type); | ||
| } | ||
| else if (type == typeof(bool)) | ||
| { | ||
| converter = new SocketDataBoolConverter(); | ||
| } | ||
| else if (type == typeof(short)) | ||
| { | ||
| converter = new SocketDataInt16BigEndianConverter(); | ||
| } | ||
| else if (type == typeof(int)) | ||
| { | ||
| converter = new SocketDataInt32BigEndianConverter(); | ||
| } | ||
| else if (type == typeof(long)) | ||
| { | ||
| converter = new SocketDataInt64BigEndianConverter(); | ||
| } | ||
| else if (type == typeof(float)) | ||
| { | ||
| converter = new SocketDataSingleBigEndianConverter(); | ||
| } | ||
| else if (type == typeof(double)) | ||
| { | ||
| converter = new SocketDataDoubleBigEndianConverter(); | ||
| } | ||
| else if (type == typeof(ushort)) | ||
| { | ||
| converter = new SocketDataUInt16BigEndianConverter(); | ||
| } | ||
| else if (type == typeof(uint)) | ||
| { | ||
| converter = new SocketDataUInt32BigEndianConverter(); | ||
| } | ||
| else if (type == typeof(ulong)) | ||
| { | ||
| converter = new SocketDataUInt64BigEndianConverter(); | ||
| } | ||
| } | ||
| return converter; | ||
| } | ||
|
|
||
| public static object? ConvertTo(this SocketDataPropertyAttribute attribute, ReadOnlyMemory<byte> data) | ||
| { | ||
| object? ret = null; | ||
| var start = attribute.Offset; | ||
| var length = attribute.Length; | ||
|
|
||
| if (data.Length >= start + length) | ||
| { | ||
| var buffer = data.Slice(start, length); | ||
| var converter = attribute.GetConverter(); | ||
| if (converter != null) | ||
| { | ||
| ret = converter.Convert(buffer); | ||
| } | ||
| } | ||
| return ret; | ||
| } | ||
| } | ||
19 changes: 19 additions & 0 deletions
19
src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/ISocketDataPropertyConverter.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // 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([email protected]) Website: https://www.blazor.zone | ||
|
|
||
| namespace BootstrapBlazor.Components; | ||
|
|
||
| /// <summary> | ||
| /// Socket 数据转换器接口 | ||
| /// </summary> | ||
| public interface ISocketDataPropertyConverter | ||
| { | ||
| /// <summary> | ||
| /// 将数据转换为指定类型的对象 | ||
| /// </summary> | ||
| /// <param name="data"></param> | ||
| /// <returns></returns> | ||
| object? Convert(ReadOnlyMemory<byte> data); | ||
| } |
26 changes: 26 additions & 0 deletions
26
src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataBoolConverter.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| // 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([email protected]) Website: https://www.blazor.zone | ||
|
|
||
| namespace BootstrapBlazor.Components; | ||
|
|
||
| /// <summary> | ||
| /// Sokcet 数据转换为 bool 数据转换器 | ||
| /// </summary> | ||
| public class SocketDataBoolConverter : ISocketDataPropertyConverter | ||
| { | ||
| /// <summary> | ||
| /// <inheritdoc/> | ||
| /// </summary> | ||
| /// <param name="data"></param> | ||
| public object? Convert(ReadOnlyMemory<byte> data) | ||
| { | ||
| var ret = false; | ||
| if (data.Length == 1) | ||
| { | ||
| ret = data.Span[0] != 0x00; | ||
| } | ||
| return ret; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.