Compare commits

...

50 Commits

Author SHA1 Message Date
2248356998 qq.com
2f58f7b47c 调整列宽 2025-10-16 15:59:46 +08:00
2248356998 qq.com
576c5207b7 feat: js刷新变量表数据 2025-10-16 15:52:41 +08:00
2248356998 qq.com
2e83c19e0c Merge branch 'variablePage' of https://gitee.com/ThingsGateway/ThingsGateway into variablePage 2025-10-16 14:43:31 +08:00
2248356998 qq.com
b22e90609b feat: js刷新变量表 2025-10-16 06:37:09 +00:00
Diego
35edd7dc43 !77 update src/Admin/ThingsGateway.Common/Common/ConcurrentList.cs.
Merge pull request !77 from Sunny/v10
2025-10-16 06:14:04 +00:00
Sunny
bd178831e3 update src/Admin/ThingsGateway.Common/Common/ConcurrentList.cs.
修改 ConcurrentList.cs 一处错误代码

Signed-off-by: Sunny <yhuse@163.com>
2025-10-16 04:36:31 +00:00
2248356998 qq.com
fe9ec6ad10 fix: s7连接错误 2025-10-16 12:00:08 +08:00
2248356998 qq.com
67354e70b3 feat: js刷新变量表 2025-10-16 10:52:50 +08:00
2248356998 qq.com
6f9ec2e24b feat: 并发字典替换为 NonBlockingDictionary 类型 2025-10-15 17:40:33 +08:00
2248356998 qq.com
c0337e2b19 build: 10.11.105
feat: json添加AllowNamedFloatingPointLiterals
2025-10-15 11:16:18 +08:00
2248356998 qq.com
8a95f48f5a 适配 net10 rc2 2025-10-15 10:02:11 +08:00
2248356998 qq.com
14f3c31265 build: 10.11.102 2025-10-14 22:22:06 +08:00
2248356998 qq.com
1bad65378f build: 10.11.101 2025-10-14 11:46:26 +08:00
2248356998 qq.com
db3affc67e build: 10.11.100
feat(VariablePage): 优化UI性能
2025-10-14 10:18:09 +08:00
2248356998 qq.com
5ee8b50a92 build: 10.11.99 2025-10-13 22:11:05 +08:00
2248356998 qq.com
301beda2a2 build: 10.11.98 2025-10-13 21:10:39 +08:00
2248356998 qq.com
628b51a353 build: 10.11.97 2025-10-12 15:15:42 +08:00
2248356998 qq.com
f03445bc83 更新依赖 2025-10-12 00:25:57 +08:00
2248356998 qq.com
55a2ff5487 build: 10.11.96 2025-10-11 13:38:28 +08:00
2248356998 qq.com
0fef7dcf3b 更新依赖 2025-10-10 21:41:24 +08:00
2248356998 qq.com
19d9702606 fix: sqlite 批量更新 2025-10-10 14:19:43 +08:00
2248356998 qq.com
a8a9774932 fix: orm 序列化 2025-10-10 12:26:35 +08:00
2248356998 qq.com
aad0f0e8c3 fix: orm批量插入 2025-10-10 11:19:14 +08:00
2248356998 qq.com
e74eae50a7 build: 10.11.87 2025-10-09 20:58:16 +08:00
2248356998 qq.com
3b16d7019f feat: 优化orm 批量插入 2025-10-09 20:57:18 +08:00
2248356998 qq.com
3e038028c2 feat: 优化orm 批量插入 2025-10-09 19:19:18 +08:00
2248356998 qq.com
b1d8041f7e feat: 优化orm BulkCopy 2025-10-09 19:05:33 +08:00
2248356998 qq.com
53a98b26cd fix: 数据库插件保留天数逻辑错误 2025-10-09 08:52:02 +08:00
2248356998 qq.com
42c740fa1b 更新依赖 2025-10-07 22:25:38 +08:00
2248356998 qq.com
556819c90c 10.11.83 2025-09-30 15:25:15 +08:00
2248356998 qq.com
2522333a9c 更新依赖 2025-09-29 23:16:34 +08:00
2248356998 qq.com
bd4ce7c09b 适配net10 2025-09-29 17:47:24 +08:00
2248356998 qq.com
156ed88bd6 10.11.80 2025-09-29 17:09:12 +08:00
2248356998 qq.com
2416226eb0 10.11.78 2025-09-28 17:01:19 +08:00
2248356998 qq.com
976323a716 适配net10 2025-09-28 13:06:39 +08:00
2248356998 qq.com
3c9e397403 10.11.76 2025-09-28 00:33:56 +08:00
2248356998 qq.com
79406ad4a0 更新依赖 2025-09-27 23:59:38 +08:00
2248356998 qq.com
20c44f10ca 10.11.73 2025-09-27 20:18:56 +08:00
2248356998 qq.com
31d6b2a9e6 添加FastMapper 2025-09-26 15:24:37 +08:00
2248356998 qq.com
68e5a9c546 build: 10.11.71 2025-09-26 11:51:05 +08:00
2248356998 qq.com
b2ea9f99b9 适配net10 2025-09-26 10:58:57 +08:00
2248356998 qq.com
6d7d0e468a fix: opcua连接状态 2025-09-24 11:03:23 +08:00
2248356998 qq.com
ff1f632de2 build: 10.11.67 sqldb插件 2025-09-23 22:38:05 +08:00
2248356998 qq.com
fc3d7015ee sqldb插件 清理数据cron表达式错误 2025-09-23 22:36:45 +08:00
2248356998 qq.com
40c5acb522 调整UI 2025-09-23 13:41:20 +08:00
2248356998 qq.com
f9cc1cbb05 10.11.65 2025-09-23 12:02:17 +08:00
2248356998 qq.com
cf6e8b58f0 更新内存变量 2025-09-23 01:22:02 +08:00
2248356998 qq.com
615e3bb24c 修改 脚本调试项目 依赖项 2025-09-22 14:58:03 +08:00
2248356998 qq.com
4a7534b210 添加 脚本调试项目 2025-09-22 10:36:28 +08:00
2248356998 qq.com
58e099cb93 10.11.58 2025-09-21 20:53:50 +08:00
650 changed files with 11479 additions and 4076 deletions

View File

@@ -45,6 +45,7 @@ public class VerificatInfo : PrimaryIdEntity
/// 登录IP /// 登录IP
/// </summary> /// </summary>
[AutoGenerateColumn(Filterable = true, Sortable = true, Width = 200)] [AutoGenerateColumn(Filterable = true, Sortable = true, Width = 200)]
[SugarColumn(IsNullable = true)]
public string LoginIp { get; set; } public string LoginIp { get; set; }
/// <summary> /// <summary>
@@ -78,5 +79,6 @@ public class VerificatInfo : PrimaryIdEntity
/// 登录设备 /// 登录设备
/// </summary> /// </summary>
[AutoGenerateColumn(Filterable = true, Sortable = true, Width = 100)] [AutoGenerateColumn(Filterable = true, Sortable = true, Width = 100)]
[SugarColumn(IsNullable = true)]
public string Device { get; set; } public string Device { get; set; }
} }

View File

@@ -251,11 +251,13 @@ public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter
if (exception == null) if (exception == null)
{ {
logger.Log(LogLevel.Information, $"{logData.Method}:{logData.Path}-{logData.Operation}"); if (logger.IsEnabled(LogLevel.Information))
logger.Log(LogLevel.Information, $"{logData.Method}:{logData.Path}-{logData.Operation}");
} }
else else
{ {
logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception?.ToSystemTextJsonString()}{Environment.NewLine}{logData.Validation?.ToSystemTextJsonString()}"); if (logger.IsEnabled(LogLevel.Warning))
logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception?.ToSystemTextJsonString()}{Environment.NewLine}{logData.Validation?.ToSystemTextJsonString()}");
} }
} }

View File

@@ -11,6 +11,7 @@
using Riok.Mapperly.Abstractions; using Riok.Mapperly.Abstractions;
namespace ThingsGateway.Admin.Application; namespace ThingsGateway.Admin.Application;
[Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, RequiredMappingStrategy = RequiredMappingStrategy.None)] [Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, RequiredMappingStrategy = RequiredMappingStrategy.None)]
public static partial class AdminMapper public static partial class AdminMapper
{ {

View File

@@ -145,7 +145,7 @@ public class AdminOAuthHandler<TOptions>(
var loginEvent = new LoginEvent var loginEvent = new LoginEvent
{ {
Ip = appService.RemoteIpAddress, Ip = appService.RemoteIpAddress,
Device = appService.UserAgent?.Platform, Device = appService.UserAgent?.Platform ?? "Unknown",
Expire = expire, Expire = expire,
SysUser = sysUser, SysUser = sysUser,
VerificatId = CommonUtils.GetSingleId() VerificatId = CommonUtils.GetSingleId()
@@ -156,7 +156,7 @@ public class AdminOAuthHandler<TOptions>(
//生成verificat信息 //生成verificat信息
var verificatInfo = new VerificatInfo var verificatInfo = new VerificatInfo
{ {
Device = loginEvent.Device, Device = loginEvent.Device ?? "Unknown",
Expire = loginEvent.Expire, Expire = loginEvent.Expire,
VerificatTimeout = tokenTimeout, VerificatTimeout = tokenTimeout,
Id = loginEvent.VerificatId, Id = loginEvent.VerificatId,

View File

@@ -26,7 +26,7 @@
"Module": 2, "Module": 2,
"Title": "权限管理", "Title": "权限管理",
"Code": "System", "Code": "System",
"NavLinkMatch": "All", "NavLinkMatch": "Prefix",
"Category": "MENU", "Category": "MENU",
"Target": "_self", "Target": "_self",
"Href": null, "Href": null,
@@ -47,7 +47,7 @@
"ParentId": 0, "ParentId": 0,
"Module": 2, "Module": 2,
"Title": "系统运维", "Title": "系统运维",
"NavLinkMatch": "All", "NavLinkMatch": "Prefix",
"Code": "System", "Code": "System",
"Category": "MENU", "Category": "MENU",
"Target": "_self", "Target": "_self",

View File

@@ -235,7 +235,7 @@ public class AuthService : IAuthService
var logingEvent = new LoginEvent var logingEvent = new LoginEvent
{ {
Ip = _appService.RemoteIpAddress, Ip = _appService.RemoteIpAddress,
Device = _appService.UserAgent?.Platform, Device = _appService.UserAgent?.Platform ?? "Unknown",
Expire = expire, Expire = expire,
SysUser = sysUser, SysUser = sysUser,
VerificatId = verificatId VerificatId = verificatId
@@ -344,7 +344,7 @@ public class AuthService : IAuthService
//生成verificat信息 //生成verificat信息
var verificatInfo = new VerificatInfo var verificatInfo = new VerificatInfo
{ {
Device = loginEvent.Device, Device = loginEvent.Device ?? "Unknown",
Expire = loginEvent.Expire, Expire = loginEvent.Expire,
VerificatTimeout = tokenTimeout, VerificatTimeout = tokenTimeout,
Id = loginEvent.VerificatId, Id = loginEvent.VerificatId,

View File

@@ -20,7 +20,7 @@ namespace ThingsGateway.Admin.Application;
/// <typeparam name="TEntry"></typeparam> /// <typeparam name="TEntry"></typeparam>
public class EventService<TEntry> : IEventService<TEntry>, IDisposable public class EventService<TEntry> : IEventService<TEntry>, IDisposable
{ {
private ConcurrentDictionary<string, Func<TEntry, Task>> Cache = new(); private NonBlockingDictionary<string, Func<TEntry, Task>> Cache = new();
public void Dispose() public void Dispose()
{ {

View File

@@ -9,6 +9,7 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application; namespace ThingsGateway.Admin.Application;
internal sealed class NoticeService : INoticeService internal sealed class NoticeService : INoticeService
{ {
private IEventService<AppMessage>? MessageDispatchService { get; set; } private IEventService<AppMessage>? MessageDispatchService { get; set; }

View File

@@ -282,7 +282,7 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
if (sysRole != null) if (sysRole != null)
{ {
var resources = await _sysResourceService.GetAllAsync().ConfigureAwait(false); var resources = await _sysResourceService.GetAllAsync().ConfigureAwait(false);
var menusList = resources.Where(a => a.Category == ResourceCategoryEnum.Menu).Where(a => menuIds.Contains(a.Id)); var menusList = resources.Where(a => a.Category == ResourceCategoryEnum.Menu && menuIds.Contains(a.Id));
#region #region

View File

@@ -550,7 +550,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
if (sysUser != null) if (sysUser != null)
{ {
var resources = await _sysResourceService.GetAllAsync().ConfigureAwait(false); var resources = await _sysResourceService.GetAllAsync().ConfigureAwait(false);
var menusList = resources.Where(a => a.Category == ResourceCategoryEnum.Menu).Where(a => menuIds.Contains(a.Id)); var menusList = resources.Where(a => a.Category == ResourceCategoryEnum.Menu && menuIds.Contains(a.Id));
#region #region

View File

@@ -119,7 +119,7 @@ internal sealed class VerificatInfoService : BaseService<VerificatInfo>, IVerifi
public void Add(VerificatInfo verificatInfo) public void Add(VerificatInfo verificatInfo)
{ {
using var db = GetDB(); using var db = GetDB();
db.Insertable<VerificatInfo>(verificatInfo).ExecuteCommand(); db.InsertableT<VerificatInfo>(verificatInfo).ExecuteCommand();
VerificatInfoService.RemoveCache(verificatInfo.Id); VerificatInfoService.RemoveCache(verificatInfo.Id);
if (verificatInfo != null) if (verificatInfo != null)
VerificatInfoService.SetCahce(verificatInfo); VerificatInfoService.SetCahce(verificatInfo);
@@ -132,7 +132,7 @@ internal sealed class VerificatInfoService : BaseService<VerificatInfo>, IVerifi
public void Update(VerificatInfo verificatInfo) public void Update(VerificatInfo verificatInfo)
{ {
using var db = GetDB(); using var db = GetDB();
db.Updateable<VerificatInfo>(verificatInfo).ExecuteCommand(); db.UpdateableT<VerificatInfo>(verificatInfo).ExecuteCommand();
VerificatInfoService.RemoveCache(verificatInfo.Id); VerificatInfoService.RemoveCache(verificatInfo.Id);
if (verificatInfo != null) if (verificatInfo != null)
VerificatInfoService.SetCahce(verificatInfo); VerificatInfoService.SetCahce(verificatInfo);

View File

@@ -8,7 +8,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks> <TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -20,7 +20,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" /> <PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Rougamo.Fody" Version="5.0.1" /> <PackageReference Include="Rougamo.Fody" Version="5.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
@@ -28,10 +28,10 @@
<PackageReference Include="System.Threading.RateLimiting" Version="8.0.0" /> <PackageReference Include="System.Threading.RateLimiting" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net10.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(NET9Version)" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(NET10Version)" />
<PackageReference Include="System.Formats.Asn1" Version="$(NET9Version)" /> <PackageReference Include="System.Formats.Asn1" Version="$(NET10Version)" />
<PackageReference Include="System.Threading.RateLimiting" Version="$(NET9Version)" /> <PackageReference Include="System.Threading.RateLimiting" Version="$(NET10Version)" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Remove="SeedData\Admin\*.json" /> <Content Remove="SeedData\Admin\*.json" />

View File

@@ -4,8 +4,8 @@
<div class="tg-table h-100"> <div class="tg-table h-100">
<Table TItem="TItem" IsBordered="true" IsStriped="true" TableSize="TableSize.Compact" SelectedRows=SelectedRows SelectedRowsChanged=privateSelectedRowsChanged IsMultipleSelect="IsMultipleSelect" @ref="Instance" SearchTemplate="SearchTemplate" <Table Id=@Id TItem="TItem" IsBordered="true" IsStriped="true" TableSize="TableSize.Compact" SelectedRows=SelectedRows SelectedRowsChanged=privateSelectedRowsChanged IsMultipleSelect="IsMultipleSelect" @ref="Instance" SearchTemplate="SearchTemplate"
DataService="DataService" CreateItemCallback="CreateItemCallback!" DataService="DataService" CreateItemCallback="CreateItemCallback!" RenderMode=RenderMode OnColumnCreating=OnColumnCreating
IsPagination="IsPagination" PageItemsSource="PageItemsSource" IsFixedHeader="IsFixedHeader" IndentSize=24 RowHeight=RowHeight ShowSearchText="ShowSearchText" ShowSearchButton="ShowSearchButton" DisableEditButtonCallback="DisableEditButtonCallback" DisableDeleteButtonCallback="DisableDeleteButtonCallback" BeforeShowEditDialogCallback=" BeforeShowEditDialogCallback!" IsPagination="IsPagination" PageItemsSource="PageItemsSource" IsFixedHeader="IsFixedHeader" IndentSize=24 RowHeight=RowHeight ShowSearchText="ShowSearchText" ShowSearchButton="ShowSearchButton" DisableEditButtonCallback="DisableEditButtonCallback" DisableDeleteButtonCallback="DisableDeleteButtonCallback" BeforeShowEditDialogCallback=" BeforeShowEditDialogCallback!"
IsTree="IsTree" OnTreeExpand="OnTreeExpand!" TreeNodeConverter="TreeNodeConverter!" TreeIcon="fa-solid fa-circle-chevron-right" TreeExpandIcon="fa-solid fa-circle-chevron-right fa-rotate-90" IsAutoQueryFirstRender=IsAutoQueryFirstRender IsTree="IsTree" OnTreeExpand="OnTreeExpand!" TreeNodeConverter="TreeNodeConverter!" TreeIcon="fa-solid fa-circle-chevron-right" TreeExpandIcon="fa-solid fa-circle-chevron-right fa-rotate-90" IsAutoQueryFirstRender=IsAutoQueryFirstRender
ShowDefaultButtons="ShowDefaultButtons" ShowAdvancedSearch="ShowAdvancedSearch" ShowResetButton=ShowResetButton ShowDefaultButtons="ShowDefaultButtons" ShowAdvancedSearch="ShowAdvancedSearch" ShowResetButton=ShowResetButton
@@ -14,7 +14,7 @@
ShowSkeleton="true" ShowLoading="ShowLoading" ShowSearch="ShowSearch" SearchModel=@SearchModel ShowLineNo ShowSkeleton="true" ShowLoading="ShowLoading" ShowSearch="ShowSearch" SearchModel=@SearchModel ShowLineNo
SearchMode=SearchMode ShowExportPdfButton=ShowExportPdfButton ExportButtonText=@ExportButtonText SearchMode=SearchMode ShowExportPdfButton=ShowExportPdfButton ExportButtonText=@ExportButtonText
ShowExportButton=@ShowExportButton Items=Items ClickToSelect=ClickToSelect ScrollMode=ScrollMode ShowExportButton=@ShowExportButton Items=Items ClickToSelect=ClickToSelect ScrollMode=ScrollMode
ShowExportCsvButton=@ShowExportCsvButton ShowCardView=ShowCardView ShowExportCsvButton=@ShowExportCsvButton ShowCardView=ShowCardView OnColumnVisibleChanged=OnColumnVisibleChanged
FixedExtendButtonsColumn=FixedExtendButtonsColumn FixedMultipleColumn=FixedMultipleColumn FixedDetailRowHeaderColumn=FixedDetailRowHeaderColumn FixedLineNoColumn=FixedLineNoColumn FixedExtendButtonsColumn=FixedExtendButtonsColumn FixedMultipleColumn=FixedMultipleColumn FixedDetailRowHeaderColumn=FixedDetailRowHeaderColumn FixedLineNoColumn=FixedLineNoColumn
IsAutoRefresh=IsAutoRefresh AutoRefreshInterval=AutoRefreshInterval IsAutoRefresh=IsAutoRefresh AutoRefreshInterval=AutoRefreshInterval
AllowDragColumn=@AllowDragColumn Height=@Height ShowRefresh=ShowRefresh AllowDragColumn=@AllowDragColumn Height=@Height ShowRefresh=ShowRefresh
@@ -41,6 +41,7 @@
DoubleClickToEdit="DoubleClickToEdit" DoubleClickToEdit="DoubleClickToEdit"
OnDoubleClickCellCallback="OnDoubleClickCellCallback" OnDoubleClickCellCallback="OnDoubleClickCellCallback"
OnDoubleClickRowCallback="OnDoubleClickRowCallback" OnDoubleClickRowCallback="OnDoubleClickRowCallback"
RowContentTemplate="RowContentTemplate"
OnClickRowCallback="OnClickRowCallback"> OnClickRowCallback="OnClickRowCallback">
</Table> </Table>
</div> </div>

View File

@@ -13,6 +13,23 @@ namespace ThingsGateway.Admin.Razor;
[CascadingTypeParameter(nameof(TItem))] [CascadingTypeParameter(nameof(TItem))]
public partial class AdminTable<TItem> where TItem : class, new() public partial class AdminTable<TItem> where TItem : class, new()
{ {
/// <inheritdoc cref="Table{TItem}.OnColumnVisibleChanged"/>
[Parameter]
public Func<string,bool, Task> OnColumnVisibleChanged { get; set; }
/// <inheritdoc cref="Table{TItem}.OnColumnCreating"/>
[Parameter]
public Func<List<ITableColumn>,Task> OnColumnCreating { get; set; }
/// <inheritdoc cref="Table{TItem}.RenderMode"/>
[Parameter]
public TableRenderMode RenderMode { get; set; }
public List<ITableColumn> Columns => Instance?.Columns;
public IEnumerable<ITableColumn> GetVisibleColumns => Instance?.GetVisibleColumns();
public List<TItem> Rows => Instance?.Rows;
/// <inheritdoc cref="Table{TItem}.SelectedRowsChanged"/> /// <inheritdoc cref="Table{TItem}.SelectedRowsChanged"/>
[Parameter] [Parameter]
public EventCallback<List<TItem>> SelectedRowsChanged { get; set; } public EventCallback<List<TItem>> SelectedRowsChanged { get; set; }
@@ -40,6 +57,10 @@ public partial class AdminTable<TItem> where TItem : class, new()
/// <inheritdoc cref="Table{TItem}.OnDoubleClickRowCallback"/> /// <inheritdoc cref="Table{TItem}.OnDoubleClickRowCallback"/>
[Parameter] [Parameter]
public Func<TItem, Task>? OnDoubleClickRowCallback { get; set; } public Func<TItem, Task>? OnDoubleClickRowCallback { get; set; }
/// <inheritdoc cref="Table{TItem}.RowContentTemplate"/>
[Parameter]
public RenderFragment<TableRowContext<TItem>>? RowContentTemplate { get; set; }
/// <inheritdoc cref="Table{TItem}.OnClickRowCallback"/> /// <inheritdoc cref="Table{TItem}.OnClickRowCallback"/>
[Parameter] [Parameter]
public Func<TItem, Task>? OnClickRowCallback { get; set; } public Func<TItem, Task>? OnClickRowCallback { get; set; }
@@ -146,6 +167,9 @@ public partial class AdminTable<TItem> where TItem : class, new()
[Parameter] [Parameter]
public IDataService<TItem> DataService { get; set; } public IDataService<TItem> DataService { get; set; }
[Parameter]
public string? Id { get; set; }
/// <inheritdoc cref="Table{TItem}.CreateItemCallback"/> /// <inheritdoc cref="Table{TItem}.CreateItemCallback"/>
[Parameter] [Parameter]
public Func<TItem> CreateItemCallback { get; set; } public Func<TItem> CreateItemCallback { get; set; }

View File

@@ -125,13 +125,22 @@ public class BlazorAppContext
var ownMenus = OwnMenus.Where(a => a.Module == CurrentModuleId); var ownMenus = OwnMenus.Where(a => a.Module == CurrentModuleId);
OwnMenuItems = AdminResourceUtil.BuildMenuTrees(ownMenus).ToList(); OwnMenuItems = AdminResourceUtil.BuildMenuTrees(ownMenus).ToList();
AllOwnMenuItems = AdminResourceUtil.BuildMenuTrees(OwnMenus).ToList(); AllOwnMenuItems = AdminResourceUtil.BuildMenuTrees(OwnMenus).ToList();
OwnSameLevelMenuItems = ownMenus.Where(a => !a.Href.IsNullOrWhiteSpace()).Select(item => new MenuItem() OwnSameLevelMenuItems = ownMenus.Where(a => !a.Href.IsNullOrWhiteSpace()).Select(item =>
{ {
Match = item.NavLinkMatch ?? Microsoft.AspNetCore.Components.Routing.NavLinkMatch.All, var menu = new MenuItem()
Text = item.Title, {
Icon = item.Icon, Match = item.NavLinkMatch ?? Microsoft.AspNetCore.Components.Routing.NavLinkMatch.Prefix,
Url = item.Href, Text = item.Title,
Target = item.Target.ToString(), Icon = item.Icon,
Url = item.Href,
Target = item.Target.ToString(),
};
if (menu.Url.IsNullOrEmpty())
{
menu.Match = Microsoft.AspNetCore.Components.Routing.NavLinkMatch.Prefix;
}
return menu;
}).ToList(); }).ToList();
UserWorkbenchOutputs = AllMenus.Where(it => UserWorkBench.Shortcuts.Contains(it.Id)).ToList(); UserWorkbenchOutputs = AllMenus.Where(it => UserWorkBench.Shortcuts.Contains(it.Id)).ToList();
} }

View File

@@ -72,7 +72,7 @@ public partial class HardwareInfoPage : IDisposable
ChartDataSource.Options.Title = Localizer[nameof(HistoryHardwareInfo)]; ChartDataSource.Options.Title = Localizer[nameof(HistoryHardwareInfo)];
ChartDataSource.Options.X.Title = Localizer["DateTime"]; ChartDataSource.Options.X.Title = Localizer["DateTime"];
ChartDataSource.Options.Y.Title = Localizer["Data"]; ChartDataSource.Options.Y.Title = Localizer["Data"];
ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd HH:mm zz")); ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd-HH:mm"));
ChartDataSource.Data.Add(new ChartDataset() ChartDataSource.Data.Add(new ChartDataset()
{ {
Tension = 0.4f, Tension = 0.4f,
@@ -116,7 +116,7 @@ public partial class HardwareInfoPage : IDisposable
else else
{ {
var hisHardwareInfos = await HardwareJob.GetHistoryHardwareInfos(); var hisHardwareInfos = await HardwareJob.GetHistoryHardwareInfos();
ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd HH:mm zz")); ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd-HH:mm"));
ChartDataSource.Data[0].Data = hisHardwareInfos.Select(a => (object)a.CpuUsage); ChartDataSource.Data[0].Data = hisHardwareInfos.Select(a => (object)a.CpuUsage);
ChartDataSource.Data[1].Data = hisHardwareInfos.Select(a => (object)a.MemoryUsage); ChartDataSource.Data[1].Data = hisHardwareInfos.Select(a => (object)a.MemoryUsage);
ChartDataSource.Data[2].Data = hisHardwareInfos.Select(a => (object)a.DriveUsage); ChartDataSource.Data[2].Data = hisHardwareInfos.Select(a => (object)a.DriveUsage);

View File

@@ -15,7 +15,7 @@
</Card> </Card>
</div> </div>
<div class="col-12 col-sm-10 h-100"> <div class="col-12 col-sm-10 h-100 ps-2">
<AdminTable @ref=table TItem="SysUser" <AdminTable @ref=table TItem="SysUser"
AutoGenerateColumns="true" AutoGenerateColumns="true"
ShowAdvancedSearch=false ShowAdvancedSearch=false

View File

@@ -20,5 +20,6 @@ public class Startup : AppStartup
services.AddScoped<IMenuService, MenuService>(); services.AddScoped<IMenuService, MenuService>();
services.AddScoped<IAuthRazorService, AuthRazorService>(); services.AddScoped<IAuthRazorService, AuthRazorService>();
services.AddBootstrapBlazorTableExportService(); services.AddBootstrapBlazorTableExportService();
services.AddBootstrapBlazorWinBoxService();
} }
} }

View File

@@ -5,19 +5,21 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" /> <ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.0" /> <PackageReference Include="BootstrapBlazor.Chart" Version="9.0.3" />
<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" /> <PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" />
<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" />
<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'"> <ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET8Version)" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET8Version)" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net9.0'"> <ItemGroup Condition="'$(TargetFramework)'=='net10.0'">
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET9Version)" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET10Version)" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks> <TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
<!--<UseRazorSourceGenerator>false</UseRazorSourceGenerator>--> <!--<UseRazorSourceGenerator>false</UseRazorSourceGenerator>-->
</PropertyGroup> </PropertyGroup>

View File

@@ -41,15 +41,22 @@ public static class AdminResourceUtil
return items return items
.Where(it => it.ParentId == parentId) .Where(it => it.ParentId == parentId)
.Select((item, index) => .Select((item, index) =>
new MenuItem() {
var menu = new MenuItem()
{ {
Match = item.NavLinkMatch ?? Microsoft.AspNetCore.Components.Routing.NavLinkMatch.All, Match = item.NavLinkMatch ?? Microsoft.AspNetCore.Components.Routing.NavLinkMatch.Prefix,
Text = item.Title, Text = item.Title,
Icon = item.Icon, Icon = item.Icon,
Url = item.Href, Url = item.Href,
Target = item.Target.ToString(), Target = item.Target.ToString(),
Items = BuildMenuTrees(items, item.Id).ToList() Items = BuildMenuTrees(items, item.Id).ToList()
};
if (menu.Url.IsNullOrEmpty())
{
menu.Match = Microsoft.AspNetCore.Components.Routing.NavLinkMatch.Prefix;
} }
return menu;
}
); );
} }

View File

@@ -21,11 +21,13 @@
<link rel="apple-touch-icon" href="favicon.png"> <link rel="apple-touch-icon" href="favicon.png">
<base href="/" /> <base href="/" />
<title>ThingsGateway</title> <title>ThingsGateway</title>
<link rel="stylesheet" href=@($"_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css?v={this.GetType().Assembly.GetName().Version}") /> <link rel="stylesheet" href=@($"_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css") />
<link rel="stylesheet" href=@($"_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css?v={this.GetType().Assembly.GetName().Version}") /> <link rel="stylesheet" href=@($"_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css") />
<link rel="stylesheet" href=@($"_content/BootstrapBlazor/css/motronic.min.css?v={this.GetType().Assembly.GetName().Version}") /> <link rel="stylesheet" href=@($"_content/BootstrapBlazor/css/motronic.min.css") />
<link rel="stylesheet" href=@($"ThingsGateway.AdminServer.styles.css?v={this.GetType().Assembly.GetName().Version}") /> <link rel="stylesheet" href=@($"ThingsGateway.AdminServer.styles.css") />
<link rel="stylesheet" href=@($"{WebsiteConst.DefaultResourceUrl}css/site.css?v={this.GetType().Assembly.GetName().Version}") /> <link rel="stylesheet" href=@($"{WebsiteConst.DefaultResourceUrl}css/site.css") />
<link rel="stylesheet" href=@($"{WebsiteConst.DefaultResourceUrl}css/devui.css") />
@* <script src=@($"{WebsiteConst.DefaultResourceUrl}js/theme.js") type="module"></script><!-- 初始主题 --> *@ @* <script src=@($"{WebsiteConst.DefaultResourceUrl}js/theme.js") type="module"></script><!-- 初始主题 --> *@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="./manifest.json" /> <link rel="manifest" href="./manifest.json" />
@@ -38,12 +40,13 @@
<BlazorReconnector @rendermode="new InteractiveServerRenderMode(false)" /> <BlazorReconnector @rendermode="new InteractiveServerRenderMode(false)" />
<script src=@($"_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js?v={this.GetType().Assembly.GetName().Version}")></script> <script src=@($"_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js")></script>
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js?v={this.GetType().Assembly.GetName().Version}")></script> <script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js")></script>
<script src="_framework/blazor.web.js"></script> <script src="_framework/blazor.web.js"></script>
<!-- PWA Service Worker --> <!-- PWA Service Worker -->
<script type="text/javascript">'serviceWorker' in navigator && navigator.serviceWorker.register('./service-worker.js')</script> <script type="text/javascript">'serviceWorker' in navigator && navigator.serviceWorker.register('./service-worker.js')</script>
<script src="pwa-install.js"></script>
</body> </body>
</html> </html>

View File

@@ -70,7 +70,7 @@
<Button @onclick="ShowAbout" class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-info" Color="Color.None" TooltipText="@Localizer[nameof(About)]" /> <Button @onclick="ShowAbout" class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-info" Color="Color.None" TooltipText="@Localizer[nameof(About)]" />
} }
@* 版本号 *@ @* 版本号 *@
<div class="px-1 navbar-header-text d-none d-lg-block">@_versionString</div> <div class="px-1 navbar-header-text text-nowrap d-none d-lg-block">@_versionString</div>
@* 主题切换 *@ @* 主题切换 *@
@* <ThemeToggle /> *@ @* <ThemeToggle /> *@
@@ -89,12 +89,19 @@
</div> </div>
</Side> </Side>
<Main> <Main>
<Tab @ref=_tab ClickTabToNavigation="true" ShowToolbar="true" ShowContextMenu="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true <Tab @ref=_tab ClickTabToNavigation="true" ShowToolbar="true" ShowContextMenu="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true
ShowFullscreenToolbarButton=false ShowContextMenuFullScreen=false ShowFullScreen=false AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.AllOwnMenuItems" ShowFullscreenToolbarButton=false ShowContextMenuFullScreen=false ShowFullScreen=false AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.AllOwnMenuItems"
DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=> DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=>
{ {
return Task.FromResult(!(a.Url=="/"||a.Url.IsNullOrEmpty())); return Task.FromResult(!(a.Url == "/" || a.Url.IsNullOrEmpty()));
})> })
>
<BeforeContextMenuTemplate>
<ContextMenuItem Icon="fa fa-window-restore" Text="@Localizer["WindowRestore"]" OnClick="WinboxRender"></ContextMenuItem>
<ContextMenuDivider></ContextMenuDivider>
</BeforeContextMenuTemplate>
</Tab> </Tab>
</Main> </Main>
<NotAuthorized> <NotAuthorized>

View File

@@ -120,6 +120,38 @@ public partial class MainLayout : IDisposable
#endregion #endregion
private async Task WinboxRender(ContextMenuItem item, object? context)
{
if (context is TabItem tabItem)
{
await WinboxRender(tabItem.ChildContent, tabItem.Text);
//await _tab.RemoveTab(tabItem);
}
}
[Inject]
[NotNull]
private WinBoxService? WinBoxService { get; set; }
private async Task WinboxRender(RenderFragment item, string title)
{
if (item != null)
{
var option = new WinBoxOption()
{
Title = title,
ContentTemplate = item,
Max = false,
Width = "80%",
Height = "80%",
Top = "0%",
Left = "10%",
Background = "var(--bb-primary-color)",
Overflow = true
};
await WinBoxService.Show(option);
}
}
private string _versionString = string.Empty; private string _versionString = string.Empty;
[Inject] [Inject]
[NotNull] [NotNull]

View File

@@ -132,7 +132,11 @@ public class Startup : AppStartup
services.Configure<ForwardedHeadersOptions>(options => services.Configure<ForwardedHeadersOptions>(options =>
{ {
options.ForwardedHeaders = ForwardedHeaders.All; options.ForwardedHeaders = ForwardedHeaders.All;
#if NET10_0_OR_GREATER
options.KnownIPNetworks.Clear();
#else
options.KnownNetworks.Clear(); options.KnownNetworks.Clear();
#endif
options.KnownProxies.Clear(); options.KnownProxies.Clear();
}); });
@@ -204,7 +208,16 @@ public class Startup : AppStartup
public void Use(IApplicationBuilder applicationBuilder, IWebHostEnvironment env) public void Use(IApplicationBuilder applicationBuilder, IWebHostEnvironment env)
{ {
var app = (WebApplication)applicationBuilder; var app = (WebApplication)applicationBuilder;
app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.All, KnownNetworks = { }, KnownProxies = { } }); app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.All,
#if NET10_0_OR_GREATER
KnownIPNetworks = { },
#else
KnownNetworks = { },
#endif
KnownProxies = { }
});
app.UseBootstrapBlazor(); app.UseBootstrapBlazor();
// 启用本地化 // 启用本地化

View File

@@ -4,7 +4,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks> <TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
@@ -53,9 +53,9 @@
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net10.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="$(NET9Version)" /> <PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="$(NET10Version)" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(NET9Version)" /> <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(NET10Version)" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -0,0 +1,34 @@
let installPromptTriggered = false;
function getCookie(name) {
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
return match ? match[2] : null;
}
function hasShownInstallPrompt() {
return getCookie("tgPWAInstallPromptShown") === "true";
}
function markInstallPromptShown() {
document.cookie = "tgPWAInstallPromptShown=true; max-age=31536000; path=/";
}
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
if (!hasShownInstallPrompt() && !installPromptTriggered) {
installPromptTriggered = true;
setTimeout(() => {
e.prompt()
.then(() => e.userChoice)
.then(choiceResult => {
markInstallPromptShown();
})
.catch(err => {
// 可选错误处理
});
}, 2000); // 延迟 2 秒提示
} else {
// console.log("已提示过安装,不再弹出");
}
});

View File

@@ -1,4 +1,4 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有 // 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议 // 源代码使用协议遵循本仓库的开源协议及附加协议
@@ -477,7 +477,7 @@ public class ConcurrentList<T> : IList<T>, IReadOnlyList<T>
{ {
lock (((ICollection)m_list).SyncRoot) lock (((ICollection)m_list).SyncRoot)
{ {
return m_list.IndexOf(item); return m_list.LastIndexOf(item);
} }
} }

View File

@@ -31,8 +31,8 @@ public static class GenericExtensions
// 比较oldModel和model的属性找出差异 // 比较oldModel和model的属性找出差异
var differences = properties var differences = properties
.Where(prop => prop.CanRead && prop.CanWrite) // 确保属性可读可写 .Where(prop => prop.CanRead && prop.CanWrite && !Equals(prop.GetValue(oldModel), prop.GetValue(model))) // 确保属性可读可写
.Where(prop => !Equals(prop.GetValue(oldModel), prop.GetValue(model))) // 找出值不同的属性 // 找出值不同的属性
.ToDictionary(prop => prop.Name, prop => prop.GetValue(model)); // 将属性名和新值存储到字典中 .ToDictionary(prop => prop.Name, prop => prop.GetValue(model)); // 将属性名和新值存储到字典中
// 应用差异到channels列表中的每个Channel对象 // 应用差异到channels列表中的每个Channel对象

View File

@@ -16,6 +16,8 @@ using System.Runtime.CompilerServices;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ThingsGateway.Extension;
namespace ThingsGateway.Common.Extension; namespace ThingsGateway.Common.Extension;
/// <summary> /// <summary>
/// 对象拓展类 /// 对象拓展类
@@ -48,113 +50,7 @@ public static class ObjectExtensions
bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type); bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type);
} }
/// <summary>
/// 将 DateTimeOffset 转换成本地 DateTime
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static DateTime ConvertToDateTime(this DateTimeOffset dateTime)
{
if (dateTime.Offset.Equals(TimeSpan.Zero))
return dateTime.UtcDateTime;
if (dateTime.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(dateTime.DateTime)))
return dateTime.ToLocalTime().DateTime;
else
return dateTime.DateTime;
}
/// <summary>
/// 将 DateTimeOffset? 转换成本地 DateTime?
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static DateTime? ConvertToDateTime(this DateTimeOffset? dateTime)
{
return dateTime.HasValue ? dateTime.Value.ConvertToDateTime() : null;
}
/// <summary>
/// 将 DateTime 转换成 DateTimeOffset
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static DateTimeOffset ConvertToDateTimeOffset(this DateTime dateTime)
{
return DateTime.SpecifyKind(dateTime, DateTimeKind.Local);
}
/// <summary>
/// 将 DateTime? 转换成 DateTimeOffset?
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static DateTimeOffset? ConvertToDateTimeOffset(this DateTime? dateTime)
{
return dateTime.HasValue ? dateTime.Value.ConvertToDateTimeOffset() : null;
}
/// <summary>
/// 将流保存到本地磁盘
/// </summary>
/// <param name="stream"></param>
/// <param name="path"></param>
/// <returns></returns>
public static void CopyToSave(this Stream stream, string path)
{
// 空检查
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullException(nameof(path));
using var fileStream = File.Create(path);
stream.CopyTo(fileStream);
}
/// <summary>
/// 将字节数组保存到本地磁盘
/// </summary>
/// <param name="bytes"></param>
/// <param name="path"></param>
/// <returns></returns>
public static void CopyToSave(this byte[] bytes, string path)
{
using var stream = new MemoryStream(bytes);
stream.CopyToSave(path);
}
/// <summary>
/// 将流保存到本地磁盘
/// </summary>
/// <param name="stream"></param>
/// <param name="path">需包含文件名完整路径</param>
/// <returns></returns>
public static async Task CopyToSaveAsync(this Stream stream, string path)
{
// 空检查
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentNullException(nameof(path));
}
// 文件名判断
if (string.IsNullOrWhiteSpace(Path.GetFileName(path)))
{
throw new ArgumentException("The parameter of <path> parameter must include the complete file name.");
}
using var fileStream = File.Create(path);
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
}
/// <summary>
/// 将字节数组保存到本地磁盘
/// </summary>
/// <param name="bytes"></param>
/// <param name="path"></param>
/// <returns></returns>
public static async Task CopyToSaveAsync(this byte[] bytes, string path)
{
using var stream = new MemoryStream(bytes);
await stream.CopyToSaveAsync(path).ConfigureAwait(false);
}
/// <summary> /// <summary>
/// 合并两个字典 /// 合并两个字典
@@ -186,7 +82,7 @@ public static class ObjectExtensions
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="dic">字典</param> /// <param name="dic">字典</param>
/// <param name="newDic">新字典</param> /// <param name="newDic">新字典</param>
internal static void AddOrUpdate<T>(this ConcurrentDictionary<string, T> dic, Dictionary<string, T> newDic) internal static void AddOrUpdate<T>(this NonBlockingDictionary<string, T> dic, Dictionary<string, T> newDic)
{ {
foreach (var (key, value) in newDic) foreach (var (key, value) in newDic)
{ {

View File

@@ -24,7 +24,7 @@ public static class ParallelExtensions
/// <typeparam name="T">集合元素类型</typeparam> /// <typeparam name="T">集合元素类型</typeparam>
/// <param name="source">要操作的集合</param> /// <param name="source">要操作的集合</param>
/// <param name="body">要执行的操作</param> /// <param name="body">要执行的操作</param>
public static void ParallelForEach<T>(this IList<T> source, Action<T> body) public static void ParallelForEach<T>(this IEnumerable<T> source, Action<T> body)
{ {
ParallelOptions options = new(); ParallelOptions options = new();
options.MaxDegreeOfParallelism = Environment.ProcessorCount; options.MaxDegreeOfParallelism = Environment.ProcessorCount;
@@ -38,7 +38,7 @@ public static class ParallelExtensions
/// <typeparam name="T">集合元素类型</typeparam> /// <typeparam name="T">集合元素类型</typeparam>
/// <param name="source">要操作的集合</param> /// <param name="source">要操作的集合</param>
/// <param name="body">要执行的操作</param> /// <param name="body">要执行的操作</param>
public static void ParallelForEach<T>(this IList<T> source, Action<T, ParallelLoopState, long> body) public static void ParallelForEach<T>(this IEnumerable<T> source, Action<T, ParallelLoopState, long> body)
{ {
ParallelOptions options = new(); ParallelOptions options = new();
options.MaxDegreeOfParallelism = Environment.ProcessorCount; options.MaxDegreeOfParallelism = Environment.ProcessorCount;
@@ -53,7 +53,7 @@ public static class ParallelExtensions
/// <param name="source">要操作的集合</param> /// <param name="source">要操作的集合</param>
/// <param name="body">要执行的操作</param> /// <param name="body">要执行的操作</param>
/// <param name="parallelCount">最大并行度</param> /// <param name="parallelCount">最大并行度</param>
public static void ParallelForEach<T>(this IList<T> source, Action<T> body, int parallelCount) public static void ParallelForEach<T>(this IEnumerable<T> source, Action<T> body, int parallelCount)
{ {
// 创建并行操作的选项对象,设置最大并行度为指定的值 // 创建并行操作的选项对象,设置最大并行度为指定的值
var options = new ParallelOptions(); var options = new ParallelOptions();
@@ -109,7 +109,7 @@ public static class ParallelExtensions
/// <param name="parallelCount">最大并行度</param> /// <param name="parallelCount">最大并行度</param>
/// <param name="cancellationToken">取消操作的标志</param> /// <param name="cancellationToken">取消操作的标志</param>
/// <returns>表示异步操作的任务</returns> /// <returns>表示异步操作的任务</returns>
public static Task ParallelForEachAsync<T>(this IList<T> source, Func<T, CancellationToken, ValueTask> body, int parallelCount, CancellationToken cancellationToken = default) public static Task ParallelForEachAsync<T>(this IEnumerable<T> source, Func<T, CancellationToken, ValueTask> body, int parallelCount, CancellationToken cancellationToken = default)
{ {
// 创建并行操作的选项对象,设置最大并行度和取消标志 // 创建并行操作的选项对象,设置最大并行度和取消标志
var options = new ParallelOptions(); var options = new ParallelOptions();
@@ -126,7 +126,7 @@ public static class ParallelExtensions
/// <param name="body">异步执行的操作</param> /// <param name="body">异步执行的操作</param>
/// <param name="cancellationToken">取消操作的标志</param> /// <param name="cancellationToken">取消操作的标志</param>
/// <returns>表示异步操作的任务</returns> /// <returns>表示异步操作的任务</returns>
public static Task ParallelForEachAsync<T>(this IList<T> source, Func<T, CancellationToken, ValueTask> body, CancellationToken cancellationToken = default) public static Task ParallelForEachAsync<T>(this IEnumerable<T> source, Func<T, CancellationToken, ValueTask> body, CancellationToken cancellationToken = default)
{ {
return ParallelForEachAsync(source, body, Environment.ProcessorCount, cancellationToken); return ParallelForEachAsync(source, body, Environment.ProcessorCount, cancellationToken);
} }

View File

@@ -66,7 +66,8 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, "{JsonStringLocalizerName} searched for '{Name}' in '{typeName}' with culture '{CultureName}' throw exception.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name); if (Logger?.IsEnabled(LogLevel.Error) == true)
Logger.LogError(ex, "{JsonStringLocalizerName} searched for '{Name}' in '{typeName}' with culture '{CultureName}' throw exception.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name);
} }
return ret; return ret;
} }
@@ -176,7 +177,8 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
localizationMissingItemHandler.HandleMissingItem(name, typeName, CultureInfo.CurrentUICulture.Name); localizationMissingItemHandler.HandleMissingItem(name, typeName, CultureInfo.CurrentUICulture.Name);
if (jsonLocalizationOptions.IgnoreLocalizerMissing == false) if (jsonLocalizationOptions.IgnoreLocalizerMissing == false)
{ {
Logger.LogInformation("{JsonStringLocalizerName} searched for '{Name}' in '{TypeName}' with culture '{CultureName}' not found.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name); if (Logger?.IsEnabled(LogLevel.Information) == true)
Logger.LogInformation("{JsonStringLocalizerName} searched for '{Name}' in '{TypeName}' with culture '{CultureName}' not found.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name);
} }
_missingManifestCache.TryAdd($"name={name}&culture={CultureInfo.CurrentUICulture.Name}"); _missingManifestCache.TryAdd($"name={name}&culture={CultureInfo.CurrentUICulture.Name}");
} }

View File

@@ -8,13 +8,13 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks> <TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" /> <PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.7" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" /> <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
<PackageReference Include="BootstrapBlazor" Version="9.10.2" /> <PackageReference Include="BootstrapBlazor" Version="9.11.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -27,10 +27,15 @@ public abstract class PrimaryIdEntity : IPrimaryIdEntity
public virtual long Id { get; set; } public virtual long Id { get; set; }
} }
public interface IPrimaryKeyEntity
{
string ExtJson { get; set; }
}
/// <summary> /// <summary>
/// 主键实体基类 /// 主键实体基类
/// </summary> /// </summary>
public abstract class PrimaryKeyEntity : PrimaryIdEntity public abstract class PrimaryKeyEntity : PrimaryIdEntity, IPrimaryKeyEntity
{ {
/// <summary> /// <summary>
/// 拓展信息 /// 拓展信息

View File

@@ -8,7 +8,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks> <TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
</PropertyGroup> </PropertyGroup>

View File

@@ -12,7 +12,7 @@
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using ThingsGateway; using ThingsGateway;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.Reflection; using ThingsGateway.Reflection;
namespace Microsoft.Extensions.Hosting; namespace Microsoft.Extensions.Hosting;

View File

@@ -20,7 +20,7 @@ using System.Text.RegularExpressions;
using ThingsGateway.NewLife; using ThingsGateway.NewLife;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// 对象拓展类 /// 对象拓展类
@@ -28,70 +28,10 @@ namespace ThingsGateway.Extensions;
[SuppressSniffer] [SuppressSniffer]
public static class ObjectExtensions public static class ObjectExtensions
{ {
/// <summary>
/// 将 DateTimeOffset 转换成本地 DateTime
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static DateTime ConvertToDateTime(this DateTimeOffset dateTime)
{
if (dateTime.Offset.Equals(TimeSpan.Zero))
return dateTime.UtcDateTime;
if (dateTime.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(dateTime.DateTime)))
return dateTime.ToLocalTime().DateTime;
else
return dateTime.DateTime;
}
/// <summary>
/// 将 DateTimeOffset? 转换成本地 DateTime?
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static DateTime? ConvertToDateTime(this DateTimeOffset? dateTime)
{
return dateTime.HasValue ? dateTime.Value.ConvertToDateTime() : null;
}
/// <summary>
/// 将 DateTime 转换成 DateTimeOffset
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static DateTimeOffset ConvertToDateTimeOffset(this DateTime dateTime)
{
return DateTime.SpecifyKind(dateTime, DateTimeKind.Local);
}
/// <summary>
/// 将 DateTime? 转换成 DateTimeOffset?
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static DateTimeOffset? ConvertToDateTimeOffset(this DateTime? dateTime)
{
return dateTime.HasValue ? dateTime.Value.ConvertToDateTimeOffset() : null;
}
/// <summary>
/// 将时间戳转换为 DateTime
/// </summary>
/// <param name="timestamp"></param>
/// <returns></returns>
internal static DateTime ConvertToDateTime(this long timestamp)
{
var timeStampDateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var digitCount = (int)Math.Floor(Math.Log10(timestamp) + 1);
if (digitCount != 13 && digitCount != 10)
{
throw new ArgumentException("Data is not a valid timestamp format.");
}
return (digitCount == 13
? timeStampDateTime.AddMilliseconds(timestamp) // 13 位时间戳
: timeStampDateTime.AddSeconds(timestamp)).ToLocalTime(); // 10 位时间戳
}
/// <summary> /// <summary>
/// 将 IFormFile 转换成 byte[] /// 将 IFormFile 转换成 byte[]
@@ -265,7 +205,7 @@ public static class ObjectExtensions
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="dic">字典</param> /// <param name="dic">字典</param>
/// <param name="newDic">新字典</param> /// <param name="newDic">新字典</param>
internal static void AddOrUpdate<T>(this ConcurrentDictionary<string, T> dic, Dictionary<string, T> newDic) internal static void AddOrUpdate<T>(this NonBlockingDictionary<string, T> dic, Dictionary<string, T> newDic)
{ {
foreach (var (key, value) in newDic) foreach (var (key, value) in newDic)
{ {

View File

@@ -94,7 +94,7 @@ public static class AspNetCoreBuilderServiceCollectionExtensions
/// <param name="mvcBuilder"></param> /// <param name="mvcBuilder"></param>
/// <param name="configure"></param> /// <param name="configure"></param>
/// <returns></returns> /// <returns></returns>
public static IMvcBuilder AddFromConvertBinding(this IMvcBuilder mvcBuilder, Action<ConcurrentDictionary<Type, Type>> configure = default) public static IMvcBuilder AddFromConvertBinding(this IMvcBuilder mvcBuilder, Action<NonBlockingDictionary<Type, Type>> configure = default)
{ {
mvcBuilder.Services.AddFromConvertBinding(configure); mvcBuilder.Services.AddFromConvertBinding(configure);
@@ -107,13 +107,13 @@ public static class AspNetCoreBuilderServiceCollectionExtensions
/// <param name="services"></param> /// <param name="services"></param>
/// <param name="configure"></param> /// <param name="configure"></param>
/// <returns></returns> /// <returns></returns>
public static IServiceCollection AddFromConvertBinding(this IServiceCollection services, Action<ConcurrentDictionary<Type, Type>> configure = default) public static IServiceCollection AddFromConvertBinding(this IServiceCollection services, Action<NonBlockingDictionary<Type, Type>> configure = default)
{ {
// 非 Web 环境跳过注册 // 非 Web 环境跳过注册
if (App.WebHostEnvironment == default) return services; if (App.WebHostEnvironment == default) return services;
// 定义模型绑定转换器集合 // 定义模型绑定转换器集合
var modelBinderConverts = new ConcurrentDictionary<Type, Type>(); var modelBinderConverts = new NonBlockingDictionary<Type, Type>();
modelBinderConverts.TryAdd(typeof(DateTime), typeof(DateTimeModelConvertBinder)); modelBinderConverts.TryAdd(typeof(DateTime), typeof(DateTimeModelConvertBinder));
modelBinderConverts.TryAdd(typeof(DateTimeOffset), typeof(DateTimeOffsetModelConvertBinder)); modelBinderConverts.TryAdd(typeof(DateTimeOffset), typeof(DateTimeOffsetModelConvertBinder));

View File

@@ -11,7 +11,7 @@
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace ThingsGateway.AspNetCore; namespace ThingsGateway.AspNetCore;

View File

@@ -27,13 +27,13 @@ public class FromConvertBinder : IModelBinder
/// <summary> /// <summary>
/// 定义模型绑定转换器集合 /// 定义模型绑定转换器集合
/// </summary> /// </summary>
private readonly ConcurrentDictionary<Type, Type> _modelBinderConverts; private readonly NonBlockingDictionary<Type, Type> _modelBinderConverts;
/// <summary> /// <summary>
/// 构造函数 /// 构造函数
/// </summary> /// </summary>
/// <param name="modelBinderConverts">定义模型绑定转换器集合</param> /// <param name="modelBinderConverts">定义模型绑定转换器集合</param>
public FromConvertBinder(ConcurrentDictionary<Type, Type> modelBinderConverts) public FromConvertBinder(NonBlockingDictionary<Type, Type> modelBinderConverts)
{ {
_modelBinderConverts = modelBinderConverts; _modelBinderConverts = modelBinderConverts;
} }

View File

@@ -28,13 +28,13 @@ public class FromConvertBinderProvider : IModelBinderProvider
/// <summary> /// <summary>
/// 定义模型绑定转换器集合 /// 定义模型绑定转换器集合
/// </summary> /// </summary>
private readonly ConcurrentDictionary<Type, Type> _modelBinderConverts; private readonly NonBlockingDictionary<Type, Type> _modelBinderConverts;
/// <summary> /// <summary>
/// 构造函数 /// 构造函数
/// </summary> /// </summary>
/// <param name="modelBinderConverts">定义模型绑定转换器集合</param> /// <param name="modelBinderConverts">定义模型绑定转换器集合</param>
public FromConvertBinderProvider(ConcurrentDictionary<Type, Type> modelBinderConverts) public FromConvertBinderProvider(NonBlockingDictionary<Type, Type> modelBinderConverts)
{ {
_modelBinderConverts = modelBinderConverts; _modelBinderConverts = modelBinderConverts;
} }

View File

@@ -11,7 +11,7 @@
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace ThingsGateway.AspNetCore; namespace ThingsGateway.AspNetCore;

View File

@@ -12,7 +12,7 @@
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace ThingsGateway.AspNetCore; namespace ThingsGateway.AspNetCore;

View File

@@ -18,7 +18,7 @@ using System.Reflection;
using ThingsGateway; using ThingsGateway;
using ThingsGateway.ConfigurableOptions; using ThingsGateway.ConfigurableOptions;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace Microsoft.Extensions.DependencyInjection; namespace Microsoft.Extensions.DependencyInjection;

View File

@@ -34,9 +34,12 @@ public static class PBKDF2Encryption
using var rng = RandomNumberGenerator.Create(); using var rng = RandomNumberGenerator.Create();
var salt = new byte[saltSize]; var salt = new byte[saltSize];
rng.GetBytes(salt); rng.GetBytes(salt);
#if NET10_0_OR_GREATER
var hash = Rfc2898DeriveBytes.Pbkdf2(System.Text.Encoding.UTF8.GetBytes(text), salt, iterationCount, HashAlgorithmName.SHA256, derivedKeyLength);
#else
using var pbkdf2 = new Rfc2898DeriveBytes(text, salt, iterationCount, HashAlgorithmName.SHA256); using var pbkdf2 = new Rfc2898DeriveBytes(text, salt, iterationCount, HashAlgorithmName.SHA256);
var hash = pbkdf2.GetBytes(derivedKeyLength); var hash = pbkdf2.GetBytes(derivedKeyLength);
#endif
// 分别编码盐和哈希,并用分隔符拼接 // 分别编码盐和哈希,并用分隔符拼接
return Convert.ToBase64String(salt) + SaltHashSeparator + Convert.ToBase64String(hash); return Convert.ToBase64String(salt) + SaltHashSeparator + Convert.ToBase64String(hash);
@@ -65,8 +68,12 @@ public static class PBKDF2Encryption
if (saltBytes.Length != saltSize || storedHashBytes.Length != derivedKeyLength) if (saltBytes.Length != saltSize || storedHashBytes.Length != derivedKeyLength)
return false; return false;
#if NET10_0_OR_GREATER
var computedHash = Rfc2898DeriveBytes.Pbkdf2(System.Text.Encoding.UTF8.GetBytes(text), saltBytes, iterationCount, HashAlgorithmName.SHA256, derivedKeyLength);
#else
using var pbkdf2 = new Rfc2898DeriveBytes(text, saltBytes, iterationCount, HashAlgorithmName.SHA256); using var pbkdf2 = new Rfc2898DeriveBytes(text, saltBytes, iterationCount, HashAlgorithmName.SHA256);
var computedHash = pbkdf2.GetBytes(derivedKeyLength); var computedHash = pbkdf2.GetBytes(derivedKeyLength);
#endif
return computedHash.SequenceEqual(storedHashBytes); return computedHash.SequenceEqual(storedHashBytes);
} }

View File

@@ -16,7 +16,7 @@ using System.ComponentModel.DataAnnotations;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.Templates.Extensions; using ThingsGateway.Templates.Extensions;
namespace ThingsGateway.DataValidation; namespace ThingsGateway.DataValidation;
@@ -40,7 +40,7 @@ public static class DataValidator
/// <summary> /// <summary>
/// 验证类型正则表达式 /// 验证类型正则表达式
/// </summary> /// </summary>
private static readonly ConcurrentDictionary<string, ValidationItemMetadataAttribute> ValidationItemMetadatas; private static readonly NonBlockingDictionary<string, ValidationItemMetadataAttribute> ValidationItemMetadatas;
/// <summary> /// <summary>
/// 构造函数 /// 构造函数
@@ -57,7 +57,7 @@ public static class DataValidator
ValidationItemMetadatas = GetValidationValidationItemMetadatas(); ValidationItemMetadatas = GetValidationValidationItemMetadatas();
// 缓存所有正则表达式 // 缓存所有正则表达式
GetValidationTypeValidationItemMetadataCached = new ConcurrentDictionary<object, (string, ValidationItemMetadataAttribute)>(); GetValidationTypeValidationItemMetadataCached = new NonBlockingDictionary<object, (string, ValidationItemMetadataAttribute)>();
} }
/// <summary> /// <summary>
@@ -203,7 +203,7 @@ public static class DataValidator
/// <summary> /// <summary>
/// 获取验证类型验证Item集合 /// 获取验证类型验证Item集合
/// </summary> /// </summary>
private static readonly ConcurrentDictionary<object, (string, ValidationItemMetadataAttribute)> GetValidationTypeValidationItemMetadataCached; private static readonly NonBlockingDictionary<object, (string, ValidationItemMetadataAttribute)> GetValidationTypeValidationItemMetadataCached;
/// <summary> /// <summary>
/// 获取验证类型正则表达式(需要缓存) /// 获取验证类型正则表达式(需要缓存)
@@ -267,9 +267,9 @@ public static class DataValidator
/// 获取验证类型所有有效的正则表达式 /// 获取验证类型所有有效的正则表达式
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private static ConcurrentDictionary<string, ValidationItemMetadataAttribute> GetValidationValidationItemMetadatas() private static NonBlockingDictionary<string, ValidationItemMetadataAttribute> GetValidationValidationItemMetadatas()
{ {
var vaidationItems = new ConcurrentDictionary<string, ValidationItemMetadataAttribute>(); var vaidationItems = new NonBlockingDictionary<string, ValidationItemMetadataAttribute>();
// 查找所有 [ValidationMessageType] 类型中的 [ValidationMessage] 消息定义 // 查找所有 [ValidationMessageType] 类型中的 [ValidationMessage] 消息定义
var customErrorMessages = ValidationMessageTypes.SelectMany(u => u.GetFields() var customErrorMessages = ValidationMessageTypes.SelectMany(u => u.GetFields()

View File

@@ -353,7 +353,7 @@ public static class DependencyInjectionServiceCollectionExtensions
/// <summary> /// <summary>
/// 类型名称集合 /// 类型名称集合
/// </summary> /// </summary>
private static readonly ConcurrentDictionary<string, Type> TypeNamedCollection; private static readonly NonBlockingDictionary<string, Type> TypeNamedCollection;
/// <summary> /// <summary>
/// 创建代理方法 /// 创建代理方法
@@ -374,7 +374,7 @@ public static class DependencyInjectionServiceCollectionExtensions
GlobalServiceProxyType = App.EffectiveTypes GlobalServiceProxyType = App.EffectiveTypes
.FirstOrDefault(u => typeof(AspectDispatchProxy).IsAssignableFrom(u) && typeof(IGlobalDispatchProxy).IsAssignableFrom(u) && u.IsClass && !u.IsInterface && !u.IsAbstract); .FirstOrDefault(u => typeof(AspectDispatchProxy).IsAssignableFrom(u) && typeof(IGlobalDispatchProxy).IsAssignableFrom(u) && u.IsClass && !u.IsInterface && !u.IsAbstract);
TypeNamedCollection = new ConcurrentDictionary<string, Type>(); TypeNamedCollection = new NonBlockingDictionary<string, Type>();
DispatchCreateMethod = typeof(AspectDispatchProxy).GetMethod(nameof(AspectDispatchProxy.Create)); DispatchCreateMethod = typeof(AspectDispatchProxy).GetMethod(nameof(AspectDispatchProxy.Create));
} }
} }

View File

@@ -21,7 +21,7 @@ using System.Collections.Concurrent;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.UnifyResult; using ThingsGateway.UnifyResult;
namespace ThingsGateway.DynamicApiController; namespace ThingsGateway.DynamicApiController;

View File

@@ -28,21 +28,21 @@ internal static class Penetrates
/// <summary> /// <summary>
/// 请求动词映射字典 /// 请求动词映射字典
/// </summary> /// </summary>
internal static ConcurrentDictionary<string, string> VerbToHttpMethods { get; private set; } internal static NonBlockingDictionary<string, string> VerbToHttpMethods { get; private set; }
/// <summary> /// <summary>
/// 控制器排序集合 /// 控制器排序集合
/// </summary> /// </summary>
internal static ConcurrentDictionary<string, (string, int, Type)> ControllerOrderCollection { get; set; } internal static NonBlockingDictionary<string, (string, int, Type)> ControllerOrderCollection { get; set; }
/// <summary> /// <summary>
/// 构造函数 /// 构造函数
/// </summary> /// </summary>
static Penetrates() static Penetrates()
{ {
ControllerOrderCollection = new ConcurrentDictionary<string, (string, int, Type)>(); ControllerOrderCollection = new NonBlockingDictionary<string, (string, int, Type)>();
VerbToHttpMethods = new ConcurrentDictionary<string, string> VerbToHttpMethods = new NonBlockingDictionary<string, string>
{ {
["post"] = "POST", ["post"] = "POST",
["add"] = "POST", ["add"] = "POST",
@@ -67,13 +67,13 @@ internal static class Penetrates
["patch"] = "PATCH" ["patch"] = "PATCH"
}; };
//IsApiControllerCached = new ConcurrentDictionary<Type, bool>(); //IsApiControllerCached = new NonBlockingDictionary<Type, bool>();
} }
///// <summary> ///// <summary>
///// <see cref="IsApiController(Type)"/> 缓存集合 ///// <see cref="IsApiController(Type)"/> 缓存集合
///// </summary> ///// </summary>
//private static readonly ConcurrentDictionary<Type, bool> IsApiControllerCached; //private static readonly NonBlockingDictionary<Type, bool> IsApiControllerCached;
/// <summary> /// <summary>
/// 是否是Api控制器 /// 是否是Api控制器

View File

@@ -61,7 +61,7 @@ internal sealed class EventBusHostedService : BackgroundService
/// <summary> /// <summary>
/// 事件处理程序集合 /// 事件处理程序集合
/// </summary> /// </summary>
private readonly ConcurrentDictionary<EventHandlerWrapper, EventHandlerWrapper> _eventHandlers = new(); private readonly NonBlockingDictionary<EventHandlerWrapper, EventHandlerWrapper> _eventHandlers = new();
/// <summary> /// <summary>
/// 构造函数 /// 构造函数
@@ -295,7 +295,8 @@ internal sealed class EventBusHostedService : BackgroundService
, retryAction: (total, times) => , retryAction: (total, times) =>
{ {
// 输出重试日志 // 输出重试日志
_logger.LogWarning("Retrying {times}/{total} times for {EventId}", times, total, eventSource.EventId); if (_logger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Warning) == true)
_logger.LogWarning("Retrying {times}/{total} times for {EventId}", times, total, eventSource.EventId);
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
else else

View File

@@ -17,7 +17,7 @@ using System.ComponentModel.DataAnnotations;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.Templates.Extensions; using ThingsGateway.Templates.Extensions;
namespace ThingsGateway.FriendlyException; namespace ThingsGateway.FriendlyException;
@@ -31,7 +31,7 @@ public static class Oops
/// <summary> /// <summary>
/// 方法错误异常特性 /// 方法错误异常特性
/// </summary> /// </summary>
private static readonly ConcurrentDictionary<MethodBase, MethodIfException> _errorMethods; private static readonly NonBlockingDictionary<MethodBase, MethodIfException> _errorMethods;
/// <summary> /// <summary>
/// 错误代码类型 /// 错误代码类型
@@ -41,7 +41,7 @@ public static class Oops
/// <summary> /// <summary>
/// 错误消息字典 /// 错误消息字典
/// </summary> /// </summary>
private static readonly ConcurrentDictionary<string, string> _errorCodeMessages; private static readonly NonBlockingDictionary<string, string> _errorCodeMessages;
/// <summary> /// <summary>
/// 友好异常设置 /// 友好异常设置
@@ -53,7 +53,7 @@ public static class Oops
/// </summary> /// </summary>
static Oops() static Oops()
{ {
_errorMethods = new ConcurrentDictionary<MethodBase, MethodIfException>(); _errorMethods = new NonBlockingDictionary<MethodBase, MethodIfException>();
_friendlyExceptionSettings = App.GetConfig<FriendlyExceptionSettingsOptions>("FriendlyExceptionSettings", true); _friendlyExceptionSettings = App.GetConfig<FriendlyExceptionSettingsOptions>("FriendlyExceptionSettings", true);
_errorCodeTypes = GetErrorCodeTypes(); _errorCodeTypes = GetErrorCodeTypes();
_errorCodeMessages = GetErrorCodeMessages(); _errorCodeMessages = GetErrorCodeMessages();
@@ -258,9 +258,9 @@ public static class Oops
/// 获取所有错误消息 /// 获取所有错误消息
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private static ConcurrentDictionary<string, string> GetErrorCodeMessages() private static NonBlockingDictionary<string, string> GetErrorCodeMessages()
{ {
var defaultErrorCodeMessages = new ConcurrentDictionary<string, string>(); var defaultErrorCodeMessages = new NonBlockingDictionary<string, string>();
// 查找所有 [ErrorCodeType] 类型中的 [ErrorCodeMetadata] 元数据定义 // 查找所有 [ErrorCodeType] 类型中的 [ErrorCodeMetadata] 元数据定义
var errorCodeMessages = _errorCodeTypes.SelectMany(u => u.GetFields().Where(u => u.IsDefined(typeof(ErrorCodeItemMetadataAttribute)))) var errorCodeMessages = _errorCodeTypes.SelectMany(u => u.GetFields().Where(u => u.IsDefined(typeof(ErrorCodeItemMetadataAttribute))))

View File

@@ -16,7 +16,7 @@ using Microsoft.AspNetCore.SignalR;
using System.Reflection; using System.Reflection;
using ThingsGateway; using ThingsGateway;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.InstantMessaging; using ThingsGateway.InstantMessaging;
namespace Microsoft.AspNetCore.Builder; namespace Microsoft.AspNetCore.Builder;

View File

@@ -23,7 +23,7 @@ public static class ILoggerExtensions
/// 设置日志上下文 /// 设置日志上下文
/// </summary> /// </summary>
/// <param name="logger"></param> /// <param name="logger"></param>
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param> /// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
/// <returns></returns> /// <returns></returns>
public static IDisposable ScopeContext(this ILogger logger, IDictionary<string, object> properties) public static IDisposable ScopeContext(this ILogger logger, IDictionary<string, object> properties)
{ {

View File

@@ -9,7 +9,7 @@
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace ThingsGateway.Logging; namespace ThingsGateway.Logging;

View File

@@ -82,7 +82,7 @@ public static class StringLoggingExtensions
/// 配置日志上下文 /// 配置日志上下文
/// </summary> /// </summary>
/// <param name="message"></param> /// <param name="message"></param>
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param> /// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
/// <returns></returns> /// <returns></returns>
public static StringLoggingPart ScopeContext(this string message, IDictionary<string, object> properties) public static StringLoggingPart ScopeContext(this string message, IDictionary<string, object> properties)
{ {

View File

@@ -44,7 +44,7 @@ public sealed class FileLoggerProvider : ILoggerProvider, ISupportExternalScope
/// 记录日志所有滚动文件名 /// 记录日志所有滚动文件名
/// </summary> /// </summary>
/// <remarks>只有 MaxRollingFiles 和 FileSizeLimitBytes 大于 0 有效</remarks> /// <remarks>只有 MaxRollingFiles 和 FileSizeLimitBytes 大于 0 有效</remarks>
internal readonly ConcurrentDictionary<string, FileInfo> _rollingFileNames = new(); internal readonly NonBlockingDictionary<string, FileInfo> _rollingFileNames = new();
/// <summary> /// <summary>
/// 文件日志写入器 /// 文件日志写入器

View File

@@ -36,7 +36,7 @@ using System.Text.Json;
using ThingsGateway; using ThingsGateway;
using ThingsGateway.DataValidation; using ThingsGateway.DataValidation;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.FriendlyException; using ThingsGateway.FriendlyException;
using ThingsGateway.JsonSerialization; using ThingsGateway.JsonSerialization;
using ThingsGateway.Logging; using ThingsGateway.Logging;
@@ -151,7 +151,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
return; return;
} }
await MonitorAsync(actionMethod, context.ActionArguments, context, next).ConfigureAwait(false); await MonitorAsync(actionMethod, context.ActionArguments, context, () => next()).ConfigureAwait(false);
} }
/// <summary> /// <summary>
@@ -183,7 +183,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
return; return;
} }
await MonitorAsync(actionMethod, context.HandlerArguments, context, next).ConfigureAwait(false); await MonitorAsync(actionMethod, context.HandlerArguments, context, () => next()).ConfigureAwait(false);
} }
/// <summary> /// <summary>
@@ -789,12 +789,12 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
return typeName; return typeName;
} }
private async Task MonitorAsync(MethodInfo actionMethod, IDictionary<string, object> parameterValues, FilterContext context, dynamic next) private async Task MonitorAsync<T>(MethodInfo actionMethod, IDictionary<string, object> parameterValues, FilterContext context, Func<Task<T>> next)
{ {
// 排除 WebSocket 请求处理 // 排除 WebSocket 请求处理
if (context.HttpContext.IsWebSocketRequest()) if (context.HttpContext.IsWebSocketRequest())
{ {
_ = await next(); _ = await next().ConfigureAwait(false);
return; return;
} }
@@ -805,14 +805,14 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
if (actionMethod.IsDefined(typeof(SuppressMonitorAttribute), true) if (actionMethod.IsDefined(typeof(SuppressMonitorAttribute), true)
|| actionMethod.DeclaringType.IsDefined(typeof(SuppressMonitorAttribute), true)) || actionMethod.DeclaringType.IsDefined(typeof(SuppressMonitorAttribute), true))
{ {
_ = await next(); _ = await next().ConfigureAwait(false);
return; return;
} }
// 判断是否自定义了日志筛选器,如果是则检查是否符合条件 // 判断是否自定义了日志筛选器,如果是则检查是否符合条件
if (LoggingMonitorSettings.InternalWriteFilter?.Invoke(context) == false) if (LoggingMonitorSettings.InternalWriteFilter?.Invoke(context) == false)
{ {
_ = await next(); _ = await next().ConfigureAwait(false);
return; return;
} }
@@ -825,7 +825,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
// 解决局部和全局触发器同时配置触发两次问题 // 解决局部和全局触发器同时配置触发两次问题
if (isDefinedScopedAttribute && Settings.FromGlobalFilter == true) if (isDefinedScopedAttribute && Settings.FromGlobalFilter == true)
{ {
_ = await next(); _ = await next().ConfigureAwait(false);
return; return;
} }
@@ -839,7 +839,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
&& !Settings.IncludeOfMethods.Contains(methodFullName, StringComparer.OrdinalIgnoreCase)) && !Settings.IncludeOfMethods.Contains(methodFullName, StringComparer.OrdinalIgnoreCase))
{ {
// 查找是否包含匹配,忽略大小写 // 查找是否包含匹配,忽略大小写
_ = await next(); _ = await next().ConfigureAwait(false);
return; return;
} }
@@ -847,7 +847,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
if (Settings.GlobalEnabled if (Settings.GlobalEnabled
&& Settings.ExcludeOfMethods.Contains(methodFullName, StringComparer.OrdinalIgnoreCase)) && Settings.ExcludeOfMethods.Contains(methodFullName, StringComparer.OrdinalIgnoreCase))
{ {
_ = await next(); _ = await next().ConfigureAwait(false);
return; return;
} }
} }
@@ -958,7 +958,8 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
// 计算接口执行时间 // 计算接口执行时间
var timeOperation = Stopwatch.StartNew(); var timeOperation = Stopwatch.StartNew();
var resultContext = await next();
var resultContext = await next().ConfigureAwait(false);
timeOperation.Stop(); timeOperation.Stop();
writer.WriteNumber("timeOperationElapsedMilliseconds", timeOperation.ElapsedMilliseconds); writer.WriteNumber("timeOperationElapsedMilliseconds", timeOperation.ElapsedMilliseconds);
@@ -1014,8 +1015,13 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
var environment = httpContext.RequestServices.GetRequiredService<IWebHostEnvironment>().EnvironmentName; var environment = httpContext.RequestServices.GetRequiredService<IWebHostEnvironment>().EnvironmentName;
writer.WriteString(nameof(environment), environment); writer.WriteString(nameof(environment), environment);
Exception exception = null;
// 获取异常对象情况 // 获取异常对象情况
Exception exception = resultContext.Exception; if (resultContext is PageHandlerExecutedContext pageHandlerExecutedContext)
exception = pageHandlerExecutedContext.Exception;
else if (resultContext is ActionExecutedContext actionExecutedContext)
exception = actionExecutedContext.Exception;
if (exception == null) if (exception == null)
{ {
// 解析存储的验证信息 // 解析存储的验证信息

View File

@@ -94,7 +94,7 @@ public sealed partial class StringLoggingPart
/// <summary> /// <summary>
/// 配置日志上下文 /// 配置日志上下文
/// </summary> /// </summary>
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param> /// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
/// <returns></returns> /// <returns></returns>
public StringLoggingPart ScopeContext(IDictionary<string, object> properties) public StringLoggingPart ScopeContext(IDictionary<string, object> properties)
{ {

View File

@@ -57,7 +57,7 @@ public static class Log
/// <summary> /// <summary>
/// 配置日志上下文 /// 配置日志上下文
/// </summary> /// </summary>
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param> /// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
/// <returns></returns> /// <returns></returns>
public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<string, object> properties) public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<string, object> properties)
{ {

View File

@@ -16,7 +16,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.Options; using ThingsGateway.Options;
namespace Microsoft.Extensions.Options; namespace Microsoft.Extensions.Options;

View File

@@ -21,7 +21,7 @@ internal sealed class JobCancellationToken : IJobCancellationToken
/// <summary> /// <summary>
/// 取消作业执行 Token 集合 /// 取消作业执行 Token 集合
/// </summary> /// </summary>
private readonly ConcurrentDictionary<string, CancellationTokenSource> _cancellationTokenSources; private readonly NonBlockingDictionary<string, CancellationTokenSource> _cancellationTokenSources;
/// <summary> /// <summary>
/// 作业调度器日志服务 /// 作业调度器日志服务

View File

@@ -167,7 +167,7 @@ public partial class JobDetail
/// <summary> /// <summary>
/// 带命名规则的数据库列名 /// 带命名规则的数据库列名
/// </summary> /// </summary>
private readonly ConcurrentDictionary<NamingConventions, string[]> _namingColumnNames = new(); private readonly NonBlockingDictionary<NamingConventions, string[]> _namingColumnNames = new();
/// <summary> /// <summary>
/// 获取数据库列名 /// 获取数据库列名

View File

@@ -65,7 +65,7 @@ internal sealed partial class SchedulerFactory : ISchedulerFactory
/// <summary> /// <summary>
/// 作业计划集合 /// 作业计划集合
/// </summary> /// </summary>
private readonly ConcurrentDictionary<string, Scheduler> _schedulers = new(); private readonly NonBlockingDictionary<string, Scheduler> _schedulers = new();
/// <summary> /// <summary>
/// 作业计划构建器集合 /// 作业计划构建器集合
@@ -204,7 +204,7 @@ internal sealed partial class SchedulerFactory : ISchedulerFactory
} }
else else
{ {
_logger.LogWarning("Schedule hosted service preload completed, and a total of <{Count}> schedulers are appended.", _schedulers.Count); _logger.LogInformation("Schedule hosted service preload completed, and a total of <{Count}> schedulers are appended.", _schedulers.Count);
} }
} }
} }

View File

@@ -369,11 +369,13 @@ internal sealed class ScheduleHostedService : BackgroundService
// 写入作业执行详细日志 // 写入作业执行详细日志
if (executionException == null) if (executionException == null)
{ {
jobLogger?.LogInformation("{jobExecutingContext}", jobExecutingContext); if (jobLogger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information) == true)
jobLogger?.LogInformation("{jobExecutingContext}", jobExecutingContext);
} }
else else
{ {
jobLogger?.LogError(executionException, "{jobExecutingContext}", jobExecutingContext); if (jobLogger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Error) == true)
jobLogger?.LogError(executionException, "{jobExecutingContext}", jobExecutingContext);
} }
// 记录作业触发器运行信息 // 记录作业触发器运行信息

View File

@@ -380,7 +380,7 @@ public partial class Trigger
/// <summary> /// <summary>
/// 带命名规则的数据库列名 /// 带命名规则的数据库列名
/// </summary> /// </summary>
private readonly ConcurrentDictionary<NamingConventions, string[]> _namingColumnNames = new(); private readonly NonBlockingDictionary<NamingConventions, string[]> _namingColumnNames = new();
/// <summary> /// <summary>
/// 获取数据库列名 /// 获取数据库列名

View File

@@ -31,7 +31,7 @@ using System.Xml.Linq;
using System.Xml.XPath; using System.Xml.XPath;
using ThingsGateway.DynamicApiController; using ThingsGateway.DynamicApiController;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.Reflection; using ThingsGateway.Reflection;
namespace ThingsGateway.SpecificationDocument; namespace ThingsGateway.SpecificationDocument;
@@ -83,11 +83,11 @@ public static class SpecificationDocumentBuilder
// 初始化常量 // 初始化常量
_groupOrderRegex = new Regex(@"@(?<order>[0-9]+$)"); _groupOrderRegex = new Regex(@"@(?<order>[0-9]+$)");
GetActionGroupsCached = new ConcurrentDictionary<MethodInfo, IEnumerable<GroupExtraInfo>>(); GetActionGroupsCached = new NonBlockingDictionary<MethodInfo, IEnumerable<GroupExtraInfo>>();
GetControllerGroupsCached = new ConcurrentDictionary<Type, IEnumerable<GroupExtraInfo>>(); GetControllerGroupsCached = new NonBlockingDictionary<Type, IEnumerable<GroupExtraInfo>>();
GetGroupOpenApiInfoCached = new ConcurrentDictionary<string, SpecificationOpenApiInfo>(); GetGroupOpenApiInfoCached = new NonBlockingDictionary<string, SpecificationOpenApiInfo>();
GetControllerTagCached = new ConcurrentDictionary<ControllerActionDescriptor, string>(); GetControllerTagCached = new NonBlockingDictionary<ControllerActionDescriptor, string>();
GetActionTagCached = new ConcurrentDictionary<ApiDescription, string>(); GetActionTagCached = new NonBlockingDictionary<ApiDescription, string>();
// 默认分组,支持多个逗号分割 // 默认分组,支持多个逗号分割
DocumentGroupExtras = new List<GroupExtraInfo> { ResolveGroupExtraInfo(_specificationDocumentSettings.DefaultGroupName) }; DocumentGroupExtras = new List<GroupExtraInfo> { ResolveGroupExtraInfo(_specificationDocumentSettings.DefaultGroupName) };
@@ -143,7 +143,7 @@ public static class SpecificationDocumentBuilder
/// <summary> /// <summary>
/// 获取分组信息缓存集合 /// 获取分组信息缓存集合
/// </summary> /// </summary>
private static readonly ConcurrentDictionary<string, SpecificationOpenApiInfo> GetGroupOpenApiInfoCached; private static readonly NonBlockingDictionary<string, SpecificationOpenApiInfo> GetGroupOpenApiInfoCached;
/// <summary> /// <summary>
/// 获取分组配置信息 /// 获取分组配置信息
@@ -738,7 +738,7 @@ public static class SpecificationDocumentBuilder
/// <summary> /// <summary>
/// 获取控制器组缓存集合 /// 获取控制器组缓存集合
/// </summary> /// </summary>
private static readonly ConcurrentDictionary<Type, IEnumerable<GroupExtraInfo>> GetControllerGroupsCached; private static readonly NonBlockingDictionary<Type, IEnumerable<GroupExtraInfo>> GetControllerGroupsCached;
/// <summary> /// <summary>
/// 获取控制器分组列表 /// 获取控制器分组列表
@@ -773,7 +773,7 @@ public static class SpecificationDocumentBuilder
/// <summary> /// <summary>
/// <see cref="GetActionGroups(MethodInfo)"/> 缓存集合 /// <see cref="GetActionGroups(MethodInfo)"/> 缓存集合
/// </summary> /// </summary>
private static readonly ConcurrentDictionary<MethodInfo, IEnumerable<GroupExtraInfo>> GetActionGroupsCached; private static readonly NonBlockingDictionary<MethodInfo, IEnumerable<GroupExtraInfo>> GetActionGroupsCached;
/// <summary> /// <summary>
/// 获取动作方法分组列表 /// 获取动作方法分组列表
@@ -808,7 +808,7 @@ public static class SpecificationDocumentBuilder
/// <summary> /// <summary>
/// <see cref="GetActionTag(ApiDescription)"/> 缓存集合 /// <see cref="GetActionTag(ApiDescription)"/> 缓存集合
/// </summary> /// </summary>
private static readonly ConcurrentDictionary<ControllerActionDescriptor, string> GetControllerTagCached; private static readonly NonBlockingDictionary<ControllerActionDescriptor, string> GetControllerTagCached;
/// <summary> /// <summary>
/// 获取控制器标签 /// 获取控制器标签
@@ -835,7 +835,7 @@ public static class SpecificationDocumentBuilder
/// <summary> /// <summary>
/// <see cref="GetActionTag(ApiDescription)"/> 缓存集合 /// <see cref="GetActionTag(ApiDescription)"/> 缓存集合
/// </summary> /// </summary>
private static readonly ConcurrentDictionary<ApiDescription, string> GetActionTagCached; private static readonly NonBlockingDictionary<ApiDescription, string> GetActionTagCached;
/// <summary> /// <summary>
/// 获取动作方法标签 /// 获取动作方法标签

View File

@@ -20,7 +20,7 @@ using System.Reflection;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace ThingsGateway.SpecificationDocument; namespace ThingsGateway.SpecificationDocument;

View File

@@ -11,7 +11,7 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace ThingsGateway.Templates.Extensions; namespace ThingsGateway.Templates.Extensions;

View File

@@ -4,7 +4,7 @@
<Import Project="..\..\PackNuget.props" /> <Import Project="..\..\PackNuget.props" />
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks> <TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
@@ -29,8 +29,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.4" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.6" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="$(NET9Version)" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="$(NET10Version)" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
@@ -39,10 +39,10 @@
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.2" /> <PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net10.0' ">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="$(NET9Version)" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="$(NET10Version)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(NET9Version)" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(NET10Version)" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(NET9Version)" /> <PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(NET10Version)" />
</ItemGroup> </ItemGroup>

View File

@@ -13,7 +13,7 @@ using Microsoft.AspNetCore.Http;
using System.Reflection; using System.Reflection;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.UnifyResult; using ThingsGateway.UnifyResult;
namespace Microsoft.AspNetCore.Mvc; namespace Microsoft.AspNetCore.Mvc;

View File

@@ -22,7 +22,7 @@ using Microsoft.Extensions.Options;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Reflection; using System.Reflection;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.FriendlyException; using ThingsGateway.FriendlyException;
namespace ThingsGateway.UnifyResult; namespace ThingsGateway.UnifyResult;
@@ -51,12 +51,12 @@ public static class UnifyContext
/// <summary> /// <summary>
/// 规范化结果提供器 /// 规范化结果提供器
/// </summary> /// </summary>
internal static ConcurrentDictionary<string, UnifyMetadata> UnifyProviders = new(); internal static NonBlockingDictionary<string, UnifyMetadata> UnifyProviders = new();
/// <summary> /// <summary>
/// 规范化序列化配置 /// 规范化序列化配置
/// </summary> /// </summary>
internal static ConcurrentDictionary<string, object> UnifySerializerSettings = new(); internal static NonBlockingDictionary<string, object> UnifySerializerSettings = new();
/// <summary> /// <summary>
/// 获取异常元数据 /// 获取异常元数据

View File

@@ -12,7 +12,7 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace ThingsGateway.Converters.Json; namespace ThingsGateway.Converters.Json;

View File

@@ -11,7 +11,7 @@
using System.Reflection; using System.Reflection;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="Assembly" /> 拓展类 /// <see cref="Assembly" /> 拓展类

View File

@@ -11,10 +11,10 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="ConcurrentDictionary{TKey, TValue}" /> 拓展类 /// <see cref="NonBlockingDictionary{TKey, TValue}" /> 拓展类
/// </summary> /// </summary>
internal static class ConcurrentDictionaryExtensions internal static class ConcurrentDictionaryExtensions
{ {
@@ -24,7 +24,7 @@ internal static class ConcurrentDictionaryExtensions
/// <typeparam name="TKey">字典键类型</typeparam> /// <typeparam name="TKey">字典键类型</typeparam>
/// <typeparam name="TValue">字典值类型</typeparam> /// <typeparam name="TValue">字典值类型</typeparam>
/// <param name="dictionary"> /// <param name="dictionary">
/// <see cref="ConcurrentDictionary{TKey, TValue}" /> /// <see cref="NonBlockingDictionary{TKey, TValue}" />
/// </param> /// </param>
/// <param name="key"> /// <param name="key">
/// <typeparamref name="TKey" /> /// <typeparamref name="TKey" />
@@ -36,7 +36,7 @@ internal static class ConcurrentDictionaryExtensions
/// <returns> /// <returns>
/// <see cref="bool" /> /// <see cref="bool" />
/// </returns> /// </returns>
internal static bool TryUpdate<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> dictionary internal static bool TryUpdate<TKey, TValue>(this NonBlockingDictionary<TKey, TValue> dictionary
, TKey key , TKey key
, Func<TValue, TValue> updateFactory , Func<TValue, TValue> updateFactory
, out TValue? value) , out TValue? value)

View File

@@ -15,7 +15,7 @@ using Microsoft.Extensions.Hosting;
using System.Reflection; using System.Reflection;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// 核心模块 <see cref="IServiceCollection" /> 拓展类 /// 核心模块 <see cref="IServiceCollection" /> 拓展类

View File

@@ -11,7 +11,7 @@
using System.Data; using System.Data;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="DataTable" /> 和 <see cref="DataSet" /> 拓展类 /// <see cref="DataTable" /> 和 <see cref="DataSet" /> 拓展类

View File

@@ -9,7 +9,7 @@
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// 委托拓展类 /// 委托拓展类

View File

@@ -12,7 +12,7 @@
using System.ComponentModel; using System.ComponentModel;
using System.Reflection; using System.Reflection;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// 枚举拓展类 /// 枚举拓展类

View File

@@ -9,7 +9,7 @@
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="EventHandler{TEventArgs}" /> 拓展类 /// <see cref="EventHandler{TEventArgs}" /> 拓展类

View File

@@ -11,7 +11,7 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="ICollection{T}" /> 拓展类 /// <see cref="ICollection{T}" /> 拓展类

View File

@@ -11,7 +11,7 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="IDictionary{TKey, TValue}" /> 拓展类 /// <see cref="IDictionary{TKey, TValue}" /> 拓展类
@@ -241,7 +241,7 @@ internal static class IDictionaryExtensions
/// </summary> /// </summary>
/// <remarks>其中键是由值通过给定的选择器函数生成的。</remarks> /// <remarks>其中键是由值通过给定的选择器函数生成的。</remarks>
/// <param name="dictionary"> /// <param name="dictionary">
/// <see cref="ConcurrentDictionary{TKey, TValue}" /> /// <see cref="NonBlockingDictionary{TKey, TValue}" />
/// </param> /// </param>
/// <param name="values"> /// <param name="values">
/// <see cref="IEnumerable{T}" /> /// <see cref="IEnumerable{T}" />
@@ -249,7 +249,7 @@ internal static class IDictionaryExtensions
/// <param name="keySelector">键选择器</param> /// <param name="keySelector">键选择器</param>
/// <typeparam name="TKey">字典键类型</typeparam> /// <typeparam name="TKey">字典键类型</typeparam>
/// <typeparam name="TValue">字典值类型</typeparam> /// <typeparam name="TValue">字典值类型</typeparam>
internal static void TryAdd<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> dictionary, internal static void TryAdd<TKey, TValue>(this NonBlockingDictionary<TKey, TValue> dictionary,
IEnumerable<TValue>? values, Func<TValue, TKey> keySelector) IEnumerable<TValue>? values, Func<TValue, TKey> keySelector)
where TKey : notnull where TKey : notnull
{ {

View File

@@ -9,7 +9,7 @@
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="IEnumerable" /> 拓展类 /// <see cref="IEnumerable" /> 拓展类

View File

@@ -18,7 +18,7 @@ using System.Text.RegularExpressions;
using System.Xml; using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// System.Text.Json 拓展类 /// System.Text.Json 拓展类

View File

@@ -12,7 +12,7 @@
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="Expression" /> 拓展类 /// <see cref="Expression" /> 拓展类

View File

@@ -12,7 +12,7 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Reflection; using System.Reflection;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="MethodInfo" /> 拓展类 /// <see cref="MethodInfo" /> 拓展类

View File

@@ -9,7 +9,7 @@
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// 数值类型拓展类 /// 数值类型拓展类

View File

@@ -17,7 +17,7 @@ using System.Reflection;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="string" /> 拓展类 /// <see cref="string" /> 拓展类

View File

@@ -15,7 +15,7 @@ using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="Type" /> 拓展类 /// <see cref="Type" /> 拓展类

View File

@@ -13,7 +13,7 @@ using System.Buffers;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="Utf8JsonReader" /> 拓展类 /// <see cref="Utf8JsonReader" /> 拓展类

View File

@@ -16,7 +16,7 @@ using System.Text.Json;
using ThingsGateway.Utilities; using ThingsGateway.Utilities;
namespace ThingsGateway.Extensions; namespace ThingsGateway.Extension;
/// <summary> /// <summary>
/// <see cref="object" /> 拓展类 /// <see cref="object" /> 拓展类

View File

@@ -21,20 +21,20 @@ internal sealed class CoreOptions
/// <summary> /// <summary>
/// 已注册的组件元数据集合 /// 已注册的组件元数据集合
/// </summary> /// </summary>
internal readonly ConcurrentDictionary<string, ComponentMetadata> _metadataOfRegistered; internal readonly NonBlockingDictionary<string, ComponentMetadata> _metadataOfRegistered;
/// <summary> /// <summary>
/// 子选项集合 /// 子选项集合
/// </summary> /// </summary>
internal readonly ConcurrentDictionary<Type, object> _optionsInstances; internal readonly NonBlockingDictionary<Type, object> _optionsInstances;
/// <summary> /// <summary>
/// <inheritdoc cref="CoreOptions" /> /// <inheritdoc cref="CoreOptions" />
/// </summary> /// </summary>
internal CoreOptions() internal CoreOptions()
{ {
_optionsInstances = new ConcurrentDictionary<Type, object>(); _optionsInstances = new NonBlockingDictionary<Type, object>();
_metadataOfRegistered = new ConcurrentDictionary<string, ComponentMetadata>(StringComparer.OrdinalIgnoreCase); _metadataOfRegistered = new NonBlockingDictionary<string, ComponentMetadata>(StringComparer.OrdinalIgnoreCase);
EntryComponentTypes = []; EntryComponentTypes = [];
} }

View File

@@ -13,7 +13,7 @@ using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Reflection; using System.Reflection;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace ThingsGateway.Reflection; namespace ThingsGateway.Reflection;
@@ -32,7 +32,7 @@ public sealed class ObjectPropertyGetter<T> where T : class
/// <summary> /// <summary>
/// 对象类型实例属性值访问器集合 /// 对象类型实例属性值访问器集合
/// </summary> /// </summary>
internal readonly ConcurrentDictionary<string, Func<object, object?>> _propertyGetters = new(); internal readonly NonBlockingDictionary<string, Func<object, object?>> _propertyGetters = new();
/// <summary> /// <summary>
/// <inheritdoc cref="ObjectPropertyGetter{T}" /> /// <inheritdoc cref="ObjectPropertyGetter{T}" />

View File

@@ -13,7 +13,7 @@ using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Reflection; using System.Reflection;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace ThingsGateway.Reflection; namespace ThingsGateway.Reflection;
@@ -32,7 +32,7 @@ public sealed class ObjectPropertySetter<T> where T : class
/// <summary> /// <summary>
/// 对象类型实例属性值设置器集合 /// 对象类型实例属性值设置器集合
/// </summary> /// </summary>
internal readonly ConcurrentDictionary<string, Action<object, object?>> _propertySetters = new(); internal readonly NonBlockingDictionary<string, Action<object, object?>> _propertySetters = new();
/// <summary> /// <summary>
/// <inheritdoc cref="ObjectPropertySetter{T}" /> /// <inheritdoc cref="ObjectPropertySetter{T}" />

View File

@@ -11,7 +11,7 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace ThingsGateway.Utilities; namespace ThingsGateway.Utilities;

View File

@@ -11,7 +11,7 @@
using System.Text; using System.Text;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace ThingsGateway.Utilities; namespace ThingsGateway.Utilities;

View File

@@ -18,7 +18,7 @@ using Microsoft.Net.Http.Headers;
using System.Net.Mime; using System.Net.Mime;
using ThingsGateway.AspNetCore.Extensions; using ThingsGateway.AspNetCore.Extensions;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue; using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue;

View File

@@ -12,7 +12,7 @@
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Threading.Channels; using System.Threading.Channels;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.Utilities; using ThingsGateway.Utilities;
namespace ThingsGateway.HttpRemote; namespace ThingsGateway.HttpRemote;

View File

@@ -16,7 +16,7 @@ using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading.Channels; using System.Threading.Channels;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.Utilities; using ThingsGateway.Utilities;
namespace ThingsGateway.HttpRemote; namespace ThingsGateway.HttpRemote;

View File

@@ -16,7 +16,7 @@ using Microsoft.Extensions.Logging;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
namespace ThingsGateway.HttpRemote; namespace ThingsGateway.HttpRemote;

View File

@@ -17,7 +17,7 @@ using System.Net.Mime;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using ThingsGateway.Utilities; using ThingsGateway.Utilities;
using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue; using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue;

View File

@@ -16,7 +16,7 @@ using System.Globalization;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Net.Mime; using System.Net.Mime;
using ThingsGateway.Extensions; using ThingsGateway.Extension;
using CacheControlHeaderValue = System.Net.Http.Headers.CacheControlHeaderValue; using CacheControlHeaderValue = System.Net.Http.Headers.CacheControlHeaderValue;
using StringWithQualityHeaderValue = System.Net.Http.Headers.StringWithQualityHeaderValue; using StringWithQualityHeaderValue = System.Net.Http.Headers.StringWithQualityHeaderValue;

Some files were not shown because too many files have changed in this diff Show More