diff --git a/src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor b/src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor index e06b1682265..58b8bfd2760 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor @@ -84,22 +84,22 @@ private async Task CreateClient()

数据适配器设计思路如下

    -
  1. 使用 SocketDataConverterAttribute 标签约定通讯数据使用那个转换类型进行转换 指定类型需继承 ISocketDataConverter +
  2. 使用 SocketDataTypeConverterAttribute 标签约定通讯数据使用那个转换类型进行转换 指定类型需继承 ISocketDataConverter 接口
  3. -
  4. 使用 SocketDataPropertyAttribute 标签约定如何转换数据类型 (Property) 属性值
  5. +
  6. 使用 SocketDataPropertyConverterAttribute 标签约定如何转换数据类型 (Property) 属性值
-
[SocketDataConverter(Type = typeof(SocketDataConverter<MockEntity>))]
+
[SocketDataTypeConverter(Type = typeof(SocketDataConverter<MockEntity>))]
 class MockEntity
 {
-    [SocketDataProperty(Type = typeof(byte[]), Offset = 0, Length = 5)]
+    [SocketDataPropertyConverter(Type = typeof(byte[]), Offset = 0, Length = 5)]
     public byte[]? Header { get; set; }
 
-    [SocketDataProperty(Type = typeof(byte[]), Offset = 5, Length = 2)]
+    [SocketDataPropertyConverter(Type = typeof(byte[]), Offset = 5, Length = 2)]
     public byte[]? Body { get; set; }
 
-    [SocketDataProperty(Type = typeof(Foo), Offset = 7, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])]
+    [SocketDataPropertyConverter(Type = typeof(Foo), Offset = 7, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])]
     public string? Value1 { get; set; }
 }
diff --git a/src/BootstrapBlazor.Server/Components/Samples/Sockets/DataEntities.razor b/src/BootstrapBlazor.Server/Components/Samples/Sockets/DataEntities.razor index abc9669f65a..f42a04f24c7 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Sockets/DataEntities.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Sockets/DataEntities.razor @@ -7,62 +7,30 @@ -

通过 SocketDataConverterAttribute 类标签与 SocketDataPropertyAttribute +

+ 通过 SocketDataTypeConverterAttribute 类标签与 SocketDataPropertyConverterAttribute 属性标签可以将通讯数据自动转化为我们系统中需要的业务实体类,示例如下:

-
[SocketDataConverter(Type = typeof(SocketDataConverter<MockEntity>))]
+    
[SocketDataTypeConverter(Type = typeof(SocketDataConverter<MockEntity>))]
 class MockEntity
 {
-    [SocketDataProperty(Type = typeof(byte[]), Offset = 0, Length = 5)]
+    [SocketDataPropertyConverter(Type = typeof(byte[]), Offset = 0, Length = 5)]
     public byte[]? Header { get; set; }
 
-    [SocketDataProperty(Type = typeof(byte[]), Offset = 5, Length = 2)]
+    [SocketDataPropertyConverter(Type = typeof(byte[]), Offset = 5, Length = 2)]
     public byte[]? Body { get; set; }
 
-    [SocketDataProperty(Type = typeof(string), Offset = 7, Length = 1, EncodingName = "utf-8")]
+    [SocketDataPropertyConverter(Type = typeof(string), Offset = 7, Length = 1, EncodingName = "utf-8")]
     public string? Value1 { get; set; }
 
-    [SocketDataProperty(Type = typeof(int), Offset = 8, Length = 1)]
+    [SocketDataPropertyConverter(Type = typeof(int), Offset = 8, Length = 1)]
     public int Value2 { get; set; }
 }
-

1. SocketDataConverter 参数说明:

+

1. SocketDataTypeConverter 参数说明:

  • Type: 自定义转换器类型,组件库内置了 SocketDataConverter 泛型类,建议看一下源码非常方便扩展出自己的转换器
-
class MockEntitySocketDataConverter : SocketDataConverterBase<MockEntity>
-{
-    public override bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out MockEntity? entity)
-    {
-        var v = new MockEntity();
-
-        // 调用基类的 Parse 方法即可
-        var ret = Parse(data, v);
-        entity = ret ? v : null;
-        return ret;
-    }
-}
-

Parse 方法也是可以重载自定义实现的,内置实现逻辑如下:

-
protected virtual bool Parse(ReadOnlyMemory<byte> data, TEntity entity)
-{
-    // 使用 SocketDataPropertyAttribute 特性获取数据转换规则
-    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;
-}
-

即遍历所有有 SocketDataPropertyAttribute 标签的属性对其进行过自动赋值

-

2. SocketDataPropertyAttribute 参数说明

+

2. SocketDataPropertyConverterAttribute 参数说明

  • Type: 转换目标数据类型
  • Offset: 数据偏移量,即在接收到的数据中起始位置
  • @@ -95,16 +63,16 @@ class MockEntity
  • SocketDataUInt64LittleEndianConverter 转成 ulong 无符号整形小端读取

自定义数据类型转化器示例

-
[SocketDataConverter(Type = typeof(SocketDataConverter<MockEntity>))]
+    
[SocketTypeDataConverter(Type = typeof(SocketDataConverter<MockEntity>))]
 class MockEntity
 {
-    [SocketDataProperty(Type = typeof(byte[]), Offset = 0, Length = 5)]
+    [SocketDataPropertyConverter(Type = typeof(byte[]), Offset = 0, Length = 5)]
     public byte[]? Header { get; set; }
 
-    [SocketDataProperty(Type = typeof(byte[]), Offset = 5, Length = 2)]
+    [SocketDataPropertyConverter(Type = typeof(byte[]), Offset = 5, Length = 2)]
     public byte[]? Body { get; set; }
 
-    [SocketDataProperty(Type = typeof(Foo), Offset = 7, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])]
+    [SocketDataPropertyConverter(Type = typeof(Foo), Offset = 7, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])]
     public string? Value1 { get; set; }
 }
class FooConverter(string name) : ISocketDataPropertyConverter
diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json
index 612f08e7996..57bc7a42645 100644
--- a/src/BootstrapBlazor.Server/Locales/en-US.json
+++ b/src/BootstrapBlazor.Server/Locales/en-US.json
@@ -7129,7 +7129,7 @@
     "DataEntityTitle": "Socket Auto Entity",
     "DataEntityDescription": "After receiving the communication data, it is automatically converted into the entity class required by the business",
     "NormalTitle": "Basic usage",
-    "NormalIntro": "Enable automatic data conversion through the SocketDataConverterAttribute tag"
+    "NormalIntro": "Enable automatic data conversion through the SocketDataTypeConverterAttribute attribute"
   },
   "BootstrapBlazor.Server.Components.Samples.NetworkMonitors": {
     "NetworkMonitorTitle": "NetworkMonitor",
diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json
index 61a1c4dba1a..c4a0301e360 100644
--- a/src/BootstrapBlazor.Server/Locales/zh-CN.json
+++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json
@@ -7129,7 +7129,7 @@
     "DataEntityTitle": "Socket 数据转化实体类",
     "DataEntityDescription": "接收到通讯数据后自动转成业务需要的实体类",
     "NormalTitle": "基本用法",
-    "NormalIntro": "通过 SocketDataConverterAttribute 标签开启数据自动转换功能"
+    "NormalIntro": "通过 SocketDataTypeConverterAttribute 标签开启数据自动转换功能"
   },
   "BootstrapBlazor.Server.Components.Samples.NetworkMonitors": {
     "NetworkMonitorTitle": "NetworkMonitor 网络状态",
diff --git a/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs b/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs
index bf57e690b9f..e18ce3df56f 100644
--- a/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs
+++ b/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs
@@ -117,6 +117,7 @@ public static IServiceCollection AddBootstrapBlazor(this IServiceCollection serv
 
         services.AddTabItemBindOptions();
         services.AddIconTheme();
+        services.AddSocketDataConverters();
         return services;
     }
 
@@ -212,7 +213,30 @@ static IServiceCollection AddTabItemBindOptions(this IServiceCollection services
     }
 
     /// 
-    /// 增加第三方菜单路由与 Tab 捆绑字典配置
+    /// 增加 Socket 数据转换器集合配置项服务
+    /// 
+    /// 
+    /// 
+    static IServiceCollection AddSocketDataConverters(this IServiceCollection services)
+    {
+        services.AddOptionsMonitor();
+        return services;
+    }
+
+    /// 
+    /// 配置第三方数据模型与  数据转换器集合配置扩展方法
+    /// 
+    /// 
+    /// 
+    /// 
+    public static IServiceCollection ConfigureSocketDataConverters(this IServiceCollection services, Action configureOptions)
+    {
+        services.Configure(configureOptions);
+        return services;
+    }
+
+    /// 
+    /// 配置第三方菜单路由与 Tab 标签页捆绑字典扩展方法
     /// 
     /// 
     /// 
@@ -236,7 +260,7 @@ static IServiceCollection AddIconTheme(this IServiceCollection services)
     }
 
     /// 
-    /// IconThemeOptions 扩展配置方法
+    /// 配置  扩展方法
     /// 
     /// 
     /// 
diff --git a/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs b/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs
index 9fadf2ab442..ccd03c1d31e 100644
--- a/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs
+++ b/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs
@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information.
 // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
 
+using Microsoft.Extensions.DependencyInjection;
 using System.Reflection;
 using System.Runtime.Versioning;
 using System.Text;
@@ -115,7 +116,7 @@ public static void SetDataPackageAdapter(this ITcpSocketClient client,
     /// 
     /// This method sets up the  to use the specified  for handling incoming data. If the  type is decorated with a , the associated converter is used to transform the data before invoking
+    /// cref="SocketDataTypeConverterAttribute"/>, the associated converter is used to transform the data before invoking
     /// the . The callback is called with the converted entity or  if
     /// conversion fails.
     /// The type of entity that the data package adapter will handle.
@@ -131,27 +132,60 @@ public static void SetDataPackageAdapter(this ITcpSocketClient client,
             await adapter.HandlerAsync(buffer);
         };
 
+        ISocketDataConverter? converter = null;
+
         var type = typeof(TEntity);
-        var converterType = type.GetCustomAttribute();
+        var converterType = type.GetCustomAttribute();
         if (converterType is { Type: not null })
         {
+            // 如果类型上有 SocketDataTypeConverterAttribute 特性则使用特性中指定的转换器
             if (Activator.CreateInstance(converterType.Type) is ISocketDataConverter socketDataConverter)
             {
-                // 设置 DataPackageAdapter 的回调函数
-                adapter.ReceivedCallBack = async buffer =>
-                {
-                    TEntity? ret = default;
-                    if (socketDataConverter.TryConvertTo(buffer, out var t))
-                    {
-                        ret = t;
-                    }
-                    await callback(ret);
-                };
+                converter = socketDataConverter;
             }
         }
         else
         {
+            // 如果没有特性则从 ITcpSocketClient 中的服务容器获取转换器
+            converter = client.GetSocketDataConverter();
+        }
+
+        if (converter == null)
+        {
+            // 设置正常回调
             adapter.ReceivedCallBack = async buffer => await callback(default);
         }
+        else
+        {
+            // 设置转化器
+            adapter.SetDataAdapterCallback(converter, callback);
+        }
+    }
+
+    private static void SetDataAdapterCallback(this IDataPackageAdapter adapter, ISocketDataConverter converter, Func callback)
+    {
+        adapter.ReceivedCallBack = async buffer =>
+        {
+            TEntity? ret = default;
+            if (converter.TryConvertTo(buffer, out var t))
+            {
+                ret = t;
+            }
+            await callback(ret);
+        };
+    }
+
+    private static ISocketDataConverter? GetSocketDataConverter(this ITcpSocketClient client)
+    {
+        ISocketDataConverter? converter = null;
+        if (client is IServiceProvider provider)
+        {
+            var converters = provider.GetRequiredService>().Value;
+            if (converters.TryGetTypeConverter(out var v))
+            {
+                converter = v;
+            }
+        }
+        return converter;
     }
 }
diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs
index 1d5f3f6b0d7..aaf31b557e7 100644
--- a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs
+++ b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs
@@ -5,11 +5,19 @@
 
 namespace BootstrapBlazor.Components;
 
+/// 
+/// Socket 数据转换器接口
+/// 
+public interface ISocketDataConverter
+{
+
+}
+
 /// 
 /// 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
+public interface ISocketDataConverter : ISocketDataConverter
 {
     /// 
     /// Attempts to convert the specified data to an instance of .
diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverter.cs
index 642cac45870..324da0cafa2 100644
--- a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverter.cs
+++ b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverter.cs
@@ -11,8 +11,16 @@ 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 class SocketDataConverter : ISocketDataConverter
+public class SocketDataConverter(SocketDataConverterCollections converters) : ISocketDataConverter
 {
+    /// 
+    /// 构造函数
+    /// 
+    public SocketDataConverter() : this(new())
+    {
+
+    }
+
     /// 
     /// 
     /// 
@@ -44,10 +52,14 @@ protected virtual bool Parse(ReadOnlyMemory data, TEntity entity)
         var ret = false;
         if (entity != null)
         {
+            var unuseProperties = new List(32);
+
+            // 通过 SocketDataPropertyConverterAttribute 特性获取属性转换器
             var properties = entity.GetType().GetProperties().Where(p => p.CanWrite).ToList();
             foreach (var p in properties)
             {
-                var attr = p.GetCustomAttribute(false);
+                var attr = p.GetCustomAttribute(false)
+                    ?? GetPropertyConverterAttribute(p);
                 if (attr != null)
                 {
                     p.SetValue(entity, attr.ConvertTo(data));
@@ -57,4 +69,14 @@ protected virtual bool Parse(ReadOnlyMemory data, TEntity entity)
         }
         return ret;
     }
+
+    private SocketDataPropertyConverterAttribute? GetPropertyConverterAttribute(PropertyInfo propertyInfo)
+    {
+        SocketDataPropertyConverterAttribute? attr = null;
+        if (converters.TryGetPropertyConverter(propertyInfo, out var v))
+        {
+            attr = v;
+        }
+        return attr;
+    }
 }
diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterCollections.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterCollections.cs
new file mode 100644
index 00000000000..71891e2a0ab
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterCollections.cs
@@ -0,0 +1,96 @@
+// 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
+
+using System.Collections.Concurrent;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace BootstrapBlazor.Components;
+
+/// 
+/// 
+/// 
+public class SocketDataConverterCollections
+{
+    readonly ConcurrentDictionary _converters = new();
+    readonly ConcurrentDictionary _propertyConverters = new();
+
+    /// 
+    /// 增加数据类型转换器方法
+    /// 
+    /// 
+    /// 
+    public void AddOrUpdateTypeConverter(ISocketDataConverter converter)
+    {
+        var type = typeof(TEntity);
+        _converters.AddOrUpdate(type, t => converter, (t, v) => converter);
+    }
+
+    /// 
+    /// 添加属性类型转化器方法
+    /// 
+    /// 
+    /// 
+    /// 
+    public void AddOrUpdatePropertyConverter(Expression> propertyExpression, SocketDataPropertyConverterAttribute attribute)
+    {
+        if (propertyExpression.Body is MemberExpression memberExpression)
+        {
+            if(attribute.Type == null)
+            {
+                attribute.Type = memberExpression.Type;
+            }
+            _propertyConverters.AddOrUpdate(memberExpression.Member, m => attribute, (m, v) => attribute);
+        }
+    }
+
+    /// 
+    /// 获得指定数据类型转换器方法
+    /// 
+    /// 
+    public bool TryGetTypeConverter([NotNullWhen(true)] out ISocketDataConverter? converter)
+    {
+        converter = null;
+        var ret = false;
+        if (_converters.TryGetValue(typeof(TEntity), out var v) && v is ISocketDataConverter c)
+        {
+            converter = c;
+            ret = true;
+        }
+        return ret;
+    }
+
+    /// 
+    /// 获得指定数据类型属性转换器方法
+    /// 
+    /// 
+    public bool TryGetPropertyConverter(Expression> propertyExpression, [NotNullWhen(true)] out SocketDataPropertyConverterAttribute? converterAttribute)
+    {
+        converterAttribute = null;
+        var ret = false;
+        if (propertyExpression.Body is MemberExpression memberExpression && TryGetPropertyConverter(memberExpression.Member, out var v))
+        {
+            converterAttribute = v;
+            ret = true;
+        }
+        return ret;
+    }
+
+    /// 
+    /// 获得指定数据类型属性转换器方法
+    /// 
+    /// 
+    public bool TryGetPropertyConverter(MemberInfo memberInfo, [NotNullWhen(true)] out SocketDataPropertyConverterAttribute? converterAttribute)
+    {
+        converterAttribute = null;
+        var ret = false;
+        if (_propertyConverters.TryGetValue(memberInfo, out var v))
+        {
+            converterAttribute = v;
+            ret = true;
+        }
+        return ret;
+    }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterAttribute.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataTypeConverterAttribute.cs
similarity index 90%
rename from src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterAttribute.cs
rename to src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataTypeConverterAttribute.cs
index 4a44475bc53..61b33a1ed31 100644
--- a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterAttribute.cs
+++ b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataTypeConverterAttribute.cs
@@ -9,7 +9,7 @@ namespace BootstrapBlazor.Components;
 /// 
 /// 
 [AttributeUsage(AttributeTargets.Class)]
-public class SocketDataConverterAttribute : Attribute
+public class SocketDataTypeConverterAttribute : Attribute
 {
     /// 
     /// Gets or sets the type of the .
diff --git a/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs b/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs
index dbbb863d05d..8c84d7a538f 100644
--- a/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs
+++ b/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs
@@ -12,7 +12,7 @@
 namespace BootstrapBlazor.Components;
 
 [UnsupportedOSPlatform("browser")]
-class DefaultTcpSocketClient(SocketClientOptions options) : ITcpSocketClient
+class DefaultTcpSocketClient(SocketClientOptions options) : IServiceProvider, ITcpSocketClient
 {
     /// 
     /// Gets or sets the socket client provider used for managing socket connections.
@@ -27,6 +27,7 @@ class DefaultTcpSocketClient(SocketClientOptions options) : ITcpSocketClient
     /// 
     /// Gets or sets the service provider used to resolve dependencies.
     /// 
+    [NotNull]
     public IServiceProvider? ServiceProvider { get; set; }
 
     /// 
@@ -406,6 +407,13 @@ private async ValueTask CloseCoreAsync()
         }
     }
 
+    /// 
+    /// 
+    /// 
+    /// 
+    /// 
+    public object? GetService(Type serviceType) => ServiceProvider.GetService(serviceType);
+
     /// 
     /// Releases the resources used by the current instance of the class.
     /// 
diff --git a/src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs b/src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs
index f24d5e02580..dbfe1f7f4a3 100644
--- a/src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs
+++ b/src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs
@@ -7,12 +7,12 @@ namespace BootstrapBlazor.Components;
 
 static class SocketDataPropertyExtensions
 {
-    public static ISocketDataPropertyConverter? GetConverter(this SocketDataPropertyAttribute attribute)
+    public static ISocketDataPropertyConverter? GetConverter(this SocketDataPropertyConverterAttribute attribute)
     {
         return attribute.GetConverterByType() ?? attribute.GetDefaultConverter();
     }
 
-    private static ISocketDataPropertyConverter? GetConverterByType(this SocketDataPropertyAttribute attribute)
+    private static ISocketDataPropertyConverter? GetConverterByType(this SocketDataPropertyConverterAttribute attribute)
     {
         ISocketDataPropertyConverter? converter = null;
         var converterType = attribute.ConverterType;
@@ -28,7 +28,7 @@ static class SocketDataPropertyExtensions
         return converter;
     }
 
-    private static ISocketDataPropertyConverter? GetDefaultConverter(this SocketDataPropertyAttribute attribute)
+    private static ISocketDataPropertyConverter? GetDefaultConverter(this SocketDataPropertyConverterAttribute attribute)
     {
         ISocketDataPropertyConverter? converter = null;
         var type = attribute.Type;
@@ -86,7 +86,7 @@ static class SocketDataPropertyExtensions
         return converter;
     }
 
-    public static object? ConvertTo(this SocketDataPropertyAttribute attribute, ReadOnlyMemory data)
+    public static object? ConvertTo(this SocketDataPropertyConverterAttribute attribute, ReadOnlyMemory data)
     {
         object? ret = null;
         var start = attribute.Offset;
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataPropertyAttribute.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataPropertyConverterAttribute.cs
similarity index 91%
rename from src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataPropertyAttribute.cs
rename to src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataPropertyConverterAttribute.cs
index f766b404f14..a8f171e5fd3 100644
--- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataPropertyAttribute.cs
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataPropertyConverterAttribute.cs
@@ -11,8 +11,8 @@ namespace BootstrapBlazor.Components;
 /// This attribute can be applied to fields to indicate that they are part of the data transmitted over a
 /// socket connection. It is intended for use in scenarios where socket communication requires specific fields to be
 /// identified for processing.
-[AttributeUsage(AttributeTargets.Property)]
-public class SocketDataPropertyAttribute : Attribute
+[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+public class SocketDataPropertyConverterAttribute : Attribute
 {
     /// 
     /// 获得/设置 数据类型
diff --git a/test/UnitTest/Services/SocketDataConverterCollectionsTest.cs b/test/UnitTest/Services/SocketDataConverterCollectionsTest.cs
new file mode 100644
index 00000000000..5f923af1014
--- /dev/null
+++ b/test/UnitTest/Services/SocketDataConverterCollectionsTest.cs
@@ -0,0 +1,105 @@
+// 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
+
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace UnitTest.Services;
+
+public class SocketDataConverterCollectionsTest : BootstrapBlazorTestBase
+{
+    protected override void ConfigureConfiguration(IServiceCollection services)
+    {
+        base.ConfigureConfiguration(services);
+
+        services.ConfigureSocketDataConverters(options =>
+        {
+            options.AddOrUpdateTypeConverter(new SocketDataConverter());
+            options.AddOrUpdatePropertyConverter(entity => entity.Header, new SocketDataPropertyConverterAttribute()
+            {
+                Offset = 0,
+                Length = 5
+            });
+            options.AddOrUpdatePropertyConverter(entity => entity.Body, new SocketDataPropertyConverterAttribute()
+            {
+                Offset = 5,
+                Length = 2
+            });
+
+            // 为提高代码覆盖率 重复添加转换器以后面的为准
+            options.AddOrUpdateTypeConverter(new SocketDataConverter());
+            options.AddOrUpdatePropertyConverter(entity => entity.Header, new SocketDataPropertyConverterAttribute()
+            {
+                Offset = 0,
+                Length = 5
+            });
+        });
+    }
+
+    [Fact]
+    public void TryGetConverter_Ok()
+    {
+        var service = Context.Services.GetRequiredService>();
+        Assert.NotNull(service.Value);
+
+        var ret = service.Value.TryGetTypeConverter(out var converter);
+        Assert.True(ret);
+        Assert.NotNull(converter);
+
+        var fakeConverter = service.Value.TryGetTypeConverter(out var fooConverter);
+        Assert.False(fakeConverter);
+        Assert.Null(fooConverter);
+
+        ret = service.Value.TryGetPropertyConverter(entity => entity.Header, out var propertyConverterAttribute);
+        Assert.True(ret);
+        Assert.NotNull(propertyConverterAttribute);
+        Assert.True(propertyConverterAttribute is { Offset: 0, Length: 5 });
+
+        ret = service.Value.TryGetPropertyConverter(entity => entity.Name, out var fooPropertyConverterAttribute);
+        Assert.False(ret);
+        Assert.Null(fooPropertyConverterAttribute);
+
+        ret = service.Value.TryGetPropertyConverter(entity => entity.ToString(), out _);
+        Assert.False(ret);
+    }
+
+    class MockEntity
+    {
+        public byte[]? Header { get; set; }
+
+        public byte[]? Body { get; set; }
+    }
+
+    class MockLoggerProvider : ILoggerProvider
+    {
+        public ILogger CreateLogger(string categoryName)
+        {
+            return new MockLogger();
+        }
+
+        public void Dispose()
+        {
+
+        }
+    }
+
+    class MockLogger : ILogger
+    {
+        public IDisposable? BeginScope(TState state) where TState : notnull
+        {
+            return null;
+        }
+
+        public bool IsEnabled(LogLevel logLevel)
+        {
+            return true;
+        }
+
+        public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter)
+        {
+
+        }
+    }
+}
diff --git a/test/UnitTest/Services/TcpSocketFactoryTest.cs b/test/UnitTest/Services/TcpSocketFactoryTest.cs
index 740d723558b..93e36e8c55c 100644
--- a/test/UnitTest/Services/TcpSocketFactoryTest.cs
+++ b/test/UnitTest/Services/TcpSocketFactoryTest.cs
@@ -755,6 +755,66 @@ public async Task TryConvertTo_Ok()
         var converter = new MockSocketDataConverter();
         result = converter.TryConvertTo(new byte[] { 0x1, 0x2 }, out t);
         Assert.False(result);
+
+        server.Stop();
+    }
+
+    [Fact]
+    public async Task TryGetTypeConverter_Ok()
+    {
+        // 测试服务配置转换器
+        var port = 8895;
+        var server = StartTcpServer(port, MockSplitPackageAsync);
+
+        var client = CreateClient(builder =>
+        {
+            builder.ConfigureSocketDataConverters(options =>
+            {
+                options.AddOrUpdateTypeConverter(new SocketDataConverter(options));
+                options.AddOrUpdatePropertyConverter(entity => entity.Header, new SocketDataPropertyConverterAttribute()
+                {
+                    Offset = 0,
+                    Length = 5
+                });
+                options.AddOrUpdatePropertyConverter(entity => entity.Body, new SocketDataPropertyConverterAttribute()
+                {
+                    Offset = 5,
+                    Length = 2
+                });
+            });
+        });
+        var tcs = new TaskCompletionSource();
+        var receivedBuffer = new byte[128];
+
+        // 连接 TCP Server
+        var connect = await client.ConnectAsync("localhost", port);
+
+        // 设置数据适配器
+        var adapter = new DataPackageAdapter
+        {
+            DataPackageHandler = new FixLengthDataPackageHandler(7)
+        };
+
+        OptionConvertEntity? entity = null;
+        client.SetDataPackageAdapter(adapter, data =>
+        {
+            // buffer 即是接收到的数据
+            entity = data;
+            tcs.SetResult();
+            return Task.CompletedTask;
+        });
+
+        // 发送数据
+        var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
+        await client.SendAsync(data);
+
+        // 等待接收数据处理完成
+        await tcs.Task;
+        Assert.NotNull(entity);
+        Assert.Equal([1, 2, 3, 4, 5], entity.Header);
+        Assert.Equal([3, 4], entity.Body);
+
+        server.Stop();
     }
 
     private static TcpListener StartTcpServer(int port, Func handler)
@@ -1141,58 +1201,58 @@ public void SetReceive(bool state)
         }
     }
 
-    [SocketDataConverter(Type = typeof(SocketDataConverter))]
+    [SocketDataTypeConverter(Type = typeof(SocketDataConverter))]
     class MockEntity
     {
-        [SocketDataProperty(Type = typeof(byte[]), Offset = 0, Length = 5)]
+        [SocketDataPropertyConverter(Type = typeof(byte[]), Offset = 0, Length = 5)]
         public byte[]? Header { get; set; }
 
-        [SocketDataProperty(Type = typeof(byte[]), Offset = 5, Length = 2)]
+        [SocketDataPropertyConverter(Type = typeof(byte[]), Offset = 5, Length = 2)]
         public byte[]? Body { get; set; }
 
-        [SocketDataProperty(Type = typeof(string), Offset = 7, Length = 1, EncodingName = "utf-8")]
+        [SocketDataPropertyConverter(Type = typeof(string), Offset = 7, Length = 1, EncodingName = "utf-8")]
         public string? Value1 { get; set; }
 
-        [SocketDataProperty(Type = typeof(int), Offset = 8, Length = 1)]
+        [SocketDataPropertyConverter(Type = typeof(int), Offset = 8, Length = 1)]
         public int Value2 { get; set; }
 
-        [SocketDataProperty(Type = typeof(long), Offset = 9, Length = 1)]
+        [SocketDataPropertyConverter(Type = typeof(long), Offset = 9, Length = 1)]
         public long Value3 { get; set; }
 
-        [SocketDataProperty(Type = typeof(double), Offset = 10, Length = 8)]
+        [SocketDataPropertyConverter(Type = typeof(double), Offset = 10, Length = 8)]
         public double Value4 { get; set; }
 
-        [SocketDataProperty(Type = typeof(float), Offset = 18, Length = 4)]
+        [SocketDataPropertyConverter(Type = typeof(float), Offset = 18, Length = 4)]
         public float Value5 { get; set; }
 
-        [SocketDataProperty(Type = typeof(short), Offset = 22, Length = 1)]
+        [SocketDataPropertyConverter(Type = typeof(short), Offset = 22, Length = 1)]
         public short Value6 { get; set; }
 
-        [SocketDataProperty(Type = typeof(ushort), Offset = 23, Length = 1)]
+        [SocketDataPropertyConverter(Type = typeof(ushort), Offset = 23, Length = 1)]
         public ushort Value7 { get; set; }
 
-        [SocketDataProperty(Type = typeof(uint), Offset = 24, Length = 1)]
+        [SocketDataPropertyConverter(Type = typeof(uint), Offset = 24, Length = 1)]
         public uint Value8 { get; set; }
 
-        [SocketDataProperty(Type = typeof(ulong), Offset = 25, Length = 1)]
+        [SocketDataPropertyConverter(Type = typeof(ulong), Offset = 25, Length = 1)]
         public ulong Value9 { get; set; }
 
-        [SocketDataProperty(Type = typeof(bool), Offset = 26, Length = 1)]
+        [SocketDataPropertyConverter(Type = typeof(bool), Offset = 26, Length = 1)]
         public bool Value10 { get; set; }
 
-        [SocketDataProperty(Type = typeof(EnumEducation), Offset = 27, Length = 1)]
+        [SocketDataPropertyConverter(Type = typeof(EnumEducation), Offset = 27, Length = 1)]
         public EnumEducation Value11 { get; set; }
 
-        [SocketDataProperty(Type = typeof(Foo), Offset = 28, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])]
+        [SocketDataPropertyConverter(Type = typeof(Foo), Offset = 28, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])]
         public Foo? Value12 { get; set; }
 
-        [SocketDataProperty(Type = typeof(string), Offset = 7, Length = 1)]
+        [SocketDataPropertyConverter(Type = typeof(string), Offset = 7, Length = 1)]
         public string? Value14 { get; set; }
 
         public string? Value13 { get; set; }
     }
 
-    class MockSocketDataConverter: SocketDataConverter
+    class MockSocketDataConverter : SocketDataConverter
     {
         protected override bool Parse(ReadOnlyMemory data, MockEntity entity)
         {
@@ -1214,4 +1274,11 @@ class NoConvertEntity
 
         public byte[]? Body { get; set; }
     }
+
+    class OptionConvertEntity
+    {
+        public byte[]? Header { get; set; }
+
+        public byte[]? Body { get; set; }
+    }
 }