Compare commits
	
		
			19 Commits
		
	
	
		
			10.11.90.0
			...
			21215d0379
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					21215d0379 | ||
| 
						 | 
					7448183791 | ||
| 
						 | 
					35edd7dc43 | ||
| 
						 | 
					bd178831e3 | ||
| 
						 | 
					fe9ec6ad10 | ||
| 
						 | 
					6f9ec2e24b | ||
| 
						 | 
					c0337e2b19 | ||
| 
						 | 
					8a95f48f5a | ||
| 
						 | 
					14f3c31265 | ||
| 
						 | 
					1bad65378f | ||
| 
						 | 
					db3affc67e | ||
| 
						 | 
					5ee8b50a92 | ||
| 
						 | 
					301beda2a2 | ||
| 
						 | 
					628b51a353 | ||
| 
						 | 
					f03445bc83 | ||
| 
						 | 
					55a2ff5487 | ||
| 
						 | 
					0fef7dcf3b | ||
| 
						 | 
					19d9702606 | ||
| 
						 | 
					a8a9774932 | 
@@ -251,11 +251,13 @@ public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        {
 | 
			
		||||
            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()}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ namespace ThingsGateway.Admin.Application;
 | 
			
		||||
/// <typeparam name="TEntry"></typeparam>
 | 
			
		||||
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()
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
	
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<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 Condition=" '$(TargetFramework)' == 'net8.0' ">
 | 
			
		||||
		<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,8 @@
 | 
			
		||||
 | 
			
		||||
<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"
 | 
			
		||||
           DataService="DataService" CreateItemCallback="CreateItemCallback!"
 | 
			
		||||
    <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!" RenderMode=RenderMode OnColumnCreating=OnColumnCreating
 | 
			
		||||
           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
 | 
			
		||||
           ShowDefaultButtons="ShowDefaultButtons" ShowAdvancedSearch="ShowAdvancedSearch" ShowResetButton=ShowResetButton
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
           ShowSkeleton="true" ShowLoading="ShowLoading" ShowSearch="ShowSearch" SearchModel=@SearchModel ShowLineNo
 | 
			
		||||
           SearchMode=SearchMode ShowExportPdfButton=ShowExportPdfButton ExportButtonText=@ExportButtonText
 | 
			
		||||
           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
 | 
			
		||||
           IsAutoRefresh=IsAutoRefresh AutoRefreshInterval=AutoRefreshInterval
 | 
			
		||||
           AllowDragColumn=@AllowDragColumn Height=@Height ShowRefresh=ShowRefresh
 | 
			
		||||
@@ -41,6 +41,7 @@
 | 
			
		||||
           DoubleClickToEdit="DoubleClickToEdit"
 | 
			
		||||
           OnDoubleClickCellCallback="OnDoubleClickCellCallback"
 | 
			
		||||
           OnDoubleClickRowCallback="OnDoubleClickRowCallback"
 | 
			
		||||
           RowContentTemplate="RowContentTemplate"
 | 
			
		||||
           OnClickRowCallback="OnClickRowCallback">
 | 
			
		||||
    </Table>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,23 @@ namespace ThingsGateway.Admin.Razor;
 | 
			
		||||
[CascadingTypeParameter(nameof(TItem))]
 | 
			
		||||
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"/>
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    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"/>
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    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"/>
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public Func<TItem, Task>? OnClickRowCallback { get; set; }
 | 
			
		||||
@@ -146,6 +167,9 @@ public partial class AdminTable<TItem> where TItem : class, new()
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public IDataService<TItem> DataService { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public string? Id { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Table{TItem}.CreateItemCallback"/>
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public Func<TItem> CreateItemCallback { get; set; }
 | 
			
		||||
 
 | 
			
		||||
@@ -72,7 +72,7 @@ public partial class HardwareInfoPage : IDisposable
 | 
			
		||||
            ChartDataSource.Options.Title = Localizer[nameof(HistoryHardwareInfo)];
 | 
			
		||||
            ChartDataSource.Options.X.Title = Localizer["DateTime"];
 | 
			
		||||
            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()
 | 
			
		||||
            {
 | 
			
		||||
                Tension = 0.4f,
 | 
			
		||||
@@ -116,7 +116,7 @@ public partial class HardwareInfoPage : IDisposable
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            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[1].Data = hisHardwareInfos.Select(a => (object)a.MemoryUsage);
 | 
			
		||||
            ChartDataSource.Data[2].Data = hisHardwareInfos.Select(a => (object)a.DriveUsage);
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
 | 
			
		||||
		<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.1" />
 | 
			
		||||
		<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.4" />
 | 
			
		||||
		<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" />
 | 
			
		||||
		<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" />
 | 
			
		||||
		<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.3" />
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
@@ -477,7 +477,7 @@ public class ConcurrentList<T> : IList<T>, IReadOnlyList<T>
 | 
			
		||||
    {
 | 
			
		||||
        lock (((ICollection)m_list).SyncRoot)
 | 
			
		||||
        {
 | 
			
		||||
            return m_list.IndexOf(item);
 | 
			
		||||
            return m_list.LastIndexOf(item);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -82,7 +82,7 @@ public static class ObjectExtensions
 | 
			
		||||
    /// <typeparam name="T"></typeparam>
 | 
			
		||||
    /// <param name="dic">字典</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)
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -66,7 +66,8 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
 | 
			
		||||
                }
 | 
			
		||||
                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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -176,7 +177,8 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
 | 
			
		||||
        localizationMissingItemHandler.HandleMissingItem(name, typeName, CultureInfo.CurrentUICulture.Name);
 | 
			
		||||
        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}");
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.7" />
 | 
			
		||||
		<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
 | 
			
		||||
		<PackageReference Include="BootstrapBlazor" Version="9.11.1" />
 | 
			
		||||
		<PackageReference Include="BootstrapBlazor" Version="9.11.2" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
 
 | 
			
		||||
@@ -205,7 +205,7 @@ public static class ObjectExtensions
 | 
			
		||||
    /// <typeparam name="T"></typeparam>
 | 
			
		||||
    /// <param name="dic">字典</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)
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,7 @@ public static class AspNetCoreBuilderServiceCollectionExtensions
 | 
			
		||||
    /// <param name="mvcBuilder"></param>
 | 
			
		||||
    /// <param name="configure"></param>
 | 
			
		||||
    /// <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);
 | 
			
		||||
 | 
			
		||||
@@ -107,13 +107,13 @@ public static class AspNetCoreBuilderServiceCollectionExtensions
 | 
			
		||||
    /// <param name="services"></param>
 | 
			
		||||
    /// <param name="configure"></param>
 | 
			
		||||
    /// <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 环境跳过注册
 | 
			
		||||
        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(DateTimeOffset), typeof(DateTimeOffsetModelConvertBinder));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,13 +27,13 @@ public class FromConvertBinder : IModelBinder
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 定义模型绑定转换器集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private readonly ConcurrentDictionary<Type, Type> _modelBinderConverts;
 | 
			
		||||
    private readonly NonBlockingDictionary<Type, Type> _modelBinderConverts;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 构造函数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="modelBinderConverts">定义模型绑定转换器集合</param>
 | 
			
		||||
    public FromConvertBinder(ConcurrentDictionary<Type, Type> modelBinderConverts)
 | 
			
		||||
    public FromConvertBinder(NonBlockingDictionary<Type, Type> modelBinderConverts)
 | 
			
		||||
    {
 | 
			
		||||
        _modelBinderConverts = modelBinderConverts;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -28,13 +28,13 @@ public class FromConvertBinderProvider : IModelBinderProvider
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 定义模型绑定转换器集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private readonly ConcurrentDictionary<Type, Type> _modelBinderConverts;
 | 
			
		||||
    private readonly NonBlockingDictionary<Type, Type> _modelBinderConverts;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 构造函数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="modelBinderConverts">定义模型绑定转换器集合</param>
 | 
			
		||||
    public FromConvertBinderProvider(ConcurrentDictionary<Type, Type> modelBinderConverts)
 | 
			
		||||
    public FromConvertBinderProvider(NonBlockingDictionary<Type, Type> modelBinderConverts)
 | 
			
		||||
    {
 | 
			
		||||
        _modelBinderConverts = modelBinderConverts;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ public static class DataValidator
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 验证类型正则表达式
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static readonly ConcurrentDictionary<string, ValidationItemMetadataAttribute> ValidationItemMetadatas;
 | 
			
		||||
    private static readonly NonBlockingDictionary<string, ValidationItemMetadataAttribute> ValidationItemMetadatas;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 构造函数
 | 
			
		||||
@@ -57,7 +57,7 @@ public static class DataValidator
 | 
			
		||||
        ValidationItemMetadatas = GetValidationValidationItemMetadatas();
 | 
			
		||||
 | 
			
		||||
        // 缓存所有正则表达式
 | 
			
		||||
        GetValidationTypeValidationItemMetadataCached = new ConcurrentDictionary<object, (string, ValidationItemMetadataAttribute)>();
 | 
			
		||||
        GetValidationTypeValidationItemMetadataCached = new NonBlockingDictionary<object, (string, ValidationItemMetadataAttribute)>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@@ -203,7 +203,7 @@ public static class DataValidator
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取验证类型验证Item集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static readonly ConcurrentDictionary<object, (string, ValidationItemMetadataAttribute)> GetValidationTypeValidationItemMetadataCached;
 | 
			
		||||
    private static readonly NonBlockingDictionary<object, (string, ValidationItemMetadataAttribute)> GetValidationTypeValidationItemMetadataCached;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取验证类型正则表达式(需要缓存)
 | 
			
		||||
@@ -267,9 +267,9 @@ public static class DataValidator
 | 
			
		||||
    /// 获取验证类型所有有效的正则表达式
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <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] 消息定义
 | 
			
		||||
        var customErrorMessages = ValidationMessageTypes.SelectMany(u => u.GetFields()
 | 
			
		||||
 
 | 
			
		||||
@@ -353,7 +353,7 @@ public static class DependencyInjectionServiceCollectionExtensions
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 类型名称集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static readonly ConcurrentDictionary<string, Type> TypeNamedCollection;
 | 
			
		||||
    private static readonly NonBlockingDictionary<string, Type> TypeNamedCollection;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 创建代理方法
 | 
			
		||||
@@ -374,7 +374,7 @@ public static class DependencyInjectionServiceCollectionExtensions
 | 
			
		||||
        GlobalServiceProxyType = App.EffectiveTypes
 | 
			
		||||
            .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));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,21 +28,21 @@ internal static class Penetrates
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 请求动词映射字典
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static ConcurrentDictionary<string, string> VerbToHttpMethods { get; private set; }
 | 
			
		||||
    internal static NonBlockingDictionary<string, string> VerbToHttpMethods { get; private set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 控制器排序集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static ConcurrentDictionary<string, (string, int, Type)> ControllerOrderCollection { get; set; }
 | 
			
		||||
    internal static NonBlockingDictionary<string, (string, int, Type)> ControllerOrderCollection { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 构造函数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    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",
 | 
			
		||||
            ["add"] = "POST",
 | 
			
		||||
@@ -67,13 +67,13 @@ internal static class Penetrates
 | 
			
		||||
            ["patch"] = "PATCH"
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        //IsApiControllerCached = new ConcurrentDictionary<Type, bool>();
 | 
			
		||||
        //IsApiControllerCached = new NonBlockingDictionary<Type, bool>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ///// <summary>
 | 
			
		||||
    ///// <see cref="IsApiController(Type)"/> 缓存集合
 | 
			
		||||
    ///// </summary>
 | 
			
		||||
    //private static readonly ConcurrentDictionary<Type, bool> IsApiControllerCached;
 | 
			
		||||
    //private static readonly NonBlockingDictionary<Type, bool> IsApiControllerCached;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 是否是Api控制器
 | 
			
		||||
 
 | 
			
		||||
@@ -61,7 +61,7 @@ internal sealed class EventBusHostedService : BackgroundService
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 事件处理程序集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private readonly ConcurrentDictionary<EventHandlerWrapper, EventHandlerWrapper> _eventHandlers = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<EventHandlerWrapper, EventHandlerWrapper> _eventHandlers = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 构造函数
 | 
			
		||||
@@ -295,7 +295,8 @@ internal sealed class EventBusHostedService : BackgroundService
 | 
			
		||||
                        , 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);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ public static class Oops
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 方法错误异常特性
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static readonly ConcurrentDictionary<MethodBase, MethodIfException> _errorMethods;
 | 
			
		||||
    private static readonly NonBlockingDictionary<MethodBase, MethodIfException> _errorMethods;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 错误代码类型
 | 
			
		||||
@@ -41,7 +41,7 @@ public static class Oops
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 错误消息字典
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static readonly ConcurrentDictionary<string, string> _errorCodeMessages;
 | 
			
		||||
    private static readonly NonBlockingDictionary<string, string> _errorCodeMessages;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 友好异常设置
 | 
			
		||||
@@ -53,7 +53,7 @@ public static class Oops
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    static Oops()
 | 
			
		||||
    {
 | 
			
		||||
        _errorMethods = new ConcurrentDictionary<MethodBase, MethodIfException>();
 | 
			
		||||
        _errorMethods = new NonBlockingDictionary<MethodBase, MethodIfException>();
 | 
			
		||||
        _friendlyExceptionSettings = App.GetConfig<FriendlyExceptionSettingsOptions>("FriendlyExceptionSettings", true);
 | 
			
		||||
        _errorCodeTypes = GetErrorCodeTypes();
 | 
			
		||||
        _errorCodeMessages = GetErrorCodeMessages();
 | 
			
		||||
@@ -258,9 +258,9 @@ public static class Oops
 | 
			
		||||
    /// 获取所有错误消息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <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] 元数据定义
 | 
			
		||||
        var errorCodeMessages = _errorCodeTypes.SelectMany(u => u.GetFields().Where(u => u.IsDefined(typeof(ErrorCodeItemMetadataAttribute))))
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ public static class ILoggerExtensions
 | 
			
		||||
    /// 设置日志上下文
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="logger"></param>
 | 
			
		||||
    /// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
 | 
			
		||||
    /// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static IDisposable ScopeContext(this ILogger logger, IDictionary<string, object> properties)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -82,7 +82,7 @@ public static class StringLoggingExtensions
 | 
			
		||||
    /// 配置日志上下文
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="message"></param>
 | 
			
		||||
    /// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
 | 
			
		||||
    /// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static StringLoggingPart ScopeContext(this string message, IDictionary<string, object> properties)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ public sealed class FileLoggerProvider : ILoggerProvider, ISupportExternalScope
 | 
			
		||||
    /// 记录日志所有滚动文件名
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>只有 MaxRollingFiles 和 FileSizeLimitBytes 大于 0 有效</remarks>
 | 
			
		||||
    internal readonly ConcurrentDictionary<string, FileInfo> _rollingFileNames = new();
 | 
			
		||||
    internal readonly NonBlockingDictionary<string, FileInfo> _rollingFileNames = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 文件日志写入器
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,7 @@ public sealed partial class StringLoggingPart
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 配置日志上下文
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
 | 
			
		||||
    /// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public StringLoggingPart ScopeContext(IDictionary<string, object> properties)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ public static class Log
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 配置日志上下文
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
 | 
			
		||||
    /// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<string, object> properties)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ internal sealed class JobCancellationToken : IJobCancellationToken
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 取消作业执行 Token 集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private readonly ConcurrentDictionary<string, CancellationTokenSource> _cancellationTokenSources;
 | 
			
		||||
    private readonly NonBlockingDictionary<string, CancellationTokenSource> _cancellationTokenSources;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 作业调度器日志服务
 | 
			
		||||
 
 | 
			
		||||
@@ -167,7 +167,7 @@ public partial class JobDetail
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 带命名规则的数据库列名
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private readonly ConcurrentDictionary<NamingConventions, string[]> _namingColumnNames = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<NamingConventions, string[]> _namingColumnNames = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取数据库列名
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ internal sealed partial class SchedulerFactory : ISchedulerFactory
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 作业计划集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private readonly ConcurrentDictionary<string, Scheduler> _schedulers = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<string, Scheduler> _schedulers = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 作业计划构建器集合
 | 
			
		||||
 
 | 
			
		||||
@@ -369,11 +369,13 @@ internal sealed class ScheduleHostedService : BackgroundService
 | 
			
		||||
                            // 写入作业执行详细日志
 | 
			
		||||
                            if (executionException == null)
 | 
			
		||||
                            {
 | 
			
		||||
                                jobLogger?.LogInformation("{jobExecutingContext}", jobExecutingContext);
 | 
			
		||||
                                if (jobLogger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information) == true)
 | 
			
		||||
                                    jobLogger?.LogInformation("{jobExecutingContext}", jobExecutingContext);
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                jobLogger?.LogError(executionException, "{jobExecutingContext}", jobExecutingContext);
 | 
			
		||||
                                if (jobLogger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Error) == true)
 | 
			
		||||
                                    jobLogger?.LogError(executionException, "{jobExecutingContext}", jobExecutingContext);
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            // 记录作业触发器运行信息
 | 
			
		||||
 
 | 
			
		||||
@@ -380,7 +380,7 @@ public partial class Trigger
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 带命名规则的数据库列名
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private readonly ConcurrentDictionary<NamingConventions, string[]> _namingColumnNames = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<NamingConventions, string[]> _namingColumnNames = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取数据库列名
 | 
			
		||||
 
 | 
			
		||||
@@ -83,11 +83,11 @@ public static class SpecificationDocumentBuilder
 | 
			
		||||
 | 
			
		||||
        // 初始化常量
 | 
			
		||||
        _groupOrderRegex = new Regex(@"@(?<order>[0-9]+$)");
 | 
			
		||||
        GetActionGroupsCached = new ConcurrentDictionary<MethodInfo, IEnumerable<GroupExtraInfo>>();
 | 
			
		||||
        GetControllerGroupsCached = new ConcurrentDictionary<Type, IEnumerable<GroupExtraInfo>>();
 | 
			
		||||
        GetGroupOpenApiInfoCached = new ConcurrentDictionary<string, SpecificationOpenApiInfo>();
 | 
			
		||||
        GetControllerTagCached = new ConcurrentDictionary<ControllerActionDescriptor, string>();
 | 
			
		||||
        GetActionTagCached = new ConcurrentDictionary<ApiDescription, string>();
 | 
			
		||||
        GetActionGroupsCached = new NonBlockingDictionary<MethodInfo, IEnumerable<GroupExtraInfo>>();
 | 
			
		||||
        GetControllerGroupsCached = new NonBlockingDictionary<Type, IEnumerable<GroupExtraInfo>>();
 | 
			
		||||
        GetGroupOpenApiInfoCached = new NonBlockingDictionary<string, SpecificationOpenApiInfo>();
 | 
			
		||||
        GetControllerTagCached = new NonBlockingDictionary<ControllerActionDescriptor, string>();
 | 
			
		||||
        GetActionTagCached = new NonBlockingDictionary<ApiDescription, string>();
 | 
			
		||||
 | 
			
		||||
        // 默认分组,支持多个逗号分割
 | 
			
		||||
        DocumentGroupExtras = new List<GroupExtraInfo> { ResolveGroupExtraInfo(_specificationDocumentSettings.DefaultGroupName) };
 | 
			
		||||
@@ -143,7 +143,7 @@ public static class SpecificationDocumentBuilder
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取分组信息缓存集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static readonly ConcurrentDictionary<string, SpecificationOpenApiInfo> GetGroupOpenApiInfoCached;
 | 
			
		||||
    private static readonly NonBlockingDictionary<string, SpecificationOpenApiInfo> GetGroupOpenApiInfoCached;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取分组配置信息
 | 
			
		||||
@@ -738,7 +738,7 @@ public static class SpecificationDocumentBuilder
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取控制器组缓存集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static readonly ConcurrentDictionary<Type, IEnumerable<GroupExtraInfo>> GetControllerGroupsCached;
 | 
			
		||||
    private static readonly NonBlockingDictionary<Type, IEnumerable<GroupExtraInfo>> GetControllerGroupsCached;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取控制器分组列表
 | 
			
		||||
@@ -773,7 +773,7 @@ public static class SpecificationDocumentBuilder
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <see cref="GetActionGroups(MethodInfo)"/> 缓存集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static readonly ConcurrentDictionary<MethodInfo, IEnumerable<GroupExtraInfo>> GetActionGroupsCached;
 | 
			
		||||
    private static readonly NonBlockingDictionary<MethodInfo, IEnumerable<GroupExtraInfo>> GetActionGroupsCached;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取动作方法分组列表
 | 
			
		||||
@@ -808,7 +808,7 @@ public static class SpecificationDocumentBuilder
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <see cref="GetActionTag(ApiDescription)"/> 缓存集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static readonly ConcurrentDictionary<ControllerActionDescriptor, string> GetControllerTagCached;
 | 
			
		||||
    private static readonly NonBlockingDictionary<ControllerActionDescriptor, string> GetControllerTagCached;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取控制器标签
 | 
			
		||||
@@ -835,7 +835,7 @@ public static class SpecificationDocumentBuilder
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <see cref="GetActionTag(ApiDescription)"/> 缓存集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static readonly ConcurrentDictionary<ApiDescription, string> GetActionTagCached;
 | 
			
		||||
    private static readonly NonBlockingDictionary<ApiDescription, string> GetActionTagCached;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取动作方法标签
 | 
			
		||||
 
 | 
			
		||||
@@ -51,12 +51,12 @@ public static class UnifyContext
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 规范化结果提供器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static ConcurrentDictionary<string, UnifyMetadata> UnifyProviders = new();
 | 
			
		||||
    internal static NonBlockingDictionary<string, UnifyMetadata> UnifyProviders = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 规范化序列化配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static ConcurrentDictionary<string, object> UnifySerializerSettings = new();
 | 
			
		||||
    internal static NonBlockingDictionary<string, object> UnifySerializerSettings = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取异常元数据
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ using System.Collections.Concurrent;
 | 
			
		||||
namespace ThingsGateway.Extension;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     <see cref="ConcurrentDictionary{TKey, TValue}" /> 拓展类
 | 
			
		||||
///     <see cref="NonBlockingDictionary{TKey, TValue}" /> 拓展类
 | 
			
		||||
/// </summary>
 | 
			
		||||
internal static class ConcurrentDictionaryExtensions
 | 
			
		||||
{
 | 
			
		||||
@@ -24,7 +24,7 @@ internal static class ConcurrentDictionaryExtensions
 | 
			
		||||
    /// <typeparam name="TKey">字典键类型</typeparam>
 | 
			
		||||
    /// <typeparam name="TValue">字典值类型</typeparam>
 | 
			
		||||
    /// <param name="dictionary">
 | 
			
		||||
    ///     <see cref="ConcurrentDictionary{TKey, TValue}" />
 | 
			
		||||
    ///     <see cref="NonBlockingDictionary{TKey, TValue}" />
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <param name="key">
 | 
			
		||||
    ///     <typeparamref name="TKey" />
 | 
			
		||||
@@ -36,7 +36,7 @@ internal static class ConcurrentDictionaryExtensions
 | 
			
		||||
    /// <returns>
 | 
			
		||||
    ///     <see cref="bool" />
 | 
			
		||||
    /// </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
 | 
			
		||||
        , Func<TValue, TValue> updateFactory
 | 
			
		||||
        , out TValue? value)
 | 
			
		||||
 
 | 
			
		||||
@@ -241,7 +241,7 @@ internal static class IDictionaryExtensions
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>其中键是由值通过给定的选择器函数生成的。</remarks>
 | 
			
		||||
    /// <param name="dictionary">
 | 
			
		||||
    ///     <see cref="ConcurrentDictionary{TKey, TValue}" />
 | 
			
		||||
    ///     <see cref="NonBlockingDictionary{TKey, TValue}" />
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <param name="values">
 | 
			
		||||
    ///     <see cref="IEnumerable{T}" />
 | 
			
		||||
@@ -249,7 +249,7 @@ internal static class IDictionaryExtensions
 | 
			
		||||
    /// <param name="keySelector">键选择器</param>
 | 
			
		||||
    /// <typeparam name="TKey">字典键类型</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)
 | 
			
		||||
        where TKey : notnull
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,20 +21,20 @@ internal sealed class CoreOptions
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     已注册的组件元数据集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal readonly ConcurrentDictionary<string, ComponentMetadata> _metadataOfRegistered;
 | 
			
		||||
    internal readonly NonBlockingDictionary<string, ComponentMetadata> _metadataOfRegistered;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     子选项集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal readonly ConcurrentDictionary<Type, object> _optionsInstances;
 | 
			
		||||
    internal readonly NonBlockingDictionary<Type, object> _optionsInstances;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     <inheritdoc cref="CoreOptions" />
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal CoreOptions()
 | 
			
		||||
    {
 | 
			
		||||
        _optionsInstances = new ConcurrentDictionary<Type, object>();
 | 
			
		||||
        _metadataOfRegistered = new ConcurrentDictionary<string, ComponentMetadata>(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
        _optionsInstances = new NonBlockingDictionary<Type, object>();
 | 
			
		||||
        _metadataOfRegistered = new NonBlockingDictionary<string, ComponentMetadata>(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
 | 
			
		||||
        EntryComponentTypes = [];
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ public sealed class ObjectPropertyGetter<T> where T : class
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     对象类型实例属性值访问器集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal readonly ConcurrentDictionary<string, Func<object, object?>> _propertyGetters = new();
 | 
			
		||||
    internal readonly NonBlockingDictionary<string, Func<object, object?>> _propertyGetters = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     <inheritdoc cref="ObjectPropertyGetter{T}" />
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ public sealed class ObjectPropertySetter<T> where T : class
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     对象类型实例属性值设置器集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal readonly ConcurrentDictionary<string, Action<object, object?>> _propertySetters = new();
 | 
			
		||||
    internal readonly NonBlockingDictionary<string, Action<object, object?>> _propertySetters = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     <inheritdoc cref="ObjectPropertySetter{T}" />
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ public sealed class HttpDeclarativeBuilder
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     HTTP 声明式 <see cref="IHttpDeclarativeExtractor" /> 提取器集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static readonly ConcurrentDictionary<Type, IHttpDeclarativeExtractor> _extractors = new([
 | 
			
		||||
    internal static readonly NonBlockingDictionary<Type, IHttpDeclarativeExtractor> _extractors = new([
 | 
			
		||||
        new(typeof(BaseAddressDeclarativeExtractor), new BaseAddressDeclarativeExtractor()),
 | 
			
		||||
        new(typeof(ValidationDeclarativeExtractor), new ValidationDeclarativeExtractor()),
 | 
			
		||||
        new(typeof(AutoSetHostHeaderDeclarativeExtractor), new AutoSetHostHeaderDeclarativeExtractor()),
 | 
			
		||||
@@ -56,7 +56,7 @@ public sealed class HttpDeclarativeBuilder
 | 
			
		||||
    ///     HTTP 声明式 <see cref="IHttpDeclarativeExtractor" /> 提取器集合(冻结)
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>该集合用于确保某些 HTTP 声明式提取器始终位于最后。</remarks>
 | 
			
		||||
    internal static readonly ConcurrentDictionary<Type, IFrozenHttpDeclarativeExtractor> _frozenExtractors = new([
 | 
			
		||||
    internal static readonly NonBlockingDictionary<Type, IFrozenHttpDeclarativeExtractor> _frozenExtractors = new([
 | 
			
		||||
        new(typeof(MultipartDeclarativeExtractor), new MultipartDeclarativeExtractor()),
 | 
			
		||||
        new(typeof(HttpMultipartFormDataBuilderDeclarativeExtractor),
 | 
			
		||||
            new HttpMultipartFormDataBuilderDeclarativeExtractor()),
 | 
			
		||||
 
 | 
			
		||||
@@ -251,7 +251,8 @@ public sealed class ProfilerDelegatingHandler(ILogger<Logging> logger, IOptions<
 | 
			
		||||
        // 检查是否配置(注册)了日志程序
 | 
			
		||||
        if (remoteOptions.IsLoggingRegistered)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Log(remoteOptions.ProfilerLogLevel, "{message}", message);
 | 
			
		||||
            if (logger?.IsEnabled(remoteOptions.ProfilerLogLevel) == true)
 | 
			
		||||
                logger.Log(remoteOptions.ProfilerLogLevel, "{message}", message);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ public class MessagePackContentProcessor : HttpContentProcessorBase
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     MessagePack 序列化器委托字典缓存
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static readonly ConcurrentDictionary<Type, Func<object, byte[]>> _serializerCache = new();
 | 
			
		||||
    internal static readonly NonBlockingDictionary<Type, Func<object, byte[]>> _serializerCache = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     初始化 MessagePack 序列化器委托
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ public class MemoryCache : Cache
 | 
			
		||||
{
 | 
			
		||||
    #region 属性
 | 
			
		||||
    /// <summary>缓存核心</summary>
 | 
			
		||||
    protected ConcurrentDictionary<String, CacheItem> _cache = new();
 | 
			
		||||
    protected NonBlockingDictionary<String, CacheItem> _cache = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>容量。容量超标时,采用LRU机制删除,默认100_000</summary>
 | 
			
		||||
    public Int32 Capacity { get; set; } = 100_000;
 | 
			
		||||
@@ -379,7 +379,7 @@ public class MemoryCache : Cache
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public override IDictionary<String, T> GetDictionary<T>(String key)
 | 
			
		||||
    {
 | 
			
		||||
        var item = GetOrAddItem(key, k => new ConcurrentDictionary<String, T>());
 | 
			
		||||
        var item = GetOrAddItem(key, k => new NonBlockingDictionary<String, T>());
 | 
			
		||||
        return item.Visit<IDictionary<String, T>>() ??
 | 
			
		||||
         throw new InvalidCastException($"Unable to convert the value of [{key}] from {item.TypeCode} to {typeof(IDictionary<String, T>)}");
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
后续例程与使用说明均以Redis为例,各缓存实现类似。  
 | 
			
		||||
 | 
			
		||||
### 内存缓存 MemoryCache
 | 
			
		||||
MemoryCache核心是并发字典ConcurrentDictionary,由于省去了序列化和网络通信,使得它具有千万级超高性能。  
 | 
			
		||||
MemoryCache核心是并发字典NonBlockingDictionary,由于省去了序列化和网络通信,使得它具有千万级超高性能。  
 | 
			
		||||
MemoryCache支持过期时间,默认容量10万个,未过期key超过该值后,每60秒根据LRU清理溢出部分。  
 | 
			
		||||
常用于进程内千万级以下数据缓存场景。  
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ public static class CollectionHelper
 | 
			
		||||
    {
 | 
			
		||||
        //if (collection == null) return null;
 | 
			
		||||
 | 
			
		||||
        if (collection is ConcurrentDictionary<TKey, TValue> cdiv && cdiv.Keys is IList<TKey> list) return list;
 | 
			
		||||
        if (collection is NonBlockingDictionary<TKey, TValue> cdiv && cdiv.Keys is IList<TKey> list) return list;
 | 
			
		||||
 | 
			
		||||
        if (collection.Count == 0) return [];
 | 
			
		||||
        lock (collection)
 | 
			
		||||
@@ -65,8 +65,8 @@ public static class CollectionHelper
 | 
			
		||||
    {
 | 
			
		||||
        //if (collection == null) return null;
 | 
			
		||||
 | 
			
		||||
        //if (collection is ConcurrentDictionary<TKey, TValue> cdiv) return cdiv.Values as IList<TValue>;
 | 
			
		||||
        if (collection is ConcurrentDictionary<TKey, TValue> cdiv && cdiv.Values is IList<TValue> list) return list;
 | 
			
		||||
        //if (collection is NonBlockingDictionary<TKey, TValue> cdiv) return cdiv.Values as IList<TValue>;
 | 
			
		||||
        if (collection is NonBlockingDictionary<TKey, TValue> cdiv && cdiv.Values is IList<TValue> list) return list;
 | 
			
		||||
 | 
			
		||||
        if (collection.Count == 0) return [];
 | 
			
		||||
        lock (collection)
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ namespace ThingsGateway.NewLife.Collections;
 | 
			
		||||
/// </remarks>
 | 
			
		||||
public class ConcurrentHashSet<T> : IEnumerable<T> where T : notnull
 | 
			
		||||
{
 | 
			
		||||
    private readonly ConcurrentDictionary<T, Byte> _dic = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<T, Byte> _dic = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>是否空集合</summary>
 | 
			
		||||
    public Boolean IsEmpty => _dic.IsEmpty;
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,77 @@
 | 
			
		||||
// Copyright (c) Vladimir Sadov. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is distributed under the MIT License. See LICENSE.md for details.
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
namespace System.Collections.Concurrent
 | 
			
		||||
{
 | 
			
		||||
    internal abstract partial class DictionaryImpl<TKey, TKeyStore, TValue>
 | 
			
		||||
        : DictionaryImpl<TKey, TValue>
 | 
			
		||||
    {
 | 
			
		||||
        internal override Snapshot GetSnapshot()
 | 
			
		||||
        {
 | 
			
		||||
            return new SnapshotImpl(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private class SnapshotImpl : Snapshot
 | 
			
		||||
        {
 | 
			
		||||
            private readonly DictionaryImpl<TKey, TKeyStore, TValue> _table;
 | 
			
		||||
 | 
			
		||||
            public SnapshotImpl(DictionaryImpl<TKey, TKeyStore, TValue> dict)
 | 
			
		||||
            {
 | 
			
		||||
                this._table = dict;
 | 
			
		||||
 | 
			
		||||
                // linearization point.
 | 
			
		||||
                // if table is quiescent and has no copy in progress,
 | 
			
		||||
                // we can simply iterate over its table.
 | 
			
		||||
                while (_table._newTable != null)
 | 
			
		||||
                {
 | 
			
		||||
                    // there is a copy in progress, finish it and try again
 | 
			
		||||
                    _table.HelpCopy(copy_all: true);
 | 
			
		||||
                    this._table = (DictionaryImpl<TKey, TKeyStore, TValue>)this._table._topDict._table;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public override int Count => _table.Count;
 | 
			
		||||
 | 
			
		||||
            public override bool MoveNext()
 | 
			
		||||
            {
 | 
			
		||||
                var entries = this._table._entries;
 | 
			
		||||
                while (_idx < entries.Length)
 | 
			
		||||
                {
 | 
			
		||||
                    var nextEntry = entries[_idx++];
 | 
			
		||||
 | 
			
		||||
                    if (nextEntry.value != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        var nextKstore = nextEntry.key;
 | 
			
		||||
                        if (nextKstore == null)
 | 
			
		||||
                        {
 | 
			
		||||
                            // slot was deleted.
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        _curKey = _table.keyFromEntry(nextKstore);
 | 
			
		||||
                        object nextV = _table.TryGetValue(_curKey);
 | 
			
		||||
                        if (nextV != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            _curValue = _table.FromObjectValue(nextV);
 | 
			
		||||
                            return true;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                _curKey = default;
 | 
			
		||||
                _curValue = default;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public override void Reset()
 | 
			
		||||
            {
 | 
			
		||||
                _idx = 0;
 | 
			
		||||
                _curKey = default;
 | 
			
		||||
                _curValue = default;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,93 @@
 | 
			
		||||
// Copyright (c) Vladimir Sadov. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is distributed under the MIT License. See LICENSE.md for details.
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
 | 
			
		||||
namespace System.Collections.Concurrent
 | 
			
		||||
{
 | 
			
		||||
    internal abstract class DictionaryImpl
 | 
			
		||||
    {
 | 
			
		||||
        internal enum ValueMatch
 | 
			
		||||
        {
 | 
			
		||||
            Any,            // sets new value unconditionally, used by index set and TryRemove(key)
 | 
			
		||||
            NullOrDead,     // set value if original value is null or dead, used by Add/TryAdd
 | 
			
		||||
            NotNullOrDead,  // set value if original value is alive, used by Remove
 | 
			
		||||
            OldValue,       // sets new value if old value matches
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal sealed class Prime
 | 
			
		||||
        {
 | 
			
		||||
            internal object originalValue;
 | 
			
		||||
 | 
			
		||||
            public Prime(object originalValue)
 | 
			
		||||
            {
 | 
			
		||||
                this.originalValue = originalValue;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal static readonly object TOMBSTONE = new object();
 | 
			
		||||
        internal static readonly Prime TOMBPRIME = new Prime(TOMBSTONE);
 | 
			
		||||
        internal static readonly object NULLVALUE = new object();
 | 
			
		||||
 | 
			
		||||
        // represents a trivially copied empty entry
 | 
			
		||||
        // we insert it in the old table during rehashing
 | 
			
		||||
        // to reduce chances that more entries are added
 | 
			
		||||
        protected const int TOMBPRIMEHASH = 1 << 31;
 | 
			
		||||
 | 
			
		||||
        // we cannot distigush zero keys from uninitialized state
 | 
			
		||||
        // so we force them to have this special hash instead
 | 
			
		||||
        protected const int ZEROHASH = 1 << 30;
 | 
			
		||||
 | 
			
		||||
        // all regular hashes have both these bits set
 | 
			
		||||
        // to be different from either 0, TOMBPRIMEHASH or ZEROHASH
 | 
			
		||||
        // having only these bits set in a case of Ref key means that the slot is permanently deleted.
 | 
			
		||||
        protected const int SPECIAL_HASH_BITS = TOMBPRIMEHASH | ZEROHASH;
 | 
			
		||||
 | 
			
		||||
        // Heuristic to decide if we have reprobed toooo many times.  Running over
 | 
			
		||||
        // the reprobe limit on a 'get' call acts as a 'miss'; on a 'put' call it
 | 
			
		||||
        // can trigger a table resize.  Several places must have exact agreement on
 | 
			
		||||
        // what the reprobe_limit is, so we share it here.
 | 
			
		||||
        protected const int REPROBE_LIMIT = 4;
 | 
			
		||||
        protected const int REPROBE_LIMIT_SHIFT = 8;
 | 
			
		||||
 | 
			
		||||
        protected static int ReprobeLimit(int lenMask)
 | 
			
		||||
        {
 | 
			
		||||
            // 1/2 of table with some extra
 | 
			
		||||
            return REPROBE_LIMIT + (lenMask >> REPROBE_LIMIT_SHIFT);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected static bool EntryValueNullOrDead(object entryValue)
 | 
			
		||||
        {
 | 
			
		||||
            return entryValue == null || entryValue == TOMBSTONE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        protected static int ReduceHashToIndex(int fullHash, int lenMask)
 | 
			
		||||
        {
 | 
			
		||||
            var h = (uint)fullHash;
 | 
			
		||||
 | 
			
		||||
            // xor-shift some upper bits down, in case if variations are mostly in high bits
 | 
			
		||||
            // and scatter the bits a little to break up clusters if hashes are periodic (like 42, 43, 44, ...)
 | 
			
		||||
            // long clusters can cause long reprobes. small clusters are ok though.
 | 
			
		||||
            h ^= h >> 15;
 | 
			
		||||
            h ^= h >> 8;
 | 
			
		||||
            h += (h >> 3) * 2654435769u;
 | 
			
		||||
 | 
			
		||||
            return (int)h & lenMask;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        internal static object ToObjectValue<TValue>(TValue value)
 | 
			
		||||
        {
 | 
			
		||||
            if (default(TValue) != null)
 | 
			
		||||
            {
 | 
			
		||||
                return new Boxed<TValue>(value);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return (object)value ?? NULLVALUE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,138 @@
 | 
			
		||||
// Copyright (c) Vladimir Sadov. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is distributed under the MIT License. See LICENSE.md for details.
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
 | 
			
		||||
namespace System.Collections.Concurrent
 | 
			
		||||
{
 | 
			
		||||
    internal sealed class DictionaryImplBoxed<TKey, TValue>
 | 
			
		||||
            : DictionaryImpl<TKey, Boxed<TKey>, TValue>
 | 
			
		||||
    {
 | 
			
		||||
        internal DictionaryImplBoxed(int capacity, NonBlockingDictionary<TKey, TValue> topDict)
 | 
			
		||||
            : base(capacity, topDict)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal DictionaryImplBoxed(int capacity, DictionaryImplBoxed<TKey, TValue> other)
 | 
			
		||||
            : base(capacity, other)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForPut(ref Boxed<TKey> entryKey, TKey key)
 | 
			
		||||
        {
 | 
			
		||||
            var entryKeyValue = entryKey;
 | 
			
		||||
            if (entryKeyValue == null)
 | 
			
		||||
            {
 | 
			
		||||
                entryKeyValue = Interlocked.CompareExchange(ref entryKey, new Boxed<TKey>(key), null);
 | 
			
		||||
                if (entryKeyValue == null)
 | 
			
		||||
                {
 | 
			
		||||
                    // claimed a new slot
 | 
			
		||||
                    this.allocatedSlotCount.Increment();
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return _keyComparer.Equals(key, entryKeyValue.Value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForCopy(ref Boxed<TKey> entryKey, Boxed<TKey> key)
 | 
			
		||||
        {
 | 
			
		||||
            var entryKeyValue = entryKey;
 | 
			
		||||
            if (entryKeyValue == null)
 | 
			
		||||
            {
 | 
			
		||||
                entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, null);
 | 
			
		||||
                if (entryKeyValue == null)
 | 
			
		||||
                {
 | 
			
		||||
                    // claimed a new slot
 | 
			
		||||
                    this.allocatedSlotCount.Increment();
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return _keyComparer.Equals(key.Value, entryKeyValue.Value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool keyEqual(TKey key, Boxed<TKey> entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            //NOTE: slots are claimed in two stages - claim a hash, then set a key
 | 
			
		||||
            //      it is possible to observe a slot with a null key, but with hash already set
 | 
			
		||||
            //      that is not a match since the key is not yet in the table
 | 
			
		||||
            if (entryKey == null)
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return _keyComparer.Equals(key, entryKey.Value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DictionaryImpl<TKey, Boxed<TKey>, TValue> CreateNew(int capacity)
 | 
			
		||||
        {
 | 
			
		||||
            return new DictionaryImplBoxed<TKey, TValue>(capacity, this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override TKey keyFromEntry(Boxed<TKey> entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return entryKey.Value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#pragma warning disable CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode()
 | 
			
		||||
    internal class Boxed<T>
 | 
			
		||||
    {
 | 
			
		||||
        // 0 - allow writes, 1 - someone is writing, 2 frozen.
 | 
			
		||||
        public int writeStatus;
 | 
			
		||||
        public T Value;
 | 
			
		||||
 | 
			
		||||
        public Boxed(T key)
 | 
			
		||||
        {
 | 
			
		||||
            this.Value = key;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override bool Equals(object obj)
 | 
			
		||||
        {
 | 
			
		||||
            return EqualityComparer<T>.Default.Equals(this.Value, Unsafe.As<Boxed<T>>(obj).Value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        public bool TryVolatileWrite(T value)
 | 
			
		||||
        {
 | 
			
		||||
            if (Interlocked.CompareExchange(ref writeStatus, 1, 0) == 0)
 | 
			
		||||
            {
 | 
			
		||||
                Value = value;
 | 
			
		||||
                Volatile.Write(ref writeStatus, 0);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        public bool TryCompareExchange(T oldValue, T newValue, out bool changed)
 | 
			
		||||
        {
 | 
			
		||||
            changed = false;
 | 
			
		||||
            if (Interlocked.CompareExchange(ref writeStatus, 1, 0) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (EqualityComparer<T>.Default.Equals(Value, oldValue))
 | 
			
		||||
            {
 | 
			
		||||
                Value = newValue;
 | 
			
		||||
                changed = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Volatile.Write(ref writeStatus, 0);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        internal void Freeze()
 | 
			
		||||
        {
 | 
			
		||||
            // Wait for writers (1) to leave. Already 2 is ok, or set 0 -> 2.
 | 
			
		||||
            while (Interlocked.CompareExchange(ref writeStatus, 2, 0) == 1) ;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#pragma warning restore CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode()
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,143 @@
 | 
			
		||||
// Copyright (c) Vladimir Sadov. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is distributed under the MIT License. See LICENSE.md for details.
 | 
			
		||||
 | 
			
		||||
namespace System.Collections.Concurrent
 | 
			
		||||
{
 | 
			
		||||
    internal sealed class DictionaryImplInt<TValue>
 | 
			
		||||
                : DictionaryImpl<int, int, TValue>
 | 
			
		||||
    {
 | 
			
		||||
        internal DictionaryImplInt(int capacity, NonBlockingDictionary<int, TValue> topDict)
 | 
			
		||||
            : base(capacity, topDict)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal DictionaryImplInt(int capacity, DictionaryImplInt<TValue> other)
 | 
			
		||||
            : base(capacity, other)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForPut(ref int entryKey, int key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForCopy(ref int entryKey, int key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool TryClaimSlot(ref int entryKey, int key)
 | 
			
		||||
        {
 | 
			
		||||
            var entryKeyValue = entryKey;
 | 
			
		||||
            //zero keys are claimed via hash
 | 
			
		||||
            if (entryKeyValue == 0 & key != 0)
 | 
			
		||||
            {
 | 
			
		||||
                entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, 0);
 | 
			
		||||
                if (entryKeyValue == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    // claimed a new slot
 | 
			
		||||
                    this.allocatedSlotCount.Increment();
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return key == entryKeyValue || _keyComparer.Equals(key, entryKeyValue);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override int hash(int key)
 | 
			
		||||
        {
 | 
			
		||||
            if (key == 0)
 | 
			
		||||
            {
 | 
			
		||||
                return ZEROHASH;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return base.hash(key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool keyEqual(int key, int entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return key == entryKey || _keyComparer.Equals(key, entryKey);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DictionaryImpl<int, int, TValue> CreateNew(int capacity)
 | 
			
		||||
        {
 | 
			
		||||
            return new DictionaryImplInt<TValue>(capacity, this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override int keyFromEntry(int entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return entryKey;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal sealed class DictionaryImplIntNoComparer<TValue>
 | 
			
		||||
            : DictionaryImpl<int, int, TValue>
 | 
			
		||||
    {
 | 
			
		||||
        internal DictionaryImplIntNoComparer(int capacity, NonBlockingDictionary<int, TValue> topDict)
 | 
			
		||||
            : base(capacity, topDict)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal DictionaryImplIntNoComparer(int capacity, DictionaryImplIntNoComparer<TValue> other)
 | 
			
		||||
            : base(capacity, other)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForPut(ref int entryKey, int key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForCopy(ref int entryKey, int key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool TryClaimSlot(ref int entryKey, int key)
 | 
			
		||||
        {
 | 
			
		||||
            var entryKeyValue = entryKey;
 | 
			
		||||
            //zero keys are claimed via hash
 | 
			
		||||
            if (entryKeyValue == 0 & key != 0)
 | 
			
		||||
            {
 | 
			
		||||
                entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, 0);
 | 
			
		||||
                if (entryKeyValue == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    // claimed a new slot
 | 
			
		||||
                    this.allocatedSlotCount.Increment();
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return key == entryKeyValue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // inline the base implementation to devirtualize calls to hash and keyEqual
 | 
			
		||||
        internal override object TryGetValue(int key)
 | 
			
		||||
        {
 | 
			
		||||
            return base.TryGetValue(key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override int hash(int key)
 | 
			
		||||
        {
 | 
			
		||||
            return (key == 0) ?
 | 
			
		||||
                ZEROHASH :
 | 
			
		||||
                key | SPECIAL_HASH_BITS;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool keyEqual(int key, int entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return key == entryKey;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DictionaryImpl<int, int, TValue> CreateNew(int capacity)
 | 
			
		||||
        {
 | 
			
		||||
            return new DictionaryImplIntNoComparer<TValue>(capacity, this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override int keyFromEntry(int entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return entryKey;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,143 @@
 | 
			
		||||
// Copyright (c) Vladimir Sadov. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is distributed under the MIT License. See LICENSE.md for details.
 | 
			
		||||
 | 
			
		||||
namespace System.Collections.Concurrent
 | 
			
		||||
{
 | 
			
		||||
    internal sealed class DictionaryImplLong<TValue>
 | 
			
		||||
                : DictionaryImpl<long, long, TValue>
 | 
			
		||||
    {
 | 
			
		||||
        internal DictionaryImplLong(int capacity, NonBlockingDictionary<long, TValue> topDict)
 | 
			
		||||
            : base(capacity, topDict)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal DictionaryImplLong(int capacity, DictionaryImplLong<TValue> other)
 | 
			
		||||
            : base(capacity, other)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForPut(ref long entryKey, long key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForCopy(ref long entryKey, long key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool TryClaimSlot(ref long entryKey, long key)
 | 
			
		||||
        {
 | 
			
		||||
            var entryKeyValue = entryKey;
 | 
			
		||||
            //zero keys are claimed via hash
 | 
			
		||||
            if (entryKeyValue == 0 && key != 0)
 | 
			
		||||
            {
 | 
			
		||||
                entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, 0);
 | 
			
		||||
                if (entryKeyValue == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    // claimed a new slot
 | 
			
		||||
                    this.allocatedSlotCount.Increment();
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return key == entryKeyValue || _keyComparer.Equals(key, entryKeyValue);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override int hash(long key)
 | 
			
		||||
        {
 | 
			
		||||
            if (key == 0)
 | 
			
		||||
            {
 | 
			
		||||
                return ZEROHASH;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return base.hash(key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool keyEqual(long key, long entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return key == entryKey || _keyComparer.Equals(key, entryKey);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DictionaryImpl<long, long, TValue> CreateNew(int capacity)
 | 
			
		||||
        {
 | 
			
		||||
            return new DictionaryImplLong<TValue>(capacity, this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override long keyFromEntry(long entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return entryKey;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal sealed class DictionaryImplLongNoComparer<TValue>
 | 
			
		||||
            : DictionaryImpl<long, long, TValue>
 | 
			
		||||
    {
 | 
			
		||||
        internal DictionaryImplLongNoComparer(int capacity, NonBlockingDictionary<long, TValue> topDict)
 | 
			
		||||
            : base(capacity, topDict)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal DictionaryImplLongNoComparer(int capacity, DictionaryImplLongNoComparer<TValue> other)
 | 
			
		||||
            : base(capacity, other)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForPut(ref long entryKey, long key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForCopy(ref long entryKey, long key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool TryClaimSlot(ref long entryKey, long key)
 | 
			
		||||
        {
 | 
			
		||||
            var entryKeyValue = entryKey;
 | 
			
		||||
            //zero keys are claimed via hash
 | 
			
		||||
            if (entryKeyValue == 0 && key != 0)
 | 
			
		||||
            {
 | 
			
		||||
                entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, 0);
 | 
			
		||||
                if (entryKeyValue == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    // claimed a new slot
 | 
			
		||||
                    this.allocatedSlotCount.Increment();
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return key == entryKeyValue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // inline the base implementation to devirtualize calls to hash and keyEqual
 | 
			
		||||
        internal override object TryGetValue(long key)
 | 
			
		||||
        {
 | 
			
		||||
            return base.TryGetValue(key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override int hash(long key)
 | 
			
		||||
        {
 | 
			
		||||
            return (key == 0) ?
 | 
			
		||||
                ZEROHASH :
 | 
			
		||||
                key.GetHashCode() | SPECIAL_HASH_BITS;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool keyEqual(long key, long entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return key == entryKey;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DictionaryImpl<long, long, TValue> CreateNew(int capacity)
 | 
			
		||||
        {
 | 
			
		||||
            return new DictionaryImplLongNoComparer<TValue>(capacity, this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override long keyFromEntry(long entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return entryKey;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,143 @@
 | 
			
		||||
// Copyright (c) Vladimir Sadov. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is distributed under the MIT License. See LICENSE.md for details.
 | 
			
		||||
 | 
			
		||||
namespace System.Collections.Concurrent
 | 
			
		||||
{
 | 
			
		||||
    internal sealed class DictionaryImplNint<TValue>
 | 
			
		||||
                : DictionaryImpl<nint, nint, TValue>
 | 
			
		||||
    {
 | 
			
		||||
        internal DictionaryImplNint(int capacity, NonBlockingDictionary<nint, TValue> topDict)
 | 
			
		||||
            : base(capacity, topDict)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal DictionaryImplNint(int capacity, DictionaryImplNint<TValue> other)
 | 
			
		||||
            : base(capacity, other)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForPut(ref nint entryKey, nint key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForCopy(ref nint entryKey, nint key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool TryClaimSlot(ref nint entryKey, nint key)
 | 
			
		||||
        {
 | 
			
		||||
            var entryKeyValue = entryKey;
 | 
			
		||||
            //zero keys are claimed via hash
 | 
			
		||||
            if (entryKeyValue == 0 && key != 0)
 | 
			
		||||
            {
 | 
			
		||||
                entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, (nint)0);
 | 
			
		||||
                if (entryKeyValue == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    // claimed a new slot
 | 
			
		||||
                    this.allocatedSlotCount.Increment();
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return key == entryKeyValue || _keyComparer.Equals(key, entryKeyValue);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override int hash(nint key)
 | 
			
		||||
        {
 | 
			
		||||
            if (key == 0)
 | 
			
		||||
            {
 | 
			
		||||
                return ZEROHASH;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return base.hash(key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool keyEqual(nint key, nint entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return key == entryKey || _keyComparer.Equals(key, entryKey);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DictionaryImpl<nint, nint, TValue> CreateNew(int capacity)
 | 
			
		||||
        {
 | 
			
		||||
            return new DictionaryImplNint<TValue>(capacity, this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override nint keyFromEntry(nint entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return entryKey;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal sealed class DictionaryImplNintNoComparer<TValue>
 | 
			
		||||
            : DictionaryImpl<nint, nint, TValue>
 | 
			
		||||
    {
 | 
			
		||||
        internal DictionaryImplNintNoComparer(int capacity, NonBlockingDictionary<nint, TValue> topDict)
 | 
			
		||||
            : base(capacity, topDict)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal DictionaryImplNintNoComparer(int capacity, DictionaryImplNintNoComparer<TValue> other)
 | 
			
		||||
            : base(capacity, other)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForPut(ref nint entryKey, nint key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForCopy(ref nint entryKey, nint key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool TryClaimSlot(ref nint entryKey, nint key)
 | 
			
		||||
        {
 | 
			
		||||
            var entryKeyValue = entryKey;
 | 
			
		||||
            //zero keys are claimed via hash
 | 
			
		||||
            if (entryKeyValue == 0 && key != 0)
 | 
			
		||||
            {
 | 
			
		||||
                entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, (nint)0);
 | 
			
		||||
                if (entryKeyValue == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    // claimed a new slot
 | 
			
		||||
                    this.allocatedSlotCount.Increment();
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return key == entryKeyValue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // inline the base implementation to devirtualize calls to hash and keyEqual
 | 
			
		||||
        internal override object TryGetValue(nint key)
 | 
			
		||||
        {
 | 
			
		||||
            return base.TryGetValue(key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override int hash(nint key)
 | 
			
		||||
        {
 | 
			
		||||
            return (key == 0) ?
 | 
			
		||||
                ZEROHASH :
 | 
			
		||||
                key.GetHashCode() | SPECIAL_HASH_BITS;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool keyEqual(nint key, nint entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return key == entryKey;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DictionaryImpl<nint, nint, TValue> CreateNew(int capacity)
 | 
			
		||||
        {
 | 
			
		||||
            return new DictionaryImplNintNoComparer<TValue>(capacity, this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override nint keyFromEntry(nint entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return entryKey;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,95 @@
 | 
			
		||||
// Copyright (c) Vladimir Sadov. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is distributed under the MIT License. See LICENSE.md for details.
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
 | 
			
		||||
namespace System.Collections.Concurrent
 | 
			
		||||
{
 | 
			
		||||
    internal sealed class DictionaryImplRef<TKey, TKeyStore, TValue>
 | 
			
		||||
            : DictionaryImpl<TKey, TKey, TValue>
 | 
			
		||||
    {
 | 
			
		||||
        internal DictionaryImplRef(int capacity, NonBlockingDictionary<TKey, TValue> topDict)
 | 
			
		||||
            : base(capacity, topDict)
 | 
			
		||||
        {
 | 
			
		||||
            Debug.Assert(!typeof(TKey).IsValueType);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal DictionaryImplRef(int capacity, DictionaryImplRef<TKey, TKeyStore, TValue> other)
 | 
			
		||||
            : base(capacity, other)
 | 
			
		||||
        {
 | 
			
		||||
            Debug.Assert(!typeof(TKey).IsValueType);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForPut(ref TKey entryKey, TKey key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool TryClaimSlotForCopy(ref TKey entryKey, TKey key)
 | 
			
		||||
        {
 | 
			
		||||
            return TryClaimSlot(ref entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool TryClaimSlot(ref TKey entryKey, TKey key)
 | 
			
		||||
        {
 | 
			
		||||
            ref object keyLocation = ref Unsafe.As<TKey, object>(ref entryKey);
 | 
			
		||||
            object entryKeyValue = keyLocation;
 | 
			
		||||
            if (entryKeyValue == null)
 | 
			
		||||
            {
 | 
			
		||||
                entryKeyValue = Interlocked.CompareExchange(ref keyLocation, key, null);
 | 
			
		||||
                if (entryKeyValue == null)
 | 
			
		||||
                {
 | 
			
		||||
                    // claimed a new slot
 | 
			
		||||
                    this.allocatedSlotCount.Increment();
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return (object)key == entryKeyValue ||
 | 
			
		||||
                _keyComparer.Equals(key, Unsafe.As<object, TKey>(ref entryKeyValue));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // inline the base implementation to devirtualize calls to hash and keyEqual
 | 
			
		||||
        internal override object TryGetValue(TKey key)
 | 
			
		||||
        {
 | 
			
		||||
            return base.TryGetValue(key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override int hash(TKey key)
 | 
			
		||||
        {
 | 
			
		||||
            return base.hash(key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override bool keyEqual(TKey key, TKey entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            if ((object)key == (object)entryKey)
 | 
			
		||||
            {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //NOTE: slots are claimed in two stages - claim a hash, then set a key
 | 
			
		||||
            //      it is possible to observe a slot with a null key, but with hash already set
 | 
			
		||||
            //      that is not a match since the key is not yet in the table
 | 
			
		||||
            if (entryKey == null)
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return _keyComparer.Equals(entryKey, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DictionaryImpl<TKey, TKey, TValue> CreateNew(int capacity)
 | 
			
		||||
        {
 | 
			
		||||
            return new DictionaryImplRef<TKey, TKeyStore, TValue>(capacity, this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override TKey keyFromEntry(TKey entryKey)
 | 
			
		||||
        {
 | 
			
		||||
            return entryKey;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,81 @@
 | 
			
		||||
// Copyright (c) Vladimir Sadov. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is distributed under the MIT License. See LICENSE.md for details.
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
 | 
			
		||||
namespace System.Collections.Concurrent
 | 
			
		||||
{
 | 
			
		||||
    internal abstract class DictionaryImpl<TKey, TValue>
 | 
			
		||||
        : DictionaryImpl
 | 
			
		||||
    {
 | 
			
		||||
        private readonly bool _valueIsValueType = typeof(TValue).IsValueType;
 | 
			
		||||
 | 
			
		||||
        internal IEqualityComparer<TKey> _keyComparer;
 | 
			
		||||
 | 
			
		||||
        internal abstract void Clear();
 | 
			
		||||
        internal abstract int Count { get; }
 | 
			
		||||
 | 
			
		||||
        internal abstract object TryGetValue(TKey key);
 | 
			
		||||
        internal abstract bool PutIfMatch(TKey key, TValue newVal, ref TValue oldValue, ValueMatch match);
 | 
			
		||||
        internal abstract bool RemoveIfMatch(TKey key, ref TValue oldValue, ValueMatch match);
 | 
			
		||||
        internal abstract TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory);
 | 
			
		||||
 | 
			
		||||
        internal abstract Snapshot GetSnapshot();
 | 
			
		||||
 | 
			
		||||
        internal abstract class Snapshot
 | 
			
		||||
        {
 | 
			
		||||
            protected int _idx;
 | 
			
		||||
            protected TKey _curKey;
 | 
			
		||||
            protected TValue _curValue;
 | 
			
		||||
 | 
			
		||||
            public abstract int Count { get; }
 | 
			
		||||
            public abstract bool MoveNext();
 | 
			
		||||
            public abstract void Reset();
 | 
			
		||||
 | 
			
		||||
            internal DictionaryEntry Entry
 | 
			
		||||
            {
 | 
			
		||||
                get
 | 
			
		||||
                {
 | 
			
		||||
                    return new DictionaryEntry(_curKey, _curValue);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            internal KeyValuePair<TKey, TValue> Current
 | 
			
		||||
            {
 | 
			
		||||
                get
 | 
			
		||||
                {
 | 
			
		||||
                    return new KeyValuePair<TKey, TValue>(this._curKey, _curValue);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        protected TValue FromObjectValue(object obj)
 | 
			
		||||
        {
 | 
			
		||||
            // regular value type
 | 
			
		||||
            if (default(TValue) != null)
 | 
			
		||||
            {
 | 
			
		||||
                return Unsafe.As<Boxed<TValue>>(obj).Value;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // null
 | 
			
		||||
            if (obj == NULLVALUE)
 | 
			
		||||
            {
 | 
			
		||||
                return default(TValue);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // ref type
 | 
			
		||||
            if (!_valueIsValueType)
 | 
			
		||||
            {
 | 
			
		||||
                return Unsafe.As<object, TValue>(ref obj);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // nullable
 | 
			
		||||
            return (TValue)obj;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,204 @@
 | 
			
		||||
// Copyright (c) Vladimir Sadov. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is distributed under the MIT License. See LICENSE.md for details.
 | 
			
		||||
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
namespace System.Collections.Concurrent
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Scalable 32bit counter that can be used from multiple threads.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public sealed class Counter32 : CounterBase
 | 
			
		||||
    {
 | 
			
		||||
        private class Cell
 | 
			
		||||
        {
 | 
			
		||||
            [StructLayout(LayoutKind.Explicit, Size = CACHE_LINE * 2 - OBJ_HEADER_SIZE)]
 | 
			
		||||
            public struct SpacedCounter
 | 
			
		||||
            {
 | 
			
		||||
                [FieldOffset(CACHE_LINE - OBJ_HEADER_SIZE)]
 | 
			
		||||
                public int count;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public SpacedCounter counter;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // spaced out counters
 | 
			
		||||
        private Cell[]? cells;
 | 
			
		||||
 | 
			
		||||
        // default counter
 | 
			
		||||
        private int count;
 | 
			
		||||
 | 
			
		||||
        // delayed estimated count
 | 
			
		||||
        private int lastCount;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Returns the value of the counter at the time of the call.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// The value may miss in-progress updates if the counter is being concurrently modified.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        public int Value
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                var count = this.count;
 | 
			
		||||
                var cells = this.cells;
 | 
			
		||||
 | 
			
		||||
                if (cells != null)
 | 
			
		||||
                {
 | 
			
		||||
                    for (int i = 0; i < cells.Length; i++)
 | 
			
		||||
                    {
 | 
			
		||||
                        var cell = cells[i];
 | 
			
		||||
                        if (cell != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            count += cell.counter.count;
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return count;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Returns the approximate value of the counter at the time of the call.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// EstimatedValue could be significantly cheaper to obtain, but may be slightly delayed.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        public int EstimatedValue
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                if (this.cells == null)
 | 
			
		||||
                {
 | 
			
		||||
                    return this.count;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var curTicks = (uint)Environment.TickCount;
 | 
			
		||||
                // more than a millisecond passed?
 | 
			
		||||
                if (curTicks != lastCountTicks)
 | 
			
		||||
                {
 | 
			
		||||
                    lastCountTicks = curTicks;
 | 
			
		||||
                    lastCount = Value;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return lastCount;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Increments the counter by 1.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Increment()
 | 
			
		||||
        {
 | 
			
		||||
            int curCellCount = this.cellCount;
 | 
			
		||||
            var drift = increment(ref GetCountRef(curCellCount));
 | 
			
		||||
 | 
			
		||||
            if (drift != 0)
 | 
			
		||||
            {
 | 
			
		||||
                TryAddCell(curCellCount);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Decrements the counter by 1.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Decrement()
 | 
			
		||||
        {
 | 
			
		||||
            int curCellCount = this.cellCount;
 | 
			
		||||
            var drift = decrement(ref GetCountRef(curCellCount));
 | 
			
		||||
 | 
			
		||||
            if (drift != 0)
 | 
			
		||||
            {
 | 
			
		||||
                TryAddCell(curCellCount);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Increments the counter by 'value'.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Add(int value)
 | 
			
		||||
        {
 | 
			
		||||
            int curCellCount = this.cellCount;
 | 
			
		||||
            var drift = add(ref GetCountRef(curCellCount), value);
 | 
			
		||||
 | 
			
		||||
            if (drift != 0)
 | 
			
		||||
            {
 | 
			
		||||
                TryAddCell(curCellCount);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        private ref int GetCountRef(int curCellCount)
 | 
			
		||||
        {
 | 
			
		||||
            ref var countRef = ref count;
 | 
			
		||||
 | 
			
		||||
            Cell[]? cells;
 | 
			
		||||
            if ((cells = this.cells) != null && curCellCount > 1)
 | 
			
		||||
            {
 | 
			
		||||
                var cell = cells[GetIndex((uint)curCellCount)];
 | 
			
		||||
                if (cell != null)
 | 
			
		||||
                {
 | 
			
		||||
                    countRef = ref cell.counter.count;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return ref countRef;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static int increment(ref int val)
 | 
			
		||||
        {
 | 
			
		||||
            return -val - 1 + Interlocked.Increment(ref val);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static int add(ref int val, int inc)
 | 
			
		||||
        {
 | 
			
		||||
            return -val - inc + Interlocked.Add(ref val, inc);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static int decrement(ref int val)
 | 
			
		||||
        {
 | 
			
		||||
            return val - 1 - Interlocked.Decrement(ref val);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void TryAddCell(int curCellCount)
 | 
			
		||||
        {
 | 
			
		||||
            if (curCellCount < s_MaxCellCount)
 | 
			
		||||
            {
 | 
			
		||||
                TryAddCellCore(curCellCount);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.NoInlining)]
 | 
			
		||||
        private void TryAddCellCore(int curCellCount)
 | 
			
		||||
        {
 | 
			
		||||
            var cells = this.cells;
 | 
			
		||||
            if (cells == null)
 | 
			
		||||
            {
 | 
			
		||||
                var newCells = new Cell[s_MaxCellCount];
 | 
			
		||||
                cells = Interlocked.CompareExchange(ref this.cells, newCells, null) ?? newCells;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (cells[curCellCount] == null)
 | 
			
		||||
            {
 | 
			
		||||
                Interlocked.CompareExchange(ref cells[curCellCount], new Cell(), null);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.cellCount == curCellCount)
 | 
			
		||||
            {
 | 
			
		||||
                Interlocked.CompareExchange(ref this.cellCount, curCellCount + 1, curCellCount);
 | 
			
		||||
                //if (Interlocked.CompareExchange(ref this.cellCount, curCellCount + 1, curCellCount) == curCellCount)
 | 
			
		||||
                //{
 | 
			
		||||
                //    System.Console.WriteLine(curCellCount + 1);
 | 
			
		||||
                //}
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,203 @@
 | 
			
		||||
// Copyright (c) Vladimir Sadov. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is distributed under the MIT License. See LICENSE.md for details.
 | 
			
		||||
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
namespace System.Collections.Concurrent
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Scalable 64bit counter that can be used from multiple threads.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public sealed class Counter64 : CounterBase
 | 
			
		||||
    {
 | 
			
		||||
        private class Cell
 | 
			
		||||
        {
 | 
			
		||||
            [StructLayout(LayoutKind.Explicit, Size = CACHE_LINE * 2 - OBJ_HEADER_SIZE)]
 | 
			
		||||
            public struct SpacedCounter
 | 
			
		||||
            {
 | 
			
		||||
                [FieldOffset(CACHE_LINE - OBJ_HEADER_SIZE)]
 | 
			
		||||
                public long count;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public SpacedCounter counter;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // spaced out counters
 | 
			
		||||
        private Cell[]? cells;
 | 
			
		||||
 | 
			
		||||
        // default counter
 | 
			
		||||
        private long count;
 | 
			
		||||
 | 
			
		||||
        // delayed count
 | 
			
		||||
        private long lastCount;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Returns the value of the counter at the time of the call.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// The value may miss in-progress updates if the counter is being concurrently modified.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        public long Value
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                var count = this.count;
 | 
			
		||||
                var cells = this.cells;
 | 
			
		||||
 | 
			
		||||
                if (cells != null)
 | 
			
		||||
                {
 | 
			
		||||
                    for (int i = 0; i < cells.Length; i++)
 | 
			
		||||
                    {
 | 
			
		||||
                        var cell = cells[i];
 | 
			
		||||
                        if (cell != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            count += cell.counter.count;
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return count;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Returns the approximate value of the counter at the time of the call.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// EstimatedValue could be significantly cheaper to obtain, but may be slightly delayed.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        public long EstimatedValue
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                if (this.cellCount == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    return Value;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var curTicks = (uint)Environment.TickCount;
 | 
			
		||||
                // more than a millisecond passed?
 | 
			
		||||
                if (curTicks != lastCountTicks)
 | 
			
		||||
                {
 | 
			
		||||
                    lastCountTicks = curTicks;
 | 
			
		||||
                    lastCount = Value;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return lastCount;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Increments the counter by 1.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Increment()
 | 
			
		||||
        {
 | 
			
		||||
            int curCellCount = this.cellCount;
 | 
			
		||||
            var drift = increment(ref GetCountRef(curCellCount));
 | 
			
		||||
 | 
			
		||||
            if (drift != 0)
 | 
			
		||||
            {
 | 
			
		||||
                TryAddCell(curCellCount);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Decrements the counter by 1.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Decrement()
 | 
			
		||||
        {
 | 
			
		||||
            int curCellCount = this.cellCount;
 | 
			
		||||
            var drift = decrement(ref GetCountRef(curCellCount));
 | 
			
		||||
 | 
			
		||||
            if (drift != 0)
 | 
			
		||||
            {
 | 
			
		||||
                TryAddCell(curCellCount);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Increments the counter by 'value'.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Add(int value)
 | 
			
		||||
        {
 | 
			
		||||
            int curCellCount = this.cellCount;
 | 
			
		||||
            var drift = add(ref GetCountRef(curCellCount), value);
 | 
			
		||||
 | 
			
		||||
            if (drift != 0)
 | 
			
		||||
            {
 | 
			
		||||
                TryAddCell(curCellCount);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        private ref long GetCountRef(int curCellCount)
 | 
			
		||||
        {
 | 
			
		||||
            ref var countRef = ref count;
 | 
			
		||||
 | 
			
		||||
            Cell[]? cells;
 | 
			
		||||
            if ((cells = this.cells) != null && curCellCount > 1)
 | 
			
		||||
            {
 | 
			
		||||
                var cell = cells[GetIndex((uint)curCellCount)];
 | 
			
		||||
                if (cell != null)
 | 
			
		||||
                {
 | 
			
		||||
                    countRef = ref cell.counter.count;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return ref countRef;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static long increment(ref long val)
 | 
			
		||||
        {
 | 
			
		||||
            return -val - 1 + Interlocked.Increment(ref val);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static long add(ref long val, int inc)
 | 
			
		||||
        {
 | 
			
		||||
            return -val - inc + Interlocked.Add(ref val, inc);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static long decrement(ref long val)
 | 
			
		||||
        {
 | 
			
		||||
            return val - 1 - Interlocked.Decrement(ref val);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void TryAddCell(int curCellCount)
 | 
			
		||||
        {
 | 
			
		||||
            if (curCellCount < s_MaxCellCount)
 | 
			
		||||
            {
 | 
			
		||||
                TryAddCellCore(curCellCount);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void TryAddCellCore(int curCellCount)
 | 
			
		||||
        {
 | 
			
		||||
            var cells = this.cells;
 | 
			
		||||
            if (cells == null)
 | 
			
		||||
            {
 | 
			
		||||
                var newCells = new Cell[s_MaxCellCount];
 | 
			
		||||
                cells = Interlocked.CompareExchange(ref this.cells, newCells, null) ?? newCells;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (cells[curCellCount] == null)
 | 
			
		||||
            {
 | 
			
		||||
                Interlocked.CompareExchange(ref cells[curCellCount], new Cell(), null);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.cellCount == curCellCount)
 | 
			
		||||
            {
 | 
			
		||||
                Interlocked.CompareExchange(ref this.cellCount, curCellCount + 1, curCellCount);
 | 
			
		||||
                //if (Interlocked.CompareExchange(ref this.cellCount, curCellCount + 1, curCellCount) == curCellCount)
 | 
			
		||||
                //{
 | 
			
		||||
                //    System.Console.WriteLine(curCellCount + 1);
 | 
			
		||||
                //}
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,38 @@
 | 
			
		||||
// Copyright (c) Vladimir Sadov. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is distributed under the MIT License. See LICENSE.md for details.
 | 
			
		||||
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
 | 
			
		||||
namespace System.Collections.Concurrent
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Scalable counter base.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class CounterBase
 | 
			
		||||
    {
 | 
			
		||||
        private protected const int CACHE_LINE = 64;
 | 
			
		||||
        private protected const int OBJ_HEADER_SIZE = 8;
 | 
			
		||||
 | 
			
		||||
        private protected static readonly int s_MaxCellCount = Util.AlignToPowerOfTwo(Environment.ProcessorCount) + 1;
 | 
			
		||||
 | 
			
		||||
        // how many cells we have
 | 
			
		||||
        private protected int cellCount;
 | 
			
		||||
 | 
			
		||||
        // delayed count time
 | 
			
		||||
        private protected uint lastCountTicks;
 | 
			
		||||
 | 
			
		||||
        private protected CounterBase()
 | 
			
		||||
        {
 | 
			
		||||
            // touch static
 | 
			
		||||
            _ = s_MaxCellCount;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        private protected unsafe static int GetIndex(uint cellCount)
 | 
			
		||||
        {
 | 
			
		||||
            nuint addr = (nuint)(&cellCount);
 | 
			
		||||
            return (int)(addr % cellCount);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace System.Collections.Concurrent
 | 
			
		||||
{
 | 
			
		||||
    internal static class Util
 | 
			
		||||
    {
 | 
			
		||||
        // returns 2^x >= size
 | 
			
		||||
        internal static int AlignToPowerOfTwo(int size)
 | 
			
		||||
        {
 | 
			
		||||
            Debug.Assert(size > 0);
 | 
			
		||||
 | 
			
		||||
            size--;
 | 
			
		||||
            size |= size >> 1;
 | 
			
		||||
            size |= size >> 2;
 | 
			
		||||
            size |= size >> 4;
 | 
			
		||||
            size |= size >> 8;
 | 
			
		||||
            size |= size >> 16;
 | 
			
		||||
            return size + 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -44,7 +44,7 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
 | 
			
		||||
    private readonly ConcurrentQueue<Item> _free2 = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>借出去的放在这</summary>
 | 
			
		||||
    private readonly ConcurrentDictionary<T, Item> _busy = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<T, Item> _busy = new();
 | 
			
		||||
 | 
			
		||||
    //private readonly Object SyncRoot = new();
 | 
			
		||||
    #endregion
 | 
			
		||||
 
 | 
			
		||||
@@ -5,30 +5,103 @@ namespace ThingsGateway.NewLife;
 | 
			
		||||
 | 
			
		||||
public class ExpiringDictionary<TKey, TValue> : IDisposable
 | 
			
		||||
{
 | 
			
		||||
    private ConcurrentDictionary<TKey, TValue> _dict = new();
 | 
			
		||||
    private readonly TimerX _cleanupTimer;
 | 
			
		||||
 | 
			
		||||
    public ExpiringDictionary(int cleanupInterval = 60000)
 | 
			
		||||
    /// <summary>缓存项</summary>
 | 
			
		||||
    public class CacheItem
 | 
			
		||||
    {
 | 
			
		||||
        _cleanupTimer = new TimerX(Clear, null, cleanupInterval, cleanupInterval) { Async = true };
 | 
			
		||||
        private TValue? _value;
 | 
			
		||||
        /// <summary>数值</summary>
 | 
			
		||||
        public TValue? Value { get => _value; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>过期时间。系统启动以来的毫秒数</summary>
 | 
			
		||||
        public Int64 ExpiredTime { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>是否过期</summary>
 | 
			
		||||
        public Boolean Expired => ExpiredTime <= Runtime.TickCount64;
 | 
			
		||||
 | 
			
		||||
        /// <summary>访问时间</summary>
 | 
			
		||||
        public Int64 VisitTime { get; private set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>构造缓存项</summary>
 | 
			
		||||
        /// <param name="value"></param>
 | 
			
		||||
        /// <param name="expire"></param>
 | 
			
		||||
        public CacheItem(TValue? value, Int32 expire) => Set(value, expire);
 | 
			
		||||
 | 
			
		||||
        /// <summary>设置数值和过期时间</summary>
 | 
			
		||||
        /// <param name="value"></param>
 | 
			
		||||
        /// <param name="expire">过期时间,秒</param>
 | 
			
		||||
        public void Set(TValue value, Int32 expire)
 | 
			
		||||
        {
 | 
			
		||||
            _value = value;
 | 
			
		||||
 | 
			
		||||
            var now = VisitTime = Runtime.TickCount64;
 | 
			
		||||
            if (expire <= 0)
 | 
			
		||||
                ExpiredTime = Int64.MaxValue;
 | 
			
		||||
            else
 | 
			
		||||
                ExpiredTime = now + expire * 1000L;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>更新访问时间并返回数值</summary>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public TValue? Visit()
 | 
			
		||||
        {
 | 
			
		||||
            VisitTime = Runtime.TickCount64;
 | 
			
		||||
            var rs = _value;
 | 
			
		||||
            if (rs == null) return default;
 | 
			
		||||
 | 
			
		||||
            return rs;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private NonBlockingDictionary<TKey, CacheItem> _dict;
 | 
			
		||||
 | 
			
		||||
    private readonly TimerX _cleanupTimer;
 | 
			
		||||
    private int defaultExpire = 60;
 | 
			
		||||
    IEqualityComparer<TKey>? comparer;
 | 
			
		||||
    public ExpiringDictionary(int expire = 60, IEqualityComparer<TKey>? comparer = null)
 | 
			
		||||
    {
 | 
			
		||||
        defaultExpire = expire;
 | 
			
		||||
        this.comparer = comparer;
 | 
			
		||||
        _dict = new NonBlockingDictionary<TKey, CacheItem>(comparer);
 | 
			
		||||
 | 
			
		||||
        _cleanupTimer = new TimerX(TimerClear, null, 10000, 10000) { Async = true };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void TryAdd(TKey key, TValue value)
 | 
			
		||||
    public bool TryAdd(TKey key, TValue value)
 | 
			
		||||
    {
 | 
			
		||||
        _dict.TryAdd(key, value);
 | 
			
		||||
        if (_dict.TryGetValue(key, out var item))
 | 
			
		||||
        {
 | 
			
		||||
            if (!item.Expired) return false;
 | 
			
		||||
            item.Set(value, defaultExpire);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return _dict.TryAdd(key, new CacheItem(value, defaultExpire));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool TryGetValue(TKey key, out TValue value)
 | 
			
		||||
    {
 | 
			
		||||
        return _dict.TryGetValue(key, out value);
 | 
			
		||||
        value = default;
 | 
			
		||||
 | 
			
		||||
        // 没有值,直接结束
 | 
			
		||||
        if (!_dict.TryGetValue(key, out var item) || item == null) return false;
 | 
			
		||||
 | 
			
		||||
        // 得到已有值
 | 
			
		||||
        value = item.Visit();
 | 
			
		||||
 | 
			
		||||
        // 是否未过期的有效值
 | 
			
		||||
        return !item.Expired;
 | 
			
		||||
    }
 | 
			
		||||
    public TValue GetOrAdd(TKey key, Func<TKey, TValue> func)
 | 
			
		||||
    {
 | 
			
		||||
        return _dict.GetOrAdd(key, func);
 | 
			
		||||
    }
 | 
			
		||||
    public TValue GetOrAdd(TKey key, TValue value)
 | 
			
		||||
    {
 | 
			
		||||
        return _dict.GetOrAdd(key, value);
 | 
			
		||||
        CacheItem? item = null;
 | 
			
		||||
        do
 | 
			
		||||
        {
 | 
			
		||||
            if (_dict.TryGetValue(key, out item) && item != null) return item.Visit();
 | 
			
		||||
 | 
			
		||||
            item ??= new CacheItem(func(key), defaultExpire);
 | 
			
		||||
        }
 | 
			
		||||
        while (!_dict.TryAdd(key, item));
 | 
			
		||||
 | 
			
		||||
        return item.Visit();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool TryRemove(TKey key) => _dict.TryRemove(key, out _);
 | 
			
		||||
@@ -38,13 +111,38 @@ public class ExpiringDictionary<TKey, TValue> : IDisposable
 | 
			
		||||
    private void Clear(object? state)
 | 
			
		||||
    {
 | 
			
		||||
        var data = _dict;
 | 
			
		||||
        _dict = new();
 | 
			
		||||
        _dict = new(comparer);
 | 
			
		||||
        data.Clear();
 | 
			
		||||
    }
 | 
			
		||||
    private void TimerClear(object? state)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        var dic = _dict;
 | 
			
		||||
        if (dic.IsEmpty) return;
 | 
			
		||||
 | 
			
		||||
        // 60分钟之内过期的数据,进入LRU淘汰
 | 
			
		||||
        var now = Runtime.TickCount64;
 | 
			
		||||
 | 
			
		||||
        // 这里先计算,性能很重要
 | 
			
		||||
        var toDels = new List<TKey>();
 | 
			
		||||
        foreach (var item in dic)
 | 
			
		||||
        {
 | 
			
		||||
            // 已过期,准备删除
 | 
			
		||||
            var ci = item.Value;
 | 
			
		||||
            if (ci.ExpiredTime <= now)
 | 
			
		||||
                toDels.Add(item.Key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 确认删除
 | 
			
		||||
        foreach (var item in toDels)
 | 
			
		||||
        {
 | 
			
		||||
            _dict.Remove(item);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        _dict.Clear();
 | 
			
		||||
        _cleanupTimer.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,8 @@ public class FastMapperOption
 | 
			
		||||
public static class FastMapper
 | 
			
		||||
{
 | 
			
		||||
    // 泛型 + 非泛型共用缓存
 | 
			
		||||
    private static readonly ConcurrentDictionary<(Type Source, Type Target), Delegate> _mapCache
 | 
			
		||||
        = new ConcurrentDictionary<(Type, Type), Delegate>();
 | 
			
		||||
    private static readonly NonBlockingDictionary<(Type Source, Type Target), Delegate> _mapCache
 | 
			
		||||
        = new NonBlockingDictionary<(Type, Type), Delegate>();
 | 
			
		||||
 | 
			
		||||
    #region 泛型入口
 | 
			
		||||
    public static TTarget Mapper<TSource, TTarget>(TSource source, FastMapperOption option = null)
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,6 @@
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.NewLife.Log;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.NewLife;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -168,8 +168,8 @@ public class CompositeConfigProvider : IConfigProvider
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    #region 绑定
 | 
			
		||||
    private readonly ConcurrentDictionary<Object, String> _models = [];
 | 
			
		||||
    private readonly ConcurrentDictionary<Object, ModelWrap> _models2 = [];
 | 
			
		||||
    private readonly NonBlockingDictionary<Object, String> _models = [];
 | 
			
		||||
    private readonly NonBlockingDictionary<Object, ModelWrap> _models2 = [];
 | 
			
		||||
    /// <summary>绑定模型,使能热更新,配置存储数据改变时同步修改模型属性</summary>
 | 
			
		||||
    /// <typeparam name="T">模型。可通过实现IConfigMapping接口来自定义映射配置到模型实例</typeparam>
 | 
			
		||||
    /// <param name="model">模型实例</param>
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ public static class DictionaryExtensions
 | 
			
		||||
    /// <param name="dict"></param>
 | 
			
		||||
    /// <param name="key"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static Boolean Remove<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> dict, TKey key) where TKey : notnull => dict.TryRemove(key, out _);
 | 
			
		||||
    public static Boolean Remove<TKey, TValue>(this NonBlockingDictionary<TKey, TValue> dict, TKey key) where TKey : notnull => dict.TryRemove(key, out _);
 | 
			
		||||
 | 
			
		||||
#if !NET6_0_OR_GREATER
 | 
			
		||||
    public static bool TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> pairs, TKey key, TValue value)
 | 
			
		||||
@@ -77,7 +77,7 @@ public static class DictionaryExtensions
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 批量出队
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static List<T> ToListWithDequeue<TKEY, T>(this ConcurrentDictionary<TKEY, T> values, int maxCount = 0)
 | 
			
		||||
    public static List<T> ToListWithDequeue<TKEY, T>(this NonBlockingDictionary<TKEY, T> values, int maxCount = 0)
 | 
			
		||||
    {
 | 
			
		||||
        if (maxCount <= 0)
 | 
			
		||||
        {
 | 
			
		||||
@@ -105,7 +105,7 @@ public static class DictionaryExtensions
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 批量出队
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static Dictionary<TKEY, T> ToDictWithDequeue<TKEY, T>(this ConcurrentDictionary<TKEY, T> values, int maxCount = 0)
 | 
			
		||||
    public static Dictionary<TKEY, T> ToDictWithDequeue<TKEY, T>(this NonBlockingDictionary<TKEY, T> values, int maxCount = 0)
 | 
			
		||||
    {
 | 
			
		||||
        if (maxCount <= 0)
 | 
			
		||||
        {
 | 
			
		||||
@@ -135,7 +135,7 @@ public static class DictionaryExtensions
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 批量出队
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static IEnumerable<KeyValuePair<TKEY, T>> ToIEnumerableKVWithDequeue<TKEY, T>(this ConcurrentDictionary<TKEY, T> values, int maxCount = 0)
 | 
			
		||||
    public static IEnumerable<KeyValuePair<TKEY, T>> ToIEnumerableKVWithDequeue<TKEY, T>(this NonBlockingDictionary<TKEY, T> values, int maxCount = 0)
 | 
			
		||||
    {
 | 
			
		||||
        if (values.IsEmpty) yield break;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,7 @@ public static class LinqExtensions
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 从并发字典中删除
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static bool Remove<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> dict, TKey key) where TKey : notnull
 | 
			
		||||
    public static bool Remove<TKey, TValue>(this NonBlockingDictionary<TKey, TValue> dict, TKey key) where TKey : notnull
 | 
			
		||||
    {
 | 
			
		||||
        return dict.TryRemove(key, out TValue? _);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,6 @@
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.NewLife;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
 
 | 
			
		||||
@@ -463,7 +463,7 @@ public static class ProcessHelper
 | 
			
		||||
                StandardOutputEncoding = outputEncoding,
 | 
			
		||||
                StandardErrorEncoding = outputEncoding,
 | 
			
		||||
            };
 | 
			
		||||
            var process = Process.Start(psi);
 | 
			
		||||
            using var process = Process.Start(psi);
 | 
			
		||||
            if (process == null) return null;
 | 
			
		||||
 | 
			
		||||
            if (msWait > 0 && !process.WaitForExit(msWait))
 | 
			
		||||
 
 | 
			
		||||
@@ -97,7 +97,7 @@ public class ConsoleLog : Logger
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static readonly ConcurrentDictionary<Int32, ConsoleColor> dic = new();
 | 
			
		||||
    static readonly NonBlockingDictionary<Int32, ConsoleColor> dic = new();
 | 
			
		||||
    static readonly ConsoleColor[] colors = [
 | 
			
		||||
        ConsoleColor.Green, ConsoleColor.Cyan, ConsoleColor.Magenta, ConsoleColor.White, ConsoleColor.Yellow,
 | 
			
		||||
        ConsoleColor.DarkGreen, ConsoleColor.DarkCyan, ConsoleColor.DarkMagenta, ConsoleColor.DarkRed, ConsoleColor.DarkYellow ];
 | 
			
		||||
 
 | 
			
		||||
@@ -109,7 +109,7 @@ public class DefaultTracer : DisposeBase, ITracer, ILogFeature
 | 
			
		||||
    public IPool<ISpan> SpanPool => _SpanPool ??= new MySpanPool();
 | 
			
		||||
 | 
			
		||||
    /// <summary>Span构建器集合</summary>
 | 
			
		||||
    protected ConcurrentDictionary<String, ISpanBuilder> _builders = new();
 | 
			
		||||
    protected NonBlockingDictionary<String, ISpanBuilder> _builders = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>采样定时器</summary>
 | 
			
		||||
    protected TimerX? _timer;
 | 
			
		||||
@@ -292,7 +292,7 @@ public class DefaultTracer : DisposeBase, ITracer, ILogFeature
 | 
			
		||||
        var bs = _builders;
 | 
			
		||||
        if (bs.IsEmpty) return [];
 | 
			
		||||
 | 
			
		||||
        _builders = new ConcurrentDictionary<String, ISpanBuilder>();
 | 
			
		||||
        _builders = new NonBlockingDictionary<String, ISpanBuilder>();
 | 
			
		||||
 | 
			
		||||
        var bs2 = bs.Values.Where(e => e.Total > 0).ToArray();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ public class TextFileLog : Logger, IDisposable
 | 
			
		||||
        _Timer = new TimerX(DoWriteAndClose, null, 0_000, 5_000) { Async = true };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static readonly ConcurrentDictionary<String, TextFileLog> cache = new(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
    private static readonly NonBlockingDictionary<String, TextFileLog> cache = new(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
    /// <summary>每个目录的日志实例应该只有一个,所以采用静态创建</summary>
 | 
			
		||||
    /// <param name="path">日志目录或日志文件路径</param>
 | 
			
		||||
    /// <param name="fileFormat"></param>
 | 
			
		||||
@@ -257,7 +257,7 @@ public class TextFileLog : Logger, IDisposable
 | 
			
		||||
                {
 | 
			
		||||
                    // 删除最旧的文件
 | 
			
		||||
                    var retain = fis.Length - Backups;
 | 
			
		||||
                    fis = fis.OrderBy(e => e.CreationTime).Take(retain).ToArray();
 | 
			
		||||
                    fis = fis.OrderBy(e => e.LastWriteTimeUtc).Take(retain).ToArray();
 | 
			
		||||
                    foreach (var item in fis)
 | 
			
		||||
                    {
 | 
			
		||||
                        OnWrite(LogLevel.Info, "The log file has reached the maximum limit of {0}, delete {1}, size {2: n0} Byte", Backups, item.Name, item.Length);
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ public interface IEventHandler<TEvent>
 | 
			
		||||
/// </remarks>
 | 
			
		||||
public class EventBus<TEvent> : DisposeBase, IEventBus<TEvent>
 | 
			
		||||
{
 | 
			
		||||
    private readonly ConcurrentDictionary<String, IEventHandler<TEvent>> _handlers = [];
 | 
			
		||||
    private readonly NonBlockingDictionary<String, IEventHandler<TEvent>> _handlers = [];
 | 
			
		||||
    /// <summary>已订阅的事件处理器集合</summary>
 | 
			
		||||
    public IDictionary<String, IEventHandler<TEvent>> Handlers => _handlers;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,9 @@ public class DeferredQueue : DisposeBase
 | 
			
		||||
    /// <summary>名称</summary>
 | 
			
		||||
    public String Name { get; set; }
 | 
			
		||||
 | 
			
		||||
    private volatile ConcurrentDictionary<String, Object> _Entities = new();
 | 
			
		||||
    private volatile NonBlockingDictionary<String, Object> _Entities = new();
 | 
			
		||||
    /// <summary>实体字典</summary>
 | 
			
		||||
    public ConcurrentDictionary<String, Object> Entities => _Entities;
 | 
			
		||||
    public NonBlockingDictionary<String, Object> Entities => _Entities;
 | 
			
		||||
 | 
			
		||||
    /// <summary>跟踪数。达到该值时输出跟踪日志,默认1000</summary>
 | 
			
		||||
    public Int32 TraceCount { get; set; } = 1000;
 | 
			
		||||
@@ -206,7 +206,7 @@ public class DeferredQueue : DisposeBase
 | 
			
		||||
        var es = _Entities;
 | 
			
		||||
        if (es.IsEmpty) return;
 | 
			
		||||
 | 
			
		||||
        _Entities = new ConcurrentDictionary<String, Object>();
 | 
			
		||||
        _Entities = new NonBlockingDictionary<String, Object>();
 | 
			
		||||
        var times = _Times;
 | 
			
		||||
 | 
			
		||||
        Interlocked.Add(ref _count, -es.Count);
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ class MyServiceScope : IServiceScope, IServiceProvider
 | 
			
		||||
 | 
			
		||||
    public IServiceProvider ServiceProvider => this;
 | 
			
		||||
 | 
			
		||||
    private readonly ConcurrentDictionary<Type, Object?> _cache = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<Type, Object?> _cache = new();
 | 
			
		||||
 | 
			
		||||
    public void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ public class DnsResolver : IDnsResolver
 | 
			
		||||
    /// <summary>缓存超时时间</summary>
 | 
			
		||||
    public TimeSpan Expire { set; get; } = TimeSpan.FromMinutes(5);
 | 
			
		||||
 | 
			
		||||
    private readonly ConcurrentDictionary<String, DnsItem> _cache = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<String, DnsItem> _cache = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>解析域名</summary>
 | 
			
		||||
    /// <param name="host"></param>
 | 
			
		||||
 
 | 
			
		||||
@@ -146,7 +146,7 @@ public class NetServer : DisposeBase, IServer, IExtend, ILogFeature
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    public IServiceProvider? ServiceProvider { get; set; }
 | 
			
		||||
 | 
			
		||||
    private ConcurrentDictionary<String, Object?>? _items;
 | 
			
		||||
    private NonBlockingDictionary<String, Object?>? _items;
 | 
			
		||||
    /// <summary>数据项</summary>
 | 
			
		||||
    public IDictionary<String, Object?> Items => _items ??= new();
 | 
			
		||||
 | 
			
		||||
@@ -486,7 +486,7 @@ public class NetServer : DisposeBase, IServer, IExtend, ILogFeature
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    #region 会话
 | 
			
		||||
    private readonly ConcurrentDictionary<Int32, INetSession> _Sessions = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<Int32, INetSession> _Sessions = new();
 | 
			
		||||
    /// <summary>会话集合。用自增的数字ID作为标识,业务应用自己维持ID与业务主键的对应关系。</summary>
 | 
			
		||||
    public IDictionary<Int32, INetSession> Sessions => _Sessions;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -853,7 +853,7 @@ public abstract class SessionBase : DisposeBase, ISocketClient, ITransport, ILog
 | 
			
		||||
    #endregion 异常处理
 | 
			
		||||
 | 
			
		||||
    #region 扩展接口
 | 
			
		||||
    private ConcurrentDictionary<String, Object?>? _items;
 | 
			
		||||
    private NonBlockingDictionary<String, Object?>? _items;
 | 
			
		||||
    /// <summary>数据项</summary>
 | 
			
		||||
    public IDictionary<String, Object?> Items => _items ??= new();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ namespace ThingsGateway.NewLife.Net;
 | 
			
		||||
internal class SessionCollection : DisposeBase, IDictionary<String, ISocketSession>
 | 
			
		||||
{
 | 
			
		||||
    #region 属性
 | 
			
		||||
    private readonly ConcurrentDictionary<String, ISocketSession> _dic = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<String, ISocketSession> _dic = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>服务端</summary>
 | 
			
		||||
    public ISocketServer Server { get; private set; }
 | 
			
		||||
 
 | 
			
		||||
@@ -400,7 +400,7 @@ public class UdpSession : DisposeBase, ISocketSession, ITransport, ILogFeature
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    #region 扩展接口
 | 
			
		||||
    private ConcurrentDictionary<String, Object?>? _items;
 | 
			
		||||
    private NonBlockingDictionary<String, Object?>? _items;
 | 
			
		||||
    /// <summary>数据项</summary>
 | 
			
		||||
    public IDictionary<String, Object?> Items => _items ??= new();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -212,7 +212,7 @@ public class FullRedis : Redis
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ConcurrentDictionary<String, IPool<RedisClient>> _pools = new();
 | 
			
		||||
    private NonBlockingDictionary<String, IPool<RedisClient>> _pools = new();
 | 
			
		||||
    /// <summary>获取指定节点的连接池</summary>
 | 
			
		||||
    /// <param name="node"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
 
 | 
			
		||||
@@ -1106,10 +1106,10 @@ public class RedisClient : DisposeBase
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    #region 辅助
 | 
			
		||||
    private static readonly ConcurrentDictionary<String, String> _cache0 = new();
 | 
			
		||||
    private static readonly ConcurrentDictionary<String, String> _cache1 = new();
 | 
			
		||||
    private static readonly ConcurrentDictionary<String, String> _cache2 = new();
 | 
			
		||||
    private static readonly ConcurrentDictionary<String, String> _cache3 = new();
 | 
			
		||||
    private static readonly NonBlockingDictionary<String, String> _cache0 = new();
 | 
			
		||||
    private static readonly NonBlockingDictionary<String, String> _cache1 = new();
 | 
			
		||||
    private static readonly NonBlockingDictionary<String, String> _cache2 = new();
 | 
			
		||||
    private static readonly NonBlockingDictionary<String, String> _cache3 = new();
 | 
			
		||||
    /// <summary>获取命令对应的字节数组,全局缓存</summary>
 | 
			
		||||
    /// <param name="cmd"></param>
 | 
			
		||||
    /// <param name="args"></param>
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,7 @@ public class AssemblyX
 | 
			
		||||
    #region 构造
 | 
			
		||||
    private AssemblyX(Assembly asm) => Asm = asm;
 | 
			
		||||
 | 
			
		||||
    private static readonly ConcurrentDictionary<Assembly, AssemblyX> cache = new();
 | 
			
		||||
    private static readonly NonBlockingDictionary<Assembly, AssemblyX> cache = new();
 | 
			
		||||
    /// <summary>创建程序集辅助对象</summary>
 | 
			
		||||
    /// <param name="asm"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
@@ -234,7 +234,7 @@ public class AssemblyX
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    #region 方法
 | 
			
		||||
    private readonly ConcurrentDictionary<String, Type?> typeCache2 = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<String, Type?> typeCache2 = new();
 | 
			
		||||
    /// <summary>从程序集中查找指定名称的类型</summary>
 | 
			
		||||
    /// <param name="typeName"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
@@ -268,7 +268,7 @@ public class AssemblyX
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    #region 插件
 | 
			
		||||
    private readonly ConcurrentDictionary<Type, List<Type>> _plugins = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<Type, List<Type>> _plugins = new();
 | 
			
		||||
    /// <summary>查找插件,带缓存</summary>
 | 
			
		||||
    /// <param name="baseType">类型</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ namespace ThingsGateway.NewLife;
 | 
			
		||||
public static class AttributeX
 | 
			
		||||
{
 | 
			
		||||
    #region 静态方法
 | 
			
		||||
    private static readonly ConcurrentDictionary<String, Object> _asmCache = new();
 | 
			
		||||
    private static readonly NonBlockingDictionary<String, Object> _asmCache = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>获取自定义属性,带有缓存功能,避免因.Net内部GetCustomAttributes没有缓存而带来的损耗</summary>
 | 
			
		||||
    /// <typeparam name="TAttribute"></typeparam>
 | 
			
		||||
 
 | 
			
		||||
@@ -317,8 +317,8 @@ public class DefaultReflect : IReflect
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    #region 反射获取 字段/属性
 | 
			
		||||
    private readonly ConcurrentDictionary<Type, IList<FieldInfo>> _cache1 = new();
 | 
			
		||||
    private readonly ConcurrentDictionary<Type, IList<FieldInfo>> _cache2 = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<Type, IList<FieldInfo>> _cache1 = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<Type, IList<FieldInfo>> _cache2 = new();
 | 
			
		||||
    /// <summary>获取字段</summary>
 | 
			
		||||
    /// <param name="type"></param>
 | 
			
		||||
    /// <param name="baseFirst"></param>
 | 
			
		||||
@@ -353,8 +353,8 @@ public class DefaultReflect : IReflect
 | 
			
		||||
        return list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private readonly ConcurrentDictionary<Type, IList<PropertyInfo>> _cache3 = new();
 | 
			
		||||
    private readonly ConcurrentDictionary<Type, IList<PropertyInfo>> _cache4 = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<Type, IList<PropertyInfo>> _cache3 = new();
 | 
			
		||||
    private readonly NonBlockingDictionary<Type, IList<PropertyInfo>> _cache4 = new();
 | 
			
		||||
    /// <summary>获取属性</summary>
 | 
			
		||||
    /// <param name="type"></param>
 | 
			
		||||
    /// <param name="baseFirst"></param>
 | 
			
		||||
@@ -805,7 +805,7 @@ public class DefaultReflect : IReflect
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    #region 插件
 | 
			
		||||
    //private readonly ConcurrentDictionary<Type, ConcurrentDictionary<Type, Boolean>> _as_cache = new ConcurrentDictionary<Type, ConcurrentDictionary<Type, Boolean>>();
 | 
			
		||||
    //private readonly NonBlockingDictionary<Type, NonBlockingDictionary<Type, Boolean>> _as_cache = new NonBlockingDictionary<Type, NonBlockingDictionary<Type, Boolean>>();
 | 
			
		||||
    /// <summary>是否子类</summary>
 | 
			
		||||
    /// <param name="type"></param>
 | 
			
		||||
    /// <param name="baseType"></param>
 | 
			
		||||
@@ -832,7 +832,7 @@ public class DefaultReflect : IReflect
 | 
			
		||||
        //var key = $"{type.FullName}_{baseType.FullName}";
 | 
			
		||||
        //if (!_as_cache.TryGetValue(type, out var dic))
 | 
			
		||||
        //{
 | 
			
		||||
        //    dic = new ConcurrentDictionary<Type, Boolean>();
 | 
			
		||||
        //    dic = new NonBlockingDictionary<Type, Boolean>();
 | 
			
		||||
        //    _as_cache.TryAdd(type, dic);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -561,7 +561,7 @@ public static class Reflect
 | 
			
		||||
    /// <param name="method"></param>
 | 
			
		||||
    /// <param name="target"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static TFunc? As<TFunc>(this MethodInfo method, object? target = null)
 | 
			
		||||
    public static TFunc? As<TFunc>(this MethodInfo method, object? target = null) where TFunc : class
 | 
			
		||||
    {
 | 
			
		||||
        if (method == null) return default;
 | 
			
		||||
 | 
			
		||||
@@ -569,10 +569,14 @@ public static class Reflect
 | 
			
		||||
 | 
			
		||||
        var func = DelegateCache<TFunc>.Cache.GetOrAdd(
 | 
			
		||||
             key,
 | 
			
		||||
             _ => (TFunc)(object)(
 | 
			
		||||
             _ =>
 | 
			
		||||
             {
 | 
			
		||||
                 return (
 | 
			
		||||
                     target == null
 | 
			
		||||
                         ? Delegate.CreateDelegate(typeof(TFunc), method, true)
 | 
			
		||||
                         : Delegate.CreateDelegate(typeof(TFunc), target, method, true)));
 | 
			
		||||
                         ? Delegate.CreateDelegate(typeof(TFunc), method, true) as TFunc
 | 
			
		||||
                         : Delegate.CreateDelegate(typeof(TFunc), target, method, true) as TFunc
 | 
			
		||||
                 );
 | 
			
		||||
             });
 | 
			
		||||
 | 
			
		||||
        return func;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -111,7 +111,7 @@ public class ScriptEngine
 | 
			
		||||
        IsExpression = isExpression;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static readonly ConcurrentDictionary<String, ScriptEngine> _cache = new(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
    static readonly NonBlockingDictionary<String, ScriptEngine> _cache = new(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
    /// <summary>为指定代码片段创建脚本引擎实例。采用缓存,避免同一脚本重复创建引擎。</summary>
 | 
			
		||||
    /// <param name="code">代码片段</param>
 | 
			
		||||
    /// <param name="isExpression">是否表达式,表达式将编译成为一个Main方法</param>
 | 
			
		||||
 
 | 
			
		||||
@@ -243,7 +243,7 @@ public class BinaryComposite : BinaryHandlerBase
 | 
			
		||||
        throw new NotSupportedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static readonly ConcurrentDictionary<MemberInfo, IMemberAccessor?> _cache = new();
 | 
			
		||||
    private static readonly NonBlockingDictionary<MemberInfo, IMemberAccessor?> _cache = new();
 | 
			
		||||
    private static Boolean TryGetAccessor(MemberInfo member, [NotNullWhen(true)] out IMemberAccessor? acc)
 | 
			
		||||
    {
 | 
			
		||||
        if (_cache.TryGetValue(member, out acc)) return acc != null;
 | 
			
		||||
 
 | 
			
		||||
@@ -323,7 +323,7 @@ public class SystemJson : IJsonHost
 | 
			
		||||
 | 
			
		||||
    #region IJsonHost 成员
 | 
			
		||||
 | 
			
		||||
    private static readonly ConcurrentDictionary<string, JsonSerializerOptions> _optionsCache = new();
 | 
			
		||||
    private static readonly NonBlockingDictionary<string, JsonSerializerOptions> _optionsCache = new();
 | 
			
		||||
 | 
			
		||||
    private static JsonSerializerOptions GetOptions(JsonSerializerOptions jsonSerializerOptions, bool indented, bool nullValue, bool camelCase)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ namespace ThingsGateway.NewLife.Serialization;
 | 
			
		||||
/// <summary>序列化助手</summary>
 | 
			
		||||
public static class SerialHelper
 | 
			
		||||
{
 | 
			
		||||
    private static readonly ConcurrentDictionary<PropertyInfo, String> _cache = new();
 | 
			
		||||
    private static readonly NonBlockingDictionary<PropertyInfo, String> _cache = new();
 | 
			
		||||
    /// <summary>获取序列化名称</summary>
 | 
			
		||||
    /// <param name="pi"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
	<Import Project="..\..\PackNuget.props" />
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<TargetFrameworks>net462;netstandard2.0;net6.0;net6.0-windows;net8.0;$(OtherTargetFrameworks);net8.0-windows;</TargetFrameworks>
 | 
			
		||||
		<TargetFrameworks>net47;netstandard2.0;net6.0;net6.0-windows;net8.0;$(OtherTargetFrameworks);net8.0-windows;</TargetFrameworks>
 | 
			
		||||
		<AssemblyName>ThingsGateway.NewLife.X</AssemblyName>
 | 
			
		||||
		<RootNamespace>ThingsGateway.NewLife</RootNamespace>
 | 
			
		||||
		<AssemblyTitle>工具核心库</AssemblyTitle>
 | 
			
		||||
@@ -35,12 +35,11 @@
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
 | 
			
		||||
		<PackageReference Include="System.Memory" Version="4.6.3" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'=='net462'">
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'=='net47'">
 | 
			
		||||
		<PackageReference Include="System.Memory" Version="4.6.3" />
 | 
			
		||||
		<PackageReference Include="System.ValueTuple" Version="4.6.1" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'=='net462'">
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'=='net47'">
 | 
			
		||||
		<Using Include="System.Net.Http" />
 | 
			
		||||
		<Reference Include="Microsoft.VisualBasic" />
 | 
			
		||||
		<Reference Include="System.Management" />
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.NewLife.Log;
 | 
			
		||||
using ThingsGateway.NewLife.Reflection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.NewLife.Threading;
 | 
			
		||||
 | 
			
		||||
@@ -391,11 +390,7 @@ public class TimerX : ITimer, ITimerx, IDisposable
 | 
			
		||||
        // 释放非托管资源
 | 
			
		||||
        Scheduler?.Remove(this, disposing ? "Dispose" : "GC");
 | 
			
		||||
 | 
			
		||||
        DelegateCache<TimerCallback>.Cache.Clear();
 | 
			
		||||
#if NET6_0_OR_GREATER
 | 
			
		||||
        DelegateCache<Func<Object?, ValueTask>>.Cache.Clear();
 | 
			
		||||
#endif
 | 
			
		||||
        DelegateCache<Func<Object?, Task>>.Cache.Clear();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 用于同步操作的锁对象
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        internal static object LockObject = new object();
 | 
			
		||||
        internal static readonly object LockObject = new object();
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// SqlSugar提供者实例
 | 
			
		||||
        /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -31,61 +31,6 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
        /// 数据记录器
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private IDataRecord DataRecord;
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// IsDBNull方法
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod(nameof(IDataRecord.IsDBNull), new Type[] { typeof(int) });
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// GetBoolean方法
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private static readonly MethodInfo getBoolean = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetBoolean), new Type[] { typeof(int) });
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// GetByte方法
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private static readonly MethodInfo getByte = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetByte), new Type[] { typeof(int) });
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// GetDateTime方法
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private static readonly MethodInfo getDateTime = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetDateTime), new Type[] { typeof(int) });
 | 
			
		||||
        private static readonly MethodInfo getDecimal = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetDecimal), new[] { typeof(int) });
 | 
			
		||||
        private static readonly MethodInfo getDouble = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetDouble), new[] { typeof(int) });
 | 
			
		||||
        private static readonly MethodInfo getFloat = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetFloat), new[] { typeof(int) });
 | 
			
		||||
        private static readonly MethodInfo getGuid = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetGuid), new[] { typeof(int) });
 | 
			
		||||
        private static readonly MethodInfo getInt16 = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetInt16), new[] { typeof(int) });
 | 
			
		||||
        private static readonly MethodInfo getInt32 = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetInt32), new[] { typeof(int) });
 | 
			
		||||
        private static readonly MethodInfo getInt64 = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetInt64), new[] { typeof(int) });
 | 
			
		||||
        private static readonly MethodInfo getString = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetString), new[] { typeof(int) });
 | 
			
		||||
        private static readonly MethodInfo getdatetimeoffset = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetDateTimeOffset));
 | 
			
		||||
        private static readonly MethodInfo getdatetimeoffsetDate = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetDateTimeOffsetDate));
 | 
			
		||||
        private static readonly MethodInfo getStringGuid = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetStringGuid));
 | 
			
		||||
        private static readonly MethodInfo getXelement = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetXelement));
 | 
			
		||||
        private static readonly MethodInfo getConvertStringGuid = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertStringGuid));
 | 
			
		||||
        private static readonly MethodInfo getEnum = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetEnum));
 | 
			
		||||
        private static readonly MethodInfo getConvertString = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertString));
 | 
			
		||||
        private static readonly MethodInfo getConvertFloat = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertFloat));
 | 
			
		||||
        private static readonly MethodInfo getConvertBoolean = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertBoolean));
 | 
			
		||||
        private static readonly MethodInfo getConvertByte = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertByte));
 | 
			
		||||
        private static readonly MethodInfo getConvertChar = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertChar));
 | 
			
		||||
        private static readonly MethodInfo getConvertDateTime = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDateTime));
 | 
			
		||||
        private static readonly MethodInfo getConvertTime = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertTime));
 | 
			
		||||
        private static readonly MethodInfo getTime = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetTime));
 | 
			
		||||
        private static readonly MethodInfo getConvertDecimal = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDecimal));
 | 
			
		||||
        private static readonly MethodInfo getConvertDouble = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDouble));
 | 
			
		||||
        private static readonly MethodInfo getConvertDoubleToFloat = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDoubleToFloat));
 | 
			
		||||
        private static readonly MethodInfo getConvertGuid = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertGuid));
 | 
			
		||||
        private static readonly MethodInfo getConvertInt16 = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertInt16));
 | 
			
		||||
        private static readonly MethodInfo getConvertInt32 = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertInt32));
 | 
			
		||||
        private static readonly MethodInfo getConvertInt64 = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertInt64));
 | 
			
		||||
        private static readonly MethodInfo getConvertEnum_Null = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertEnum_Null));
 | 
			
		||||
        private static readonly MethodInfo getConvertdatetimeoffset = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertdatetimeoffset));
 | 
			
		||||
        private static readonly MethodInfo getConvertdatetimeoffsetDate = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertdatetimeoffsetDate));
 | 
			
		||||
        private static readonly MethodInfo getOtherNull = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetOtherNull));
 | 
			
		||||
        private static readonly MethodInfo getOther = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetOther));
 | 
			
		||||
        private static readonly MethodInfo getJson = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetJson));
 | 
			
		||||
        private static readonly MethodInfo getArray = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetArray));
 | 
			
		||||
        private static readonly MethodInfo getEntity = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetEntity), new[] { typeof(SqlSugarProvider) });
 | 
			
		||||
        private static readonly MethodInfo getMyIntNull = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetMyIntNull));
 | 
			
		||||
        private static readonly MethodInfo getMyInt = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetMyInt));
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 加载委托
 | 
			
		||||
@@ -219,7 +164,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
            Label endIfLabel = generator.DefineLabel();
 | 
			
		||||
            generator.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
            generator.Emit(OpCodes.Ldc_I4, i);
 | 
			
		||||
            generator.Emit(OpCodes.Callvirt, isDBNullMethod);
 | 
			
		||||
            generator.Emit(OpCodes.Callvirt, IDataReaderEntityBuilderHelpers.isDBNullMethod);
 | 
			
		||||
            generator.Emit(OpCodes.Brtrue, endIfLabel);
 | 
			
		||||
            generator.Emit(OpCodes.Ldloc, result);
 | 
			
		||||
            var method = (columnInfo.SqlParameterDbType as Type).GetMethod(nameof(ISugarDataConverter.QueryConverter));
 | 
			
		||||
@@ -253,12 +198,12 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
 | 
			
		||||
            if (columnInfo.IsJson)
 | 
			
		||||
            {
 | 
			
		||||
                MethodInfo jsonMethod = getJson.MakeGenericMethod(columnInfo.PropertyInfo.PropertyType);
 | 
			
		||||
                MethodInfo jsonMethod = IDataReaderEntityBuilderHelpers.getJson.MakeGenericMethod(columnInfo.PropertyInfo.PropertyType);
 | 
			
		||||
                int i = DataRecord.GetOrdinal(fieldName);
 | 
			
		||||
                Label endIfLabel = generator.DefineLabel();
 | 
			
		||||
                generator.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
                generator.Emit(OpCodes.Ldc_I4, i);
 | 
			
		||||
                generator.Emit(OpCodes.Callvirt, isDBNullMethod);
 | 
			
		||||
                generator.Emit(OpCodes.Callvirt, IDataReaderEntityBuilderHelpers.isDBNullMethod);
 | 
			
		||||
                generator.Emit(OpCodes.Brtrue, endIfLabel);
 | 
			
		||||
                generator.Emit(OpCodes.Ldloc, result);
 | 
			
		||||
                generator.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
@@ -278,12 +223,12 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
            }
 | 
			
		||||
            if (columnInfo.IsArray)
 | 
			
		||||
            {
 | 
			
		||||
                MethodInfo arrayMehtod = getArray.MakeGenericMethod(columnInfo.PropertyInfo.PropertyType);
 | 
			
		||||
                MethodInfo arrayMehtod = IDataReaderEntityBuilderHelpers.getArray.MakeGenericMethod(columnInfo.PropertyInfo.PropertyType);
 | 
			
		||||
                int i = DataRecord.GetOrdinal(fieldName);
 | 
			
		||||
                Label endIfLabel = generator.DefineLabel();
 | 
			
		||||
                generator.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
                generator.Emit(OpCodes.Ldc_I4, i);
 | 
			
		||||
                generator.Emit(OpCodes.Callvirt, isDBNullMethod);
 | 
			
		||||
                generator.Emit(OpCodes.Callvirt, IDataReaderEntityBuilderHelpers.isDBNullMethod);
 | 
			
		||||
                generator.Emit(OpCodes.Brtrue, endIfLabel);
 | 
			
		||||
                generator.Emit(OpCodes.Ldloc, result);
 | 
			
		||||
                generator.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
@@ -298,7 +243,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
                Label endIfLabel = generator.DefineLabel();
 | 
			
		||||
                generator.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
                generator.Emit(OpCodes.Ldc_I4, i);
 | 
			
		||||
                generator.Emit(OpCodes.Callvirt, isDBNullMethod);
 | 
			
		||||
                generator.Emit(OpCodes.Callvirt, IDataReaderEntityBuilderHelpers.isDBNullMethod);
 | 
			
		||||
                generator.Emit(OpCodes.Brtrue, endIfLabel);
 | 
			
		||||
                generator.Emit(OpCodes.Ldloc, result);
 | 
			
		||||
                generator.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
@@ -332,7 +277,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
 | 
			
		||||
            generator.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
            generator.Emit(OpCodes.Ldc_I4, i);
 | 
			
		||||
            generator.Emit(OpCodes.Callvirt, isDBNullMethod);
 | 
			
		||||
            generator.Emit(OpCodes.Callvirt, IDataReaderEntityBuilderHelpers.isDBNullMethod);
 | 
			
		||||
            generator.Emit(OpCodes.Brtrue, endIfLabel);
 | 
			
		||||
            generator.Emit(OpCodes.Ldloc, result);
 | 
			
		||||
            generator.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
@@ -378,7 +323,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
            {
 | 
			
		||||
                if (provider.IsNoSql)
 | 
			
		||||
                {
 | 
			
		||||
                    method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    if (method.IsVirtual)
 | 
			
		||||
                        generator.Emit(OpCodes.Callvirt, method);
 | 
			
		||||
                    else
 | 
			
		||||
@@ -393,39 +338,39 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
            {
 | 
			
		||||
                if (bindPropertyType.IsEnum())
 | 
			
		||||
                {
 | 
			
		||||
                    method = isNullableType ? getConvertEnum_Null.MakeGenericMethod(bindPropertyType) : getEnum.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertEnum_Null.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getEnum.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                }
 | 
			
		||||
                else if (bindPropertyType == UtilConstants.IntType)
 | 
			
		||||
                {
 | 
			
		||||
                    method = isNullableType ? getConvertInt32 : getInt32;
 | 
			
		||||
                    method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertInt32 : IDataReaderEntityBuilderHelpers.getInt32;
 | 
			
		||||
                }
 | 
			
		||||
                else if (bindPropertyType == UtilConstants.DateTimeOffsetType && SugarCompatible.IsFramework)
 | 
			
		||||
                {
 | 
			
		||||
                    method = isNullableType ? getConvertdatetimeoffset : getdatetimeoffset;
 | 
			
		||||
                    method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertdatetimeoffset : IDataReaderEntityBuilderHelpers.getdatetimeoffset;
 | 
			
		||||
                }
 | 
			
		||||
                else if (bindPropertyType == UtilConstants.ByteType)
 | 
			
		||||
                {
 | 
			
		||||
                    method = isNullableType ? getConvertByte : getByte;
 | 
			
		||||
                    method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertByte : IDataReaderEntityBuilderHelpers.getByte;
 | 
			
		||||
                }
 | 
			
		||||
                else if (bindPropertyType == UtilConstants.StringType && dbTypeName.EqualCase("timestamp"))
 | 
			
		||||
                {
 | 
			
		||||
                    method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                }
 | 
			
		||||
                else if (dbTypeName.EqualCase("STRING"))
 | 
			
		||||
                {
 | 
			
		||||
                    method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                }
 | 
			
		||||
                else if (bindPropertyType == UtilConstants.StringType && validPropertyName == "int")
 | 
			
		||||
                {
 | 
			
		||||
                    method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                }
 | 
			
		||||
                else if (bindPropertyType == UtilConstants.StringType)
 | 
			
		||||
                {
 | 
			
		||||
                    method = getString;
 | 
			
		||||
                    method = IDataReaderEntityBuilderHelpers.getString;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                }
 | 
			
		||||
                if (method.IsVirtual)
 | 
			
		||||
                    generator.Emit(OpCodes.Callvirt, method);
 | 
			
		||||
@@ -445,13 +390,13 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
                case CSharpDataType.@int:
 | 
			
		||||
                    CheckType(bind.IntThrow, bindPropertyTypeName, validPropertyName, propertyName);
 | 
			
		||||
                    if (bindPropertyTypeName.IsContainsIn("int", "int32"))
 | 
			
		||||
                        method = isNullableType ? getConvertInt32 : getInt32;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertInt32 : IDataReaderEntityBuilderHelpers.getInt32;
 | 
			
		||||
                    if (bindPropertyTypeName.IsContainsIn("int64"))
 | 
			
		||||
                        method = null;
 | 
			
		||||
                    if (bindPropertyTypeName.IsContainsIn("byte"))
 | 
			
		||||
                        method = isNullableType ? getConvertByte : getByte;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertByte : IDataReaderEntityBuilderHelpers.getByte;
 | 
			
		||||
                    if (bindPropertyTypeName.IsContainsIn("int16"))
 | 
			
		||||
                        method = isNullableType ? getConvertInt16 : getInt16;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertInt16 : IDataReaderEntityBuilderHelpers.getInt16;
 | 
			
		||||
                    if (bindPropertyTypeName == "uint32" && this.Context.CurrentConnectionConfig.DbType.IsIn(DbType.MySql, DbType.MySqlConnector))
 | 
			
		||||
                        method = null;
 | 
			
		||||
                    if (bindPropertyTypeName == "int16")
 | 
			
		||||
@@ -459,21 +404,21 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
                    break;
 | 
			
		||||
                case CSharpDataType.@bool:
 | 
			
		||||
                    if (bindPropertyTypeName == "bool" || bindPropertyTypeName == "boolean")
 | 
			
		||||
                        method = isNullableType ? getConvertBoolean : getBoolean;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertBoolean : IDataReaderEntityBuilderHelpers.getBoolean;
 | 
			
		||||
                    break;
 | 
			
		||||
                case CSharpDataType.@string:
 | 
			
		||||
                    if (this.Context.CurrentConnectionConfig.DbType != DbType.Oracle)
 | 
			
		||||
                    {
 | 
			
		||||
                        CheckType(bind.StringThrow, bindPropertyTypeName, validPropertyName, propertyName);
 | 
			
		||||
                    }
 | 
			
		||||
                    method = getString;
 | 
			
		||||
                    method = IDataReaderEntityBuilderHelpers.getString;
 | 
			
		||||
                    if (bindPropertyTypeName == "guid")
 | 
			
		||||
                    {
 | 
			
		||||
                        method = isNullableType ? getConvertStringGuid : getStringGuid;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertStringGuid : IDataReaderEntityBuilderHelpers.getStringGuid;
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (bindPropertyTypeName == "xelement")
 | 
			
		||||
                    {
 | 
			
		||||
                        method = getXelement;
 | 
			
		||||
                        method = IDataReaderEntityBuilderHelpers.getXelement;
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (dbTypeName == "CHAR" && DataRecord.GetDataTypeName(ordinal) == "CHAR(36)")
 | 
			
		||||
                    {
 | 
			
		||||
@@ -487,35 +432,35 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
                case CSharpDataType.DateTime:
 | 
			
		||||
                    CheckType(bind.DateThrow, bindPropertyTypeName, validPropertyName, propertyName);
 | 
			
		||||
                    if (bindPropertyTypeName == "datetime")
 | 
			
		||||
                        method = isNullableType ? getConvertDateTime : getDateTime;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertDateTime : IDataReaderEntityBuilderHelpers.getDateTime;
 | 
			
		||||
                    if (bindPropertyTypeName == "datetime" && dbTypeName.Equals("time", StringComparison.CurrentCultureIgnoreCase))
 | 
			
		||||
                        method = isNullableType ? getConvertTime : getTime;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertTime : IDataReaderEntityBuilderHelpers.getTime;
 | 
			
		||||
                    if (bindPropertyTypeName == "datetimeoffset")
 | 
			
		||||
                        method = isNullableType ? getConvertdatetimeoffset : getdatetimeoffset;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertdatetimeoffset : IDataReaderEntityBuilderHelpers.getdatetimeoffset;
 | 
			
		||||
                    break;
 | 
			
		||||
                case CSharpDataType.@decimal:
 | 
			
		||||
                    CheckType(bind.DecimalThrow, bindPropertyTypeName, validPropertyName, propertyName);
 | 
			
		||||
                    if (bindPropertyTypeName == "decimal")
 | 
			
		||||
                        method = isNullableType ? getConvertDecimal : getDecimal;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertDecimal : IDataReaderEntityBuilderHelpers.getDecimal;
 | 
			
		||||
                    break;
 | 
			
		||||
                case CSharpDataType.@float:
 | 
			
		||||
                case CSharpDataType.@double:
 | 
			
		||||
                    CheckType(bind.DoubleThrow, bindPropertyTypeName, validPropertyName, propertyName);
 | 
			
		||||
                    if (bindPropertyTypeName.IsIn("double", "single") && dbTypeName != "real")
 | 
			
		||||
                        method = isNullableType ? getConvertDouble : getDouble;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertDouble : IDataReaderEntityBuilderHelpers.getDouble;
 | 
			
		||||
                    else
 | 
			
		||||
                        method = isNullableType ? getConvertFloat : getFloat;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertFloat : IDataReaderEntityBuilderHelpers.getFloat;
 | 
			
		||||
                    if (dbTypeName.Equals("float", StringComparison.CurrentCultureIgnoreCase) && isNullableType && bindPropertyTypeName.Equals("single", StringComparison.CurrentCultureIgnoreCase))
 | 
			
		||||
                    {
 | 
			
		||||
                        method = getConvertDoubleToFloat;
 | 
			
		||||
                        method = IDataReaderEntityBuilderHelpers.getConvertDoubleToFloat;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (bindPropertyType == UtilConstants.DecType)
 | 
			
		||||
                    {
 | 
			
		||||
                        method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (bindPropertyType == UtilConstants.IntType)
 | 
			
		||||
                    {
 | 
			
		||||
                        method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (bindPropertyTypeName == "string")
 | 
			
		||||
                    {
 | 
			
		||||
@@ -525,45 +470,45 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
                case CSharpDataType.Guid:
 | 
			
		||||
                    CheckType(bind.GuidThrow, bindPropertyTypeName, validPropertyName, propertyName);
 | 
			
		||||
                    if (bindPropertyTypeName == "guid")
 | 
			
		||||
                        method = isNullableType ? getConvertGuid : getGuid;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertGuid : IDataReaderEntityBuilderHelpers.getGuid;
 | 
			
		||||
                    break;
 | 
			
		||||
                case CSharpDataType.@byte:
 | 
			
		||||
                    if (bindPropertyTypeName == "byte")
 | 
			
		||||
                        method = isNullableType ? getConvertByte : getByte;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertByte : IDataReaderEntityBuilderHelpers.getByte;
 | 
			
		||||
                    break;
 | 
			
		||||
                case CSharpDataType.@enum:
 | 
			
		||||
                    method = isNullableType ? getConvertEnum_Null.MakeGenericMethod(bindPropertyType) : getEnum.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertEnum_Null.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getEnum.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    break;
 | 
			
		||||
                case CSharpDataType.@short:
 | 
			
		||||
                    CheckType(bind.ShortThrow, bindPropertyTypeName, validPropertyName, propertyName);
 | 
			
		||||
                    if (bindPropertyTypeName == "int16" || bindPropertyTypeName == "short")
 | 
			
		||||
                        method = isNullableType ? getConvertInt16 : getInt16;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertInt16 : IDataReaderEntityBuilderHelpers.getInt16;
 | 
			
		||||
                    break;
 | 
			
		||||
                case CSharpDataType.@long:
 | 
			
		||||
                    if (bindPropertyTypeName == "int64" || bindPropertyTypeName == "long")
 | 
			
		||||
                        method = isNullableType ? getConvertInt64 : getInt64;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertInt64 : IDataReaderEntityBuilderHelpers.getInt64;
 | 
			
		||||
                    break;
 | 
			
		||||
                case CSharpDataType.DateTimeOffset:
 | 
			
		||||
                    method = isNullableType ? getConvertdatetimeoffset : getdatetimeoffset;
 | 
			
		||||
                    method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertdatetimeoffset : IDataReaderEntityBuilderHelpers.getdatetimeoffset;
 | 
			
		||||
                    if (bindPropertyTypeName == "datetime")
 | 
			
		||||
                        method = isNullableType ? getConvertdatetimeoffsetDate : getdatetimeoffsetDate;
 | 
			
		||||
                        method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertdatetimeoffsetDate : IDataReaderEntityBuilderHelpers.getdatetimeoffsetDate;
 | 
			
		||||
                    break;
 | 
			
		||||
                case CSharpDataType.Single:
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            if (method == null && bindPropertyType == UtilConstants.StringType)
 | 
			
		||||
            {
 | 
			
		||||
                method = getConvertString;
 | 
			
		||||
                method = IDataReaderEntityBuilderHelpers.getConvertString;
 | 
			
		||||
            }
 | 
			
		||||
            if (bindPropertyType == UtilConstants.ObjType)
 | 
			
		||||
            {
 | 
			
		||||
                method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
            }
 | 
			
		||||
            if (method == null)
 | 
			
		||||
                method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
                method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
 | 
			
		||||
 | 
			
		||||
            if (method.IsVirtual)
 | 
			
		||||
                generator.Emit(OpCodes.Callvirt, method);
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,65 @@
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.SqlSugar
 | 
			
		||||
{
 | 
			
		||||
    internal static class IDataReaderEntityBuilderHelpers
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// IsDBNull方法
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        internal static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod(nameof(IDataRecord.IsDBNull), new Type[] { typeof(int) });
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// GetBoolean方法
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        internal static readonly MethodInfo getBoolean = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetBoolean), new Type[] { typeof(int) });
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// GetByte方法
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        internal static readonly MethodInfo getByte = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetByte), new Type[] { typeof(int) });
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// GetDateTime方法
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        internal static readonly MethodInfo getDateTime = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetDateTime), new Type[] { typeof(int) });
 | 
			
		||||
        internal static readonly MethodInfo getDecimal = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetDecimal), new[] { typeof(int) });
 | 
			
		||||
        internal static readonly MethodInfo getDouble = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetDouble), new[] { typeof(int) });
 | 
			
		||||
        internal static readonly MethodInfo getFloat = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetFloat), new[] { typeof(int) });
 | 
			
		||||
        internal static readonly MethodInfo getGuid = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetGuid), new[] { typeof(int) });
 | 
			
		||||
        internal static readonly MethodInfo getInt16 = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetInt16), new[] { typeof(int) });
 | 
			
		||||
        internal static readonly MethodInfo getInt32 = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetInt32), new[] { typeof(int) });
 | 
			
		||||
        internal static readonly MethodInfo getInt64 = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetInt64), new[] { typeof(int) });
 | 
			
		||||
        internal static readonly MethodInfo getString = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetString), new[] { typeof(int) });
 | 
			
		||||
        internal static readonly MethodInfo getdatetimeoffset = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetDateTimeOffset));
 | 
			
		||||
        internal static readonly MethodInfo getdatetimeoffsetDate = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetDateTimeOffsetDate));
 | 
			
		||||
        internal static readonly MethodInfo getStringGuid = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetStringGuid));
 | 
			
		||||
        internal static readonly MethodInfo getXelement = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetXelement));
 | 
			
		||||
        internal static readonly MethodInfo getConvertStringGuid = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertStringGuid));
 | 
			
		||||
        internal static readonly MethodInfo getEnum = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetEnum));
 | 
			
		||||
        internal static readonly MethodInfo getConvertString = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertString));
 | 
			
		||||
        internal static readonly MethodInfo getConvertFloat = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertFloat));
 | 
			
		||||
        internal static readonly MethodInfo getConvertBoolean = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertBoolean));
 | 
			
		||||
        internal static readonly MethodInfo getConvertByte = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertByte));
 | 
			
		||||
        internal static readonly MethodInfo getConvertChar = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertChar));
 | 
			
		||||
        internal static readonly MethodInfo getConvertDateTime = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDateTime));
 | 
			
		||||
        internal static readonly MethodInfo getConvertTime = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertTime));
 | 
			
		||||
        internal static readonly MethodInfo getTime = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetTime));
 | 
			
		||||
        internal static readonly MethodInfo getConvertDecimal = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDecimal));
 | 
			
		||||
        internal static readonly MethodInfo getConvertDouble = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDouble));
 | 
			
		||||
        internal static readonly MethodInfo getConvertDoubleToFloat = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDoubleToFloat));
 | 
			
		||||
        internal static readonly MethodInfo getConvertGuid = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertGuid));
 | 
			
		||||
        internal static readonly MethodInfo getConvertInt16 = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertInt16));
 | 
			
		||||
        internal static readonly MethodInfo getConvertInt32 = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertInt32));
 | 
			
		||||
        internal static readonly MethodInfo getConvertInt64 = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertInt64));
 | 
			
		||||
        internal static readonly MethodInfo getConvertEnum_Null = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertEnum_Null));
 | 
			
		||||
        internal static readonly MethodInfo getConvertdatetimeoffset = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertdatetimeoffset));
 | 
			
		||||
        internal static readonly MethodInfo getConvertdatetimeoffsetDate = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertdatetimeoffsetDate));
 | 
			
		||||
        internal static readonly MethodInfo getOtherNull = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetOtherNull));
 | 
			
		||||
        internal static readonly MethodInfo getOther = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetOther));
 | 
			
		||||
        internal static readonly MethodInfo getJson = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetJson));
 | 
			
		||||
        internal static readonly MethodInfo getArray = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetArray));
 | 
			
		||||
        internal static readonly MethodInfo getEntity = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetEntity), new[] { typeof(SqlSugarProvider) });
 | 
			
		||||
        internal static readonly MethodInfo getMyIntNull = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetMyIntNull));
 | 
			
		||||
        internal static readonly MethodInfo getMyInt = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetMyInt));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -11,7 +11,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 类模板
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public static string ClassTemplate = "{using}\r\n" +
 | 
			
		||||
        public const string ClassTemplate = "{using}\r\n" +
 | 
			
		||||
                                               "namespace {Namespace}\r\n" +
 | 
			
		||||
                                               "{\r\n" +
 | 
			
		||||
                                               "{ClassDescription}{SugarTable}\r\n" +
 | 
			
		||||
@@ -26,7 +26,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 类描述模板
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public static string ClassDescriptionTemplate =
 | 
			
		||||
        public const string ClassDescriptionTemplate =
 | 
			
		||||
                                                ClassSpace + "///<summary>\r\n" +
 | 
			
		||||
                                                ClassSpace + "///{ClassDescription}" +
 | 
			
		||||
                                                ClassSpace + "///</summary>";
 | 
			
		||||
@@ -34,13 +34,13 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 属性模板
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public static string PropertyTemplate = PropertySpace + "{SugarColumn}\r\n" +
 | 
			
		||||
        public const string PropertyTemplate = PropertySpace + "{SugarColumn}\r\n" +
 | 
			
		||||
                                                PropertySpace + "public {PropertyType} {PropertyName} {get;set;}\r\n";
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 属性描述模板
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public static string PropertyDescriptionTemplate =
 | 
			
		||||
        public const string PropertyDescriptionTemplate =
 | 
			
		||||
                                                PropertySpace + "/// <summary>\r\n" +
 | 
			
		||||
                                                PropertySpace + "/// Desc:{PropertyDescription}\r\n" +
 | 
			
		||||
                                                PropertySpace + "/// Default:{DefaultValue}\r\n" +
 | 
			
		||||
@@ -50,12 +50,12 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 构造函数模板
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public static string ConstructorTemplate = PropertySpace + " this.{PropertyName} ={DefaultValue};\r\n";
 | 
			
		||||
        public const string ConstructorTemplate = PropertySpace + " this.{PropertyName} ={DefaultValue};\r\n";
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 命名空间模板
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public static string UsingTemplate = "using System;\r\n" +
 | 
			
		||||
        public const string UsingTemplate = "using System;\r\n" +
 | 
			
		||||
                                               "using System.Linq;\r\n" +
 | 
			
		||||
                                               "using System.Text;" + "\r\n";
 | 
			
		||||
        #endregion
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 默认Razor类模板
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public static string DefaultRazorClassTemplate =
 | 
			
		||||
        public const string DefaultRazorClassTemplate =
 | 
			
		||||
@"using System;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
        SqlSugarProvider Context { get; set; }
 | 
			
		||||
        internal ISqlBuilder Builder;
 | 
			
		||||
        List<SugarParameter> Parameters;
 | 
			
		||||
        IEnumerable<StorageableInfo<T>> allDatas;
 | 
			
		||||
        StorageableInfo<T>[] allDatas;
 | 
			
		||||
        List<T> dbDataList = new List<T>();
 | 
			
		||||
        List<KeyValuePair<StorageType, Func<StorageableInfo<T>, bool>, string>> whereFuncs = new List<KeyValuePair<StorageType, Func<StorageableInfo<T>, bool>, string>>();
 | 
			
		||||
        Expression<Func<T, object>> whereExpression;
 | 
			
		||||
@@ -24,7 +24,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
            this.allDatas = datas.Select(it => new StorageableInfo<T>()
 | 
			
		||||
            {
 | 
			
		||||
                Item = it
 | 
			
		||||
            });
 | 
			
		||||
            }).ToArray();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Expression<Func<T, bool>> queryableWhereExp;
 | 
			
		||||
@@ -209,7 +209,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
            {
 | 
			
		||||
                return this.Saveable().ToStorage();
 | 
			
		||||
            }
 | 
			
		||||
            if (!this.allDatas.Any())
 | 
			
		||||
            if (this.allDatas.Length == 0)
 | 
			
		||||
                return new StorageableResult<T>()
 | 
			
		||||
                {
 | 
			
		||||
                    AsDeleteable = this.Context.Deleteable<T>().AS(asname).Where(it => false),
 | 
			
		||||
@@ -298,7 +298,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
            {
 | 
			
		||||
                return this.Saveable().GetStorageableResult();
 | 
			
		||||
            }
 | 
			
		||||
            if (!this.allDatas.Any())
 | 
			
		||||
            if (this.allDatas.Length == 0)
 | 
			
		||||
                return new StorageableResult<T>()
 | 
			
		||||
                {
 | 
			
		||||
                    //AsDeleteable = this.Context.Deleteable<T>().AS(asname).Where(it => false),
 | 
			
		||||
@@ -380,7 +380,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
            {
 | 
			
		||||
                return await Saveable().ToStorageAsync().ConfigureAwait(false);
 | 
			
		||||
            }
 | 
			
		||||
            if (!this.allDatas.Any())
 | 
			
		||||
            if (this.allDatas.Length == 0)
 | 
			
		||||
                return new StorageableResult<T>()
 | 
			
		||||
                {
 | 
			
		||||
                    AsDeleteable = this.Context.Deleteable<T>().AS(asname).Where(it => false),
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
    {
 | 
			
		||||
        internal SqlSugarProvider Context { get; set; }
 | 
			
		||||
        internal MethodInfo MethodInfo { get; set; }
 | 
			
		||||
        internal object objectValue { get; set; }
 | 
			
		||||
        internal object ObjValue { get; set; }
 | 
			
		||||
        public int ExecuteCommand()
 | 
			
		||||
        {
 | 
			
		||||
            if (Context == null) return 0;
 | 
			
		||||
@@ -20,16 +20,14 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                var type = "AsInsertable";
 | 
			
		||||
                return GetAs(type);
 | 
			
		||||
                return GetAs(nameof(AsInsertable));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public StorageableAsMethodInfo AsUpdateable
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                var type = "AsUpdateable";
 | 
			
		||||
                return GetAs(type);
 | 
			
		||||
                return GetAs(nameof(AsUpdateable));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -48,9 +46,9 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
 | 
			
		||||
        private MethodInfo GetSaveMethod(ref object callValue)
 | 
			
		||||
        {
 | 
			
		||||
            if (objectValue == null)
 | 
			
		||||
            if (ObjValue == null)
 | 
			
		||||
                return null;
 | 
			
		||||
            callValue = MethodInfo.Invoke(Context, new object[] { objectValue });
 | 
			
		||||
            callValue = MethodInfo.Invoke(Context, new object[] { ObjValue });
 | 
			
		||||
            return callValue.GetType().GetMyMethod(nameof(ExecuteCommand), 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1113,7 +1113,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
                {
 | 
			
		||||
                    Context = this.Context,
 | 
			
		||||
                    MethodInfo = method,
 | 
			
		||||
                    objectValue = newList
 | 
			
		||||
                    ObjValue = newList
 | 
			
		||||
                };
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
@@ -1126,7 +1126,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
                {
 | 
			
		||||
                    Context = this.Context,
 | 
			
		||||
                    MethodInfo = method,
 | 
			
		||||
                    objectValue = singleEntityObjectOrList
 | 
			
		||||
                    ObjValue = singleEntityObjectOrList
 | 
			
		||||
                };
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
{
 | 
			
		||||
    public sealed class SnowFlakeSingle
 | 
			
		||||
    {
 | 
			
		||||
        private static object LockObject = new object();
 | 
			
		||||
        private static readonly object LockObject = new object();
 | 
			
		||||
        private static DistributedSystem.Snowflake.IdWorker worker;
 | 
			
		||||
        public static int WorkId = 1;
 | 
			
		||||
        public static int DatacenterId = 1;
 | 
			
		||||
 
 | 
			
		||||
@@ -99,10 +99,7 @@ namespace ThingsGateway.SqlSugar
 | 
			
		||||
 | 
			
		||||
        public void RemoveAllCache()
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var key in GetAllKey())
 | 
			
		||||
            {
 | 
			
		||||
                this.Remove(key);
 | 
			
		||||
            }
 | 
			
		||||
            this.InstanceCache.Clear();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IEnumerable<string> GetAllKey()
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user