Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 28 additions & 7 deletions BootstrapBlazor.Extensions.sln
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Mermaid", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.MeiliSearch", "src\components\BootstrapBlazor.MeiliSearch\BootstrapBlazor.MeiliSearch.csproj", "{4B086A62-5F5A-47BC-921F-35803F26DD68}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTestSvgIcon", "test\UniTestSvgIcon\UniTestSvgIcon.csproj", "{F965576B-A801-4473-85FE-E100125FDEF5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.RDKit", "src\components\BootstrapBlazor.RDKit\BootstrapBlazor.RDKit.csproj", "{7328E464-AE3C-4277-BEC3-422C56637066}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.SmilesDrawer", "src\components\BootstrapBlazor.SmilesDrawer\BootstrapBlazor.SmilesDrawer.csproj", "{84823875-1B07-4CCE-A009-29AEF90C6C10}"
Expand Down Expand Up @@ -190,6 +188,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Vditor", "s
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.OfficeViewer", "src\components\BootstrapBlazor.OfficeViewer\BootstrapBlazor.OfficeViewer.csproj", "{2436940C-5920-D801-8A81-721F4C20A355}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Socket", "src\extensions\BootstrapBlazor.Socket\BootstrapBlazor.Socket.csproj", "{965F1512-57DC-4621-9C74-E059A14BB866}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.TcpSocket", "src\extensions\BootstrapBlazor.TcpSocket\BootstrapBlazor.TcpSocket.csproj", "{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestTcpSocket", "test\UnitTestTcpSocket\UnitTestTcpSocket.csproj", "{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestSvgIcon", "test\UnitTestSvgIcon\UnitTestSvgIcon.csproj", "{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -448,10 +454,6 @@ Global
{4B086A62-5F5A-47BC-921F-35803F26DD68}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B086A62-5F5A-47BC-921F-35803F26DD68}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B086A62-5F5A-47BC-921F-35803F26DD68}.Release|Any CPU.Build.0 = Release|Any CPU
{F965576B-A801-4473-85FE-E100125FDEF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F965576B-A801-4473-85FE-E100125FDEF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F965576B-A801-4473-85FE-E100125FDEF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F965576B-A801-4473-85FE-E100125FDEF5}.Release|Any CPU.Build.0 = Release|Any CPU
{7328E464-AE3C-4277-BEC3-422C56637066}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7328E464-AE3C-4277-BEC3-422C56637066}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7328E464-AE3C-4277-BEC3-422C56637066}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -516,6 +518,22 @@ Global
{2436940C-5920-D801-8A81-721F4C20A355}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2436940C-5920-D801-8A81-721F4C20A355}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2436940C-5920-D801-8A81-721F4C20A355}.Release|Any CPU.Build.0 = Release|Any CPU
{965F1512-57DC-4621-9C74-E059A14BB866}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{965F1512-57DC-4621-9C74-E059A14BB866}.Debug|Any CPU.Build.0 = Debug|Any CPU
{965F1512-57DC-4621-9C74-E059A14BB866}.Release|Any CPU.ActiveCfg = Release|Any CPU
{965F1512-57DC-4621-9C74-E059A14BB866}.Release|Any CPU.Build.0 = Release|Any CPU
{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}.Release|Any CPU.Build.0 = Release|Any CPU
{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}.Release|Any CPU.Build.0 = Release|Any CPU
{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -588,7 +606,6 @@ Global
{29E47F4A-CE03-42B5-BDAA-FB4B40D4C897} = {FF1089BE-C704-4374-B629-C57C08E1798F}
{DA2198E1-2CA9-EE53-926B-7950AB4B5EBF} = {FF1089BE-C704-4374-B629-C57C08E1798F}
{4B086A62-5F5A-47BC-921F-35803F26DD68} = {FF1089BE-C704-4374-B629-C57C08E1798F}
{F965576B-A801-4473-85FE-E100125FDEF5} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
{7328E464-AE3C-4277-BEC3-422C56637066} = {FF1089BE-C704-4374-B629-C57C08E1798F}
{84823875-1B07-4CCE-A009-29AEF90C6C10} = {FF1089BE-C704-4374-B629-C57C08E1798F}
{AA4EDA37-1D81-4235-A7F6-F1C112B364EF} = {FF1089BE-C704-4374-B629-C57C08E1798F}
Expand All @@ -605,6 +622,10 @@ Global
{4757B038-70E4-40B0-9B73-700EE5632B07} = {FF1089BE-C704-4374-B629-C57C08E1798F}
{D417E1B9-D146-4983-81D0-79F3193B322B} = {FF1089BE-C704-4374-B629-C57C08E1798F}
{2436940C-5920-D801-8A81-721F4C20A355} = {FF1089BE-C704-4374-B629-C57C08E1798F}
{965F1512-57DC-4621-9C74-E059A14BB866} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D5EB1960-6F30-4CE1-B375-EAE1F787D6FF}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Version>9.0.0-beta01</Version>
</PropertyGroup>

<PropertyGroup>
<PackageTags>BootstrapBlazor Socket</PackageTags>
<Description>BootstrapBlazor extensions of Socket</Description>
</PropertyGroup>

<ItemGroup>
<Using Include="BootstrapBlazor.DataAdapters" />
<Using Include="BootstrapBlazor.DataHandlers" />
<Using Include="BootstrapBlazor.DataConverters" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

namespace BootstrapBlazor.DataAdapters;

/// <summary>
/// Provides a base implementation for adapting data packages between different systems or formats.
/// </summary>
/// <remarks>This abstract class serves as a foundation for implementing custom data package adapters. It defines
/// 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>
/// <inheritdoc/>
/// </summary>
public Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }

/// <summary>
/// <inheritdoc/>
/// </summary>
public IDataPackageHandler? DataPackageHandler { get; set; }

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="data"></param>
/// <param name="token"></param>
/// <returns></returns>
public virtual async ValueTask HandlerAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
{
if (DataPackageHandler != null)
{
if (DataPackageHandler.ReceivedCallBack == null)
{
DataPackageHandler.ReceivedCallBack = OnHandlerReceivedCallBack;
}

// 如果存在数据处理器则调用其处理方法
await DataPackageHandler.HandlerAsync(data, token);
}
}

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="data"></param>
/// <param name="socketDataConverter"></param>
/// <param name="entity"></param>
/// <returns></returns>
public virtual bool TryConvertTo<TEntity>(ReadOnlyMemory<byte> data, IDataConverter<TEntity> socketDataConverter, out TEntity? entity)
{
entity = default;
var ret = socketDataConverter.TryConvertTo(data, out var v);
if (ret)
{
entity = v;
}
return ret;
}

/// <summary>
/// Handles incoming data by invoking a callback method, if one is defined.
/// </summary>
/// <remarks>This method is designed to be overridden in derived classes to provide custom handling of
/// incoming data. If a callback method is assigned, it will be invoked asynchronously with the provided
/// data.</remarks>
/// <param name="data">The incoming data to be processed, represented as a read-only memory block of bytes.</param>
/// <returns></returns>
protected virtual async ValueTask OnHandlerReceivedCallBack(ReadOnlyMemory<byte> data)
{
if (ReceivedCallBack != null)
{
// 调用接收回调方法处理数据
await ReceivedCallBack(data);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

namespace BootstrapBlazor.DataAdapters;

/// <summary>
/// Defines an adapter for handling and transmitting data packages to a target destination.
/// </summary>
/// <remarks>This interface provides methods for sending data asynchronously and configuring a data handler.
/// Implementations of this interface are responsible for managing the interaction between the caller and the underlying
/// data transmission mechanism.</remarks>
public interface IDataPackageAdapter
{
/// <summary>
/// Gets or sets the callback function to be invoked when data is received.
/// </summary>
/// <remarks>The callback function is expected to handle the received data asynchronously. Ensure that the
/// implementation of the callback does not block the calling thread and completes promptly to avoid performance
/// issues.</remarks>
Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }

/// <summary>
/// Gets the handler responsible for processing data packages.
/// </summary>
IDataPackageHandler? DataPackageHandler { get; }

/// <summary>
/// Asynchronously receives data from a source and processes it.
/// </summary>
/// <remarks>This method does not return any result directly. It is intended for scenarios where data is received
/// and processed asynchronously. Ensure that the <paramref name="data"/> parameter contains valid data before calling
/// this method.</remarks>
/// <param name="data">A read-only memory region containing the data to be received. The caller must ensure the memory is valid and
/// populated.</param>
/// <param name="token">An optional cancellation token that can be used to cancel the operation. Defaults to <see langword="default"/> if
/// not provided.</param>
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation. The task completes when the data has been
/// successfully received and processed.</returns>
ValueTask HandlerAsync(ReadOnlyMemory<byte> data, CancellationToken token = default);

/// <summary>
/// Attempts to convert the specified byte data into an entity of type <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 its default value.</remarks>
/// <typeparam name="TEntity">The type of the entity to convert the data to.</typeparam>
/// <param name="data">The byte data to be converted.</param>
/// <param name="socketDataConverter">The converter used to transform the byte data into an entity.</param>
/// <param name="entity">When this method returns, contains the converted entity if the conversion was successful; otherwise, the default
/// value for the type of the entity.</param>
/// <returns><see langword="true"/> if the conversion was successful; otherwise, <see langword="false"/>.</returns>
bool TryConvertTo<TEntity>(ReadOnlyMemory<byte> data, IDataConverter<TEntity> socketDataConverter, out TEntity? entity);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

using System.Reflection;

namespace BootstrapBlazor.DataConverters;

/// <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 class DataConverter<TEntity>(DataConverterCollections converters) : IDataConverter<TEntity>
{
/// <summary>
/// 构造函数
/// </summary>
public DataConverter() : this(new())
{

}

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="data"></param>
/// <param name="entity"></param>
/// <returns></returns>
public virtual bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out TEntity? entity)
{
var v = CreateEntity();
var ret = Parse(data, v);
entity = ret ? v : default;
return ret;
}

/// <summary>
/// 创建实体实例方法
/// </summary>
/// <returns></returns>
protected virtual TEntity CreateEntity() => Activator.CreateInstance<TEntity>();

/// <summary>
/// 将字节数据转换为指定实体类型的实例。
/// </summary>
/// <param name="data"></param>
/// <param name="entity"></param>
protected virtual bool Parse(ReadOnlyMemory<byte> data, TEntity entity)
{
// 使用 SocketDataPropertyAttribute 特性获取数据转换规则
var ret = false;
if (entity != null)
{
var unuseProperties = new List<PropertyInfo>(32);

// 通过 SocketDataPropertyConverterAttribute 特性获取属性转换器
var properties = entity.GetType().GetProperties().Where(p => p.CanWrite).ToList();
foreach (var p in properties)
{
var attr = p.GetCustomAttribute<DataPropertyConverterAttribute>(false)
?? GetPropertyConverterAttribute(p);
if (attr != null)
{
p.SetValue(entity, attr.ConvertTo(data));
}
}
ret = true;
}
return ret;
}

private DataPropertyConverterAttribute? GetPropertyConverterAttribute(PropertyInfo propertyInfo)
{
DataPropertyConverterAttribute? attr = null;
if (converters.TryGetPropertyConverter<TEntity>(propertyInfo, out var v))
{
attr = v;
}
return attr;
}
}
Loading