mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-10-31 07:33:58 +08:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | b12e923c99 | ||
|   | ab33eed8d3 | ||
|   | d930a9a8eb | ||
|   | af381fce12 | 
| @@ -126,35 +126,8 @@ dotnet_style_qualification_for_property = false:silent | |||||||
| dotnet_style_qualification_for_method = false:silent | dotnet_style_qualification_for_method = false:silent | ||||||
| dotnet_style_qualification_for_event = false:silent | dotnet_style_qualification_for_event = false:silent | ||||||
|  |  | ||||||
|  | dotnet_diagnostic.RCS1146.severity = warning | ||||||
| dotnet_diagnostic.CA2208.severity = none | dotnet_diagnostic.RCS1059.severity = none | ||||||
| dotnet_diagnostic.CA2008.severity = none | dotnet_diagnostic.RCS1138.severity = suggestion | ||||||
| dotnet_diagnostic.CA1812.severity = none |  | ||||||
| dotnet_diagnostic.CA1508.severity = none |  | ||||||
| dotnet_diagnostic.CA1512.severity = none |  | ||||||
| dotnet_diagnostic.CA1513.severity = none |  | ||||||
| dotnet_diagnostic.CA1810.severity = none |  | ||||||
| dotnet_diagnostic.CA1814.severity = none |  | ||||||
| dotnet_diagnostic.CA1815.severity = none |  | ||||||
| dotnet_diagnostic.CA1835.severity = none |  | ||||||
| dotnet_diagnostic.CA1819.severity = none |  | ||||||
| dotnet_diagnostic.CA1823.severity = none |  | ||||||
| dotnet_diagnostic.CA2002.severity = none |  | ||||||
| dotnet_diagnostic.CA5350.severity = none |  | ||||||
| dotnet_diagnostic.CA5351.severity = none |  | ||||||
| dotnet_diagnostic.CA5358.severity = none |  | ||||||
| dotnet_diagnostic.CA5384.severity = none |  | ||||||
| dotnet_diagnostic.CA5392.severity = none |  | ||||||
| dotnet_diagnostic.CA1805.severity = none |  | ||||||
| dotnet_diagnostic.CA1851.severity = none |  | ||||||
| dotnet_diagnostic.CA1510.severity = none |  | ||||||
| dotnet_diagnostic.CA5401.severity = none |  | ||||||
| dotnet_diagnostic.CA2022.severity = none |  | ||||||
| dotnet_diagnostic.CA1848.severity = none |  | ||||||
| dotnet_diagnostic.CA2000.severity = none |  | ||||||
| dotnet_diagnostic.CA5394.severity = none |  | ||||||
| dotnet_diagnostic.CA3003.severity = none |  | ||||||
| dotnet_diagnostic.CA1515.severity = none |  | ||||||
| dotnet_diagnostic.CA1849.severity = none |  | ||||||
|  |  | ||||||
| dotnet_code_quality.CA1822.api_surface = private, internal | dotnet_code_quality.CA1822.api_surface = private, internal | ||||||
| @@ -13,14 +13,16 @@ namespace ThingsGateway.Admin.Application; | |||||||
| /// <summary> | /// <summary> | ||||||
| /// 需要角色授权权限 | /// 需要角色授权权限 | ||||||
| /// </summary> | /// </summary> | ||||||
| [AttributeUsage(AttributeTargets.Class)] | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] | ||||||
| public sealed class RolePermissionAttribute : Attribute | public sealed class RolePermissionAttribute : Attribute | ||||||
| { | { | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| /// 忽略角色授权权限 | /// 忽略角色授权权限 | ||||||
| /// </summary> | /// </summary> | ||||||
|  | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] | ||||||
| public sealed class IgnoreRolePermissionAttribute : Attribute | public sealed class IgnoreRolePermissionAttribute : Attribute | ||||||
| { | { | ||||||
| } | } | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
| namespace ThingsGateway.Admin.Application; | namespace ThingsGateway.Admin.Application; | ||||||
|  |  | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class CacheConst | public static class CacheConst | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Token表缓存Key |     /// Token表缓存Key | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
| /// <summary> | /// <summary> | ||||||
| /// 通讯器常量 | /// 通讯器常量 | ||||||
| /// </summary> | /// </summary> | ||||||
| public class HubConst | public static class HubConst | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 系统HubUrl |     /// 系统HubUrl | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
| /// 资源表常量 | /// 资源表常量 | ||||||
| /// </summary> | /// </summary> | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class ResourceConst | public static class ResourceConst | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 系统内置编码 |     /// 系统内置编码 | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
| /// 角色常量 | /// 角色常量 | ||||||
| /// </summary> | /// </summary> | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class RoleConst | public static class RoleConst | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// api角色 |     /// api角色 | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
| /// SqlSugar系统常量 | /// SqlSugar系统常量 | ||||||
| /// </summary> | /// </summary> | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class SqlSugarConst | public static class SqlSugarConst | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// DB_Admin |     /// DB_Admin | ||||||
|   | |||||||
| @@ -1,14 +1,4 @@ | |||||||
| // ------------------------------------------------------------------------ |  | ||||||
| // 版权信息 |  | ||||||
| // 版权归百小僧及百签科技(广东)有限公司所有。 |  | ||||||
| // 所有权利保留。 |  | ||||||
| // 官方网站:https://baiqian.com |  | ||||||
| // |  | ||||||
| // 许可证信息 |  | ||||||
| // 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。 |  | ||||||
| // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 |  | ||||||
| // ------------------------------------------------------------------------ |  | ||||||
|  |  | ||||||
| using ThingsGateway.DependencyInjection; | using ThingsGateway.DependencyInjection; | ||||||
|  |  | ||||||
| namespace System; | namespace System; | ||||||
|   | |||||||
| @@ -1,14 +1,4 @@ | |||||||
| // ------------------------------------------------------------------------ |  | ||||||
| // 版权信息 |  | ||||||
| // 版权归百小僧及百签科技(广东)有限公司所有。 |  | ||||||
| // 所有权利保留。 |  | ||||||
| // 官方网站:https://baiqian.com |  | ||||||
| // |  | ||||||
| // 许可证信息 |  | ||||||
| // 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。 |  | ||||||
| // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 |  | ||||||
| // ------------------------------------------------------------------------ |  | ||||||
|  |  | ||||||
| using ThingsGateway.DependencyInjection; | using ThingsGateway.DependencyInjection; | ||||||
|  |  | ||||||
| namespace System; | namespace System; | ||||||
|   | |||||||
| @@ -33,83 +33,6 @@ public class AdminOAuthHandler<TOptions>( | |||||||
| ) : OAuthHandler<TOptions>(options, logger, encoder) | ) : OAuthHandler<TOptions>(options, logger, encoder) | ||||||
|     where TOptions : AdminOAuthOptions, new() |     where TOptions : AdminOAuthOptions, new() | ||||||
| { | { | ||||||
|     private async Task<LoginEvent> GetLogin() |  | ||||||
|     { |  | ||||||
|         var sysUser = await sysUserService.GetUserByIdAsync(RoleConst.SuperAdminId).ConfigureAwait(false);//获取用户信息 |  | ||||||
|  |  | ||||||
|         var appConfig = await configService.GetAppConfigAsync().ConfigureAwait(false); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         var expire = appConfig.LoginPolicy.VerificatExpireTime; |  | ||||||
|  |  | ||||||
|         var loginEvent = new LoginEvent |  | ||||||
|         { |  | ||||||
|             Ip = appService.RemoteIpAddress, |  | ||||||
|             Device = appService.UserAgent?.Platform, |  | ||||||
|             Expire = expire, |  | ||||||
|             SysUser = sysUser, |  | ||||||
|             VerificatId = CommonUtils.GetSingleId() |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         //获取verificat列表 |  | ||||||
|         var tokenTimeout = loginEvent.DateTime.AddMinutes(loginEvent.Expire); |  | ||||||
|         //生成verificat信息 |  | ||||||
|         var verificatInfo = new VerificatInfo |  | ||||||
|         { |  | ||||||
|             Device = loginEvent.Device, |  | ||||||
|             Expire = loginEvent.Expire, |  | ||||||
|             VerificatTimeout = tokenTimeout, |  | ||||||
|             Id = loginEvent.VerificatId, |  | ||||||
|             UserId = loginEvent.SysUser.Id, |  | ||||||
|             LoginIp = loginEvent.Ip, |  | ||||||
|             LoginTime = loginEvent.DateTime |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         //添加到verificat列表 |  | ||||||
|         verificatInfoService.Add(verificatInfo); |  | ||||||
|  |  | ||||||
|         return loginEvent; |  | ||||||
|     } |  | ||||||
|     /// <summary> |  | ||||||
|     /// 登录事件 |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="loginEvent"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     private async Task UpdateUser(LoginEvent loginEvent) |  | ||||||
|     { |  | ||||||
|         var sysUser = loginEvent.SysUser; |  | ||||||
|  |  | ||||||
|         #region 登录/密码策略 |  | ||||||
|  |  | ||||||
|         var key = CacheConst.Cache_LoginErrorCount + sysUser.Account;//获取登录错误次数Key值 |  | ||||||
|         App.CacheService.Remove(key);//移除登录错误次数 |  | ||||||
|  |  | ||||||
|         //获取用户verificat列表 |  | ||||||
|         var userToken = verificatInfoService.GetOne(loginEvent.VerificatId); |  | ||||||
|  |  | ||||||
|         #endregion 登录/密码策略 |  | ||||||
|  |  | ||||||
|         #region 重新赋值属性,设置本次登录信息为最新的信息 |  | ||||||
|  |  | ||||||
|         sysUser.LastLoginIp = sysUser.LatestLoginIp; |  | ||||||
|         sysUser.LastLoginTime = sysUser.LatestLoginTime; |  | ||||||
|         sysUser.LatestLoginIp = loginEvent.Ip; |  | ||||||
|         sysUser.LatestLoginTime = loginEvent.DateTime; |  | ||||||
|  |  | ||||||
|         #endregion 重新赋值属性,设置本次登录信息为最新的信息 |  | ||||||
|  |  | ||||||
|         using var db = DbContext.Db.GetConnectionScopeWithAttr<SysUser>().CopyNew(); |  | ||||||
|         //更新用户登录信息 |  | ||||||
|         if (await db.Updateable(sysUser).UpdateColumns(it => new |  | ||||||
|         { |  | ||||||
|             it.LastLoginIp, |  | ||||||
|             it.LastLoginTime, |  | ||||||
|             it.LatestLoginIp, |  | ||||||
|             it.LatestLoginTime, |  | ||||||
|         }).ExecuteCommandAsync().ConfigureAwait(false) > 0) |  | ||||||
|             App.CacheService.HashAdd(CacheConst.Cache_SysUser, sysUser.Id.ToString(), sysUser);//更新Cache信息 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     static AdminOAuthHandler() |     static AdminOAuthHandler() | ||||||
| @@ -152,7 +75,6 @@ public class AdminOAuthHandler<TOptions>( | |||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     protected override async Task<AuthenticationTicket> CreateTicketAsync( |     protected override async Task<AuthenticationTicket> CreateTicketAsync( | ||||||
|         ClaimsIdentity identity, |         ClaimsIdentity identity, | ||||||
|         AuthenticationProperties properties, |         AuthenticationProperties properties, | ||||||
| @@ -160,14 +82,17 @@ public class AdminOAuthHandler<TOptions>( | |||||||
|     { |     { | ||||||
|         properties.RedirectUri = Options.HomePath; |         properties.RedirectUri = Options.HomePath; | ||||||
|         properties.IsPersistent = true; |         properties.IsPersistent = true; | ||||||
|  |         var appConfig = await configService.GetAppConfigAsync().ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |         int expire = appConfig.LoginPolicy.VerificatExpireTime; | ||||||
|         if (!string.IsNullOrEmpty(tokens.ExpiresIn) && int.TryParse(tokens.ExpiresIn, out var result)) |         if (!string.IsNullOrEmpty(tokens.ExpiresIn) && int.TryParse(tokens.ExpiresIn, out var result)) | ||||||
|         { |         { | ||||||
|             properties.ExpiresUtc = TimeProvider.System.GetUtcNow().AddSeconds(result); |             properties.ExpiresUtc = TimeProvider.System.GetUtcNow().AddSeconds(result); | ||||||
|  |             expire = (int)(result / 60.0); | ||||||
|         } |         } | ||||||
|         var user = await HandleUserInfoAsync(tokens).ConfigureAwait(false); |         var user = await HandleUserInfoAsync(tokens).ConfigureAwait(false); | ||||||
|  |  | ||||||
|         var loginEvent = await GetLogin().ConfigureAwait(false); |         var loginEvent = await GetLogin(expire).ConfigureAwait(false); | ||||||
|         await UpdateUser(loginEvent).ConfigureAwait(false); |         await UpdateUser(loginEvent).ConfigureAwait(false); | ||||||
|         identity.AddClaim(new Claim(ClaimConst.VerificatId, loginEvent.VerificatId.ToString())); |         identity.AddClaim(new Claim(ClaimConst.VerificatId, loginEvent.VerificatId.ToString())); | ||||||
|         identity.AddClaim(new Claim(ClaimConst.UserId, RoleConst.SuperAdminId.ToString())); |         identity.AddClaim(new Claim(ClaimConst.UserId, RoleConst.SuperAdminId.ToString())); | ||||||
| @@ -222,29 +147,6 @@ public class AdminOAuthHandler<TOptions>( | |||||||
|         return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); |         return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary>刷新 Token 方法</summary> |  | ||||||
|     protected virtual async Task<OAuthTokenResponse> RefreshTokenAsync(OAuthTokenResponse oAuthToken) |  | ||||||
|     { |  | ||||||
|         var query = new Dictionary<string, string> |  | ||||||
|         { |  | ||||||
|             { "refresh_token", oAuthToken.RefreshToken }, |  | ||||||
|             { "grant_type", "refresh_token" } |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         var request = new HttpRequestMessage(HttpMethod.Post, QueryHelpers.AddQueryString(Options.TokenEndpoint, query)); |  | ||||||
|         request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); |  | ||||||
|  |  | ||||||
|         var response = await Backchannel.SendAsync(request, Context.RequestAborted).ConfigureAwait(false); |  | ||||||
|  |  | ||||||
|         var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); |  | ||||||
|  |  | ||||||
|         if (response.IsSuccessStatusCode) |  | ||||||
|         { |  | ||||||
|             return OAuthTokenResponse.Success(JsonDocument.Parse(content)); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return OAuthTokenResponse.Failed(new OAuthTokenException($"OAuth token endpoint failure: {await Display(response).ConfigureAwait(false)}")); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary>处理用户信息方法</summary> |     /// <summary>处理用户信息方法</summary> | ||||||
|     protected virtual async Task<JsonElement> HandleUserInfoAsync(OAuthTokenResponse tokens) |     protected virtual async Task<JsonElement> HandleUserInfoAsync(OAuthTokenResponse tokens) | ||||||
| @@ -283,7 +185,93 @@ public class AdminOAuthHandler<TOptions>( | |||||||
|  |  | ||||||
|         return output.ToString(); |         return output.ToString(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private async Task<LoginEvent> GetLogin(int expire) | ||||||
|  |     { | ||||||
|  |         var sysUser = await sysUserService.GetUserByIdAsync(RoleConst.SuperAdminId).ConfigureAwait(false);//获取用户信息 | ||||||
|  |  | ||||||
|  |         var loginEvent = new LoginEvent | ||||||
|  |         { | ||||||
|  |             Ip = appService.RemoteIpAddress, | ||||||
|  |             Device = appService.UserAgent?.Platform, | ||||||
|  |             Expire = expire, | ||||||
|  |             SysUser = sysUser, | ||||||
|  |             VerificatId = CommonUtils.GetSingleId() | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         //获取verificat列表 | ||||||
|  |         var tokenTimeout = loginEvent.DateTime.AddMinutes(loginEvent.Expire); | ||||||
|  |         //生成verificat信息 | ||||||
|  |         var verificatInfo = new VerificatInfo | ||||||
|  |         { | ||||||
|  |             Device = loginEvent.Device, | ||||||
|  |             Expire = loginEvent.Expire, | ||||||
|  |             VerificatTimeout = tokenTimeout, | ||||||
|  |             Id = loginEvent.VerificatId, | ||||||
|  |             UserId = loginEvent.SysUser.Id, | ||||||
|  |             LoginIp = loginEvent.Ip, | ||||||
|  |             LoginTime = loginEvent.DateTime | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         //添加到verificat列表 | ||||||
|  |         verificatInfoService.Add(verificatInfo); | ||||||
|  |         return loginEvent; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 登录事件 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="loginEvent"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     private async Task UpdateUser(LoginEvent loginEvent) | ||||||
|  |     { | ||||||
|  |         var sysUser = loginEvent.SysUser; | ||||||
|  |  | ||||||
|  |         #region 登录/密码策略 | ||||||
|  |  | ||||||
|  |         var key = CacheConst.Cache_LoginErrorCount + sysUser.Account;//获取登录错误次数Key值 | ||||||
|  |         App.CacheService.Remove(key);//移除登录错误次数 | ||||||
|  |  | ||||||
|  |         //获取用户verificat列表 | ||||||
|  |         var userToken = verificatInfoService.GetOne(loginEvent.VerificatId); | ||||||
|  |  | ||||||
|  |         #endregion 登录/密码策略 | ||||||
|  |  | ||||||
|  |         #region 重新赋值属性,设置本次登录信息为最新的信息 | ||||||
|  |  | ||||||
|  |         sysUser.LastLoginIp = sysUser.LatestLoginIp; | ||||||
|  |         sysUser.LastLoginTime = sysUser.LatestLoginTime; | ||||||
|  |         sysUser.LatestLoginIp = loginEvent.Ip; | ||||||
|  |         sysUser.LatestLoginTime = loginEvent.DateTime; | ||||||
|  |  | ||||||
|  |         #endregion 重新赋值属性,设置本次登录信息为最新的信息 | ||||||
|  |  | ||||||
|  |         using var db = DbContext.Db.GetConnectionScopeWithAttr<SysUser>().CopyNew(); | ||||||
|  |         //更新用户登录信息 | ||||||
|  |         if (await db.Updateable(sysUser).UpdateColumns(it => new | ||||||
|  |         { | ||||||
|  |             it.LastLoginIp, | ||||||
|  |             it.LastLoginTime, | ||||||
|  |             it.LatestLoginIp, | ||||||
|  |             it.LatestLoginTime, | ||||||
|  |         }).ExecuteCommandAsync().ConfigureAwait(false) > 0) | ||||||
|  |             App.CacheService.HashAdd(CacheConst.Cache_SysUser, sysUser.Id.ToString(), sysUser);//更新Cache信息 | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// <summary>自定义 Token 异常</summary> | /// <summary>自定义 Token 异常</summary> | ||||||
| public class OAuthTokenException(string message) : Exception(message); | public class OAuthTokenException : Exception | ||||||
|  | { | ||||||
|  |     public OAuthTokenException() : base() | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public OAuthTokenException(string? message, Exception? innerException) : base(message, innerException) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public OAuthTokenException(string? message) : base(message) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authentication.OAuth; | |||||||
| using Microsoft.AspNetCore.WebUtilities; | using Microsoft.AspNetCore.WebUtilities; | ||||||
|  |  | ||||||
| using System.Net.Http.Headers; | using System.Net.Http.Headers; | ||||||
|  | using System.Text; | ||||||
| using System.Text.Json; | using System.Text.Json; | ||||||
|  |  | ||||||
| namespace ThingsGateway.Admin.Application; | namespace ThingsGateway.Admin.Application; | ||||||
| @@ -32,6 +33,42 @@ public class GiteeOAuthOptions : AdminOAuthOptions | |||||||
|             context.Response.Redirect(context.RedirectUri); |             context.Response.Redirect(context.RedirectUri); | ||||||
|             return Task.CompletedTask; |             return Task.CompletedTask; | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary>刷新 Token 方法</summary> | ||||||
|  |     protected virtual async Task<OAuthTokenResponse> RefreshTokenAsync(TicketReceivedContext ticketReceivedContext, string refreshToken) | ||||||
|  |     { | ||||||
|  |         var query = new Dictionary<string, string> | ||||||
|  |         { | ||||||
|  |             { "refresh_token", refreshToken }, | ||||||
|  |             { "grant_type", "refresh_token" } | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         var request = new HttpRequestMessage(HttpMethod.Post, QueryHelpers.AddQueryString(TokenEndpoint, query)); | ||||||
|  |         request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); | ||||||
|  |  | ||||||
|  |         var response = await Backchannel.SendAsync(request, ticketReceivedContext.HttpContext.RequestAborted).ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |         var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |         if (response.IsSuccessStatusCode) | ||||||
|  |         { | ||||||
|  |             return OAuthTokenResponse.Success(JsonDocument.Parse(content)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return OAuthTokenResponse.Failed(new OAuthTokenException($"OAuth token endpoint failure: {await Display(response).ConfigureAwait(false)}")); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary>生成错误信息方法</summary> | ||||||
|  |     protected static async Task<string> Display(HttpResponseMessage response) | ||||||
|  |     { | ||||||
|  |         var output = new StringBuilder(); | ||||||
|  |         output.Append($"Status: {response.StatusCode}; "); | ||||||
|  |         output.Append($"Headers: {response.Headers}; "); | ||||||
|  |         output.Append($"Body: {await response.Content.ReadAsStringAsync().ConfigureAwait(false)};"); | ||||||
|  |  | ||||||
|  |         return output.ToString(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public override string GetName(JsonElement element) |     public override string GetName(JsonElement element) | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
| /// <summary> | /// <summary> | ||||||
| /// 日志常量 | /// 日志常量 | ||||||
| /// </summary> | /// </summary> | ||||||
| public class LoggingConst | public static class LoggingConst | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 分类 |     /// 分类 | ||||||
|   | |||||||
| @@ -87,7 +87,7 @@ public class BlazorAuthenticationHandler : AppAuthorizeHandler | |||||||
|             var roles = await _sysRoleService.GetRoleListByUserIdAsync(userId).ConfigureAwait(false); |             var roles = await _sysRoleService.GetRoleListByUserIdAsync(userId).ConfigureAwait(false); | ||||||
|  |  | ||||||
|             //这里鉴别用户使能状态 |             //这里鉴别用户使能状态 | ||||||
|             if (user == null || !user.Status) |             if (user?.Status != true) | ||||||
|             { |             { | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
| @@ -137,7 +137,7 @@ public class BlazorAuthenticationHandler : AppAuthorizeHandler | |||||||
|         else |         else | ||||||
|         { |         { | ||||||
|             //这里鉴别用户使能状态 |             //这里鉴别用户使能状态 | ||||||
|             if (user == null || !user.Status) |             if (user?.Status != true) | ||||||
|             { |             { | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -334,7 +334,7 @@ internal sealed class SysResourceService : BaseService<SysResource>, ISysResourc | |||||||
|             flatList.Add(node); |             flatList.Add(node); | ||||||
|  |  | ||||||
|             // 如果当前节点有子节点,则递归处理每个子节点 |             // 如果当前节点有子节点,则递归处理每个子节点 | ||||||
|             if (node.Children != null && node.Children.Count > 0) |             if (node.Children?.Count > 0) | ||||||
|             { |             { | ||||||
|                 foreach (var child in node.Children) |                 foreach (var child in node.Children) | ||||||
|                 { |                 { | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ | |||||||
|  |  | ||||||
| 	<ItemGroup> | 	<ItemGroup> | ||||||
| 		<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" /> | 		<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" /> | ||||||
| 		<ProjectReference Include="..\ThingsGateway.SqlSugar\ThingsGateway.SqlSugar.csproj" /> | 		<ProjectReference Include="..\ThingsGateway.DB\ThingsGateway.DB.csproj" /> | ||||||
| 	</ItemGroup> | 	</ItemGroup> | ||||||
|  |  | ||||||
| </Project> | </Project> | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ using Microsoft.Extensions.DependencyInjection; | |||||||
| namespace ThingsGateway.Admin.Application; | namespace ThingsGateway.Admin.Application; | ||||||
|  |  | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class ClearTokenUtil | public static class ClearTokenUtil | ||||||
| { | { | ||||||
|     private static IRelationService RelationService; |     private static IRelationService RelationService; | ||||||
|     private static ISysUserService SysUserService; |     private static ISysUserService SysUserService; | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ using Microsoft.Extensions.DependencyInjection; | |||||||
| namespace ThingsGateway.Admin.Application; | namespace ThingsGateway.Admin.Application; | ||||||
|  |  | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class NoticeUtil | public static class NoticeUtil | ||||||
| { | { | ||||||
|     private static INoticeService NoticeService; |     private static INoticeService NoticeService; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
|  |  | ||||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class OpenApiUtil | public static class OpenApiUtil | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 构建树节点,传入的列表已经是树结构 |     /// 构建树节点,传入的列表已经是树结构 | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
|  |  | ||||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class OrgUtil | public static class OrgUtil | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 构造选择项,ID/TITLE |     /// 构造选择项,ID/TITLE | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
|  |  | ||||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class PositionUtil | public static class PositionUtil | ||||||
| { | { | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
|  |  | ||||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class RoleUtil | public static class RoleUtil | ||||||
| { | { | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
|  |  | ||||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class UserUtil | public static class UserUtil | ||||||
| { | { | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ using ThingsGateway.Extension.Generic; | |||||||
|  |  | ||||||
| namespace ThingsGateway.Admin.Application; | namespace ThingsGateway.Admin.Application; | ||||||
|  |  | ||||||
| public class VerificatInfoUtil | public static class VerificatInfoUtil | ||||||
| { | { | ||||||
|     private static IVerificatInfoService VerificatInfoService { get; set; } |     private static IVerificatInfoService VerificatInfoService { get; set; } | ||||||
|     static VerificatInfoUtil() |     static VerificatInfoUtil() | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
| namespace ThingsGateway.Admin.Razor; | namespace ThingsGateway.Admin.Razor; | ||||||
|  |  | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class AdminOperConst | public static class AdminOperConst | ||||||
| { | { | ||||||
|     public const string Add = "新增"; |     public const string Add = "新增"; | ||||||
|     public const string Delete = "删除"; |     public const string Delete = "删除"; | ||||||
|   | |||||||
| @@ -48,7 +48,7 @@ public partial class SysUserAvatarEdit : IDisposable | |||||||
|  |  | ||||||
|     private async Task OnAvatarUpload(UploadFile file) |     private async Task OnAvatarUpload(UploadFile file) | ||||||
|     { |     { | ||||||
|         if (file != null && file.File != null) |         if (file?.File != null) | ||||||
|         { |         { | ||||||
|             var format = file.File.ContentType; |             var format = file.File.ContentType; | ||||||
|             ReadAvatarToken ??= new CancellationTokenSource(); |             ReadAvatarToken ??= new CancellationTokenSource(); | ||||||
|   | |||||||
| @@ -48,7 +48,7 @@ public partial class UserInfoEditComponent | |||||||
|  |  | ||||||
|     private async Task OnAvatarUpload(UploadFile file) |     private async Task OnAvatarUpload(UploadFile file) | ||||||
|     { |     { | ||||||
|         if (file != null && file.File != null) |         if (file?.File != null) | ||||||
|         { |         { | ||||||
|             var format = file.File.ContentType; |             var format = file.File.ContentType; | ||||||
|             ReadAvatarToken ??= new CancellationTokenSource(); |             ReadAvatarToken ??= new CancellationTokenSource(); | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ namespace ThingsGateway.Admin.Razor; | |||||||
|  |  | ||||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class ResourceUtil | public static class ResourceUtil | ||||||
| { | { | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|   | |||||||
| @@ -1,12 +1 @@ | |||||||
| // ------------------------------------------------------------------------ | global using ThingsGateway.Admin.Application; | ||||||
| // 版权信息 |  | ||||||
| // 版权归百小僧及百签科技(广东)有限公司所有。 |  | ||||||
| // 所有权利保留。 |  | ||||||
| // 官方网站:https://baiqian.com |  | ||||||
| // |  | ||||||
| // 许可证信息 |  | ||||||
| // 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。 |  | ||||||
| // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 |  | ||||||
| // ------------------------------------------------------------------------ |  | ||||||
|  |  | ||||||
| global using ThingsGateway.Admin.Application; |  | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
| /// 授权用户常量 | /// 授权用户常量 | ||||||
| /// </summary> | /// </summary> | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class ClaimConst | public static class ClaimConst | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 账号 |     /// 账号 | ||||||
| @@ -225,7 +225,7 @@ public static class SqlSugarExtensions | |||||||
| 
 | 
 | ||||||
|     private static IEnumerable<T> Sort<T>(this IEnumerable<T> list, BasePageInput basePageInput) |     private static IEnumerable<T> Sort<T>(this IEnumerable<T> list, BasePageInput basePageInput) | ||||||
|     { |     { | ||||||
|         if (basePageInput != null && basePageInput.SortField != null) |         if (basePageInput?.SortField != null) | ||||||
|         { |         { | ||||||
|             for (int i = 0; i < basePageInput.SortField.Count; i++) |             for (int i = 0; i < basePageInput.SortField.Count; i++) | ||||||
|             { |             { | ||||||
							
								
								
									
										11
									
								
								src/Admin/ThingsGateway.DB/GlobalUsings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/Admin/ThingsGateway.DB/GlobalUsings.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://thingsgateway.cn/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  |  | ||||||
|  | global using ThingsGateway.NewLife.Extension; | ||||||
| @@ -25,7 +25,7 @@ namespace ThingsGateway.Admin.Application; | |||||||
| /// 种子数据工具类 | /// 种子数据工具类 | ||||||
| /// </summary> | /// </summary> | ||||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||||
| public class SeedDataUtil | public static class SeedDataUtil | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 获取List列表 |     /// 获取List列表 | ||||||
							
								
								
									
										33
									
								
								src/Admin/ThingsGateway.DB/ThingsGateway.DB.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/Admin/ThingsGateway.DB/ThingsGateway.DB.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk"> | ||||||
|  |  | ||||||
|  | 	<Import Project="$(SolutionDir)Version.props" /> | ||||||
|  | 	<Import Project="$(SolutionDir)PackNuget.props" /> | ||||||
|  |  | ||||||
|  | 	<PropertyGroup> | ||||||
|  | 		<GenerateDocumentationFile>True</GenerateDocumentationFile> | ||||||
|  | 	</PropertyGroup> | ||||||
|  | 	<PropertyGroup> | ||||||
|  | 		<TargetFrameworks>net8.0;net9.0;</TargetFrameworks> | ||||||
|  | 	</PropertyGroup> | ||||||
|  | 	 | ||||||
|  | 	<ItemGroup> | ||||||
|  | 		<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.5" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 		<None Include="..\README.md" Pack="true" PackagePath="\" /> | ||||||
|  | 		<None Include="..\README.zh-CN.md" Pack="true" PackagePath="\" /> | ||||||
|  | 		<None Remove="$(SolutionDir)..\README.md" Pack="false" PackagePath="\" /> | ||||||
|  | 		<None Remove="$(SolutionDir)..\README.zh-CN.md" Pack="false" PackagePath="\" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 		<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" /> | ||||||
|  | 		<ProjectReference Include="..\ThingsGateway.SqlSugar\ThingsGateway.SqlSugar.csproj" /> | ||||||
|  | 		<!--<PackageReference Include="SqlSugarCore" Version="5.1.4.195" />--> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | </Project> | ||||||
| @@ -471,7 +471,7 @@ public static class App | |||||||
|         IEnumerable<string> pathOfExternalAssemblies = Array.Empty<string>(); |         IEnumerable<string> pathOfExternalAssemblies = Array.Empty<string>(); | ||||||
|  |  | ||||||
|         // 加载 appsettings.json 配置的外部程序集 |         // 加载 appsettings.json 配置的外部程序集 | ||||||
|         if (Settings.ExternalAssemblies != null && Settings.ExternalAssemblies.Length > 0) |         if (Settings.ExternalAssemblies?.Length > 0) | ||||||
|         { |         { | ||||||
|             var externalDlls = new List<string>(); |             var externalDlls = new List<string>(); | ||||||
|             foreach (var item in Settings.ExternalAssemblies) |             foreach (var item in Settings.ExternalAssemblies) | ||||||
| @@ -552,7 +552,7 @@ public static class App | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 处理排除的程序集 |         // 处理排除的程序集 | ||||||
|         if (Settings.ExcludeAssemblies != null && Settings.ExcludeAssemblies.Length > 0) |         if (Settings.ExcludeAssemblies?.Length > 0) | ||||||
|         { |         { | ||||||
|             scanAssemblies = scanAssemblies.Where(ass => !Settings.ExcludeAssemblies.Contains(ass.GetName().Name, StringComparer.OrdinalIgnoreCase)); |             scanAssemblies = scanAssemblies.Where(ass => !Settings.ExcludeAssemblies.Contains(ass.GetName().Name, StringComparer.OrdinalIgnoreCase)); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -455,7 +455,7 @@ public static class ObjectExtensions | |||||||
|                 foreach (var property in propertys) |                 foreach (var property in propertys) | ||||||
|                 { |                 { | ||||||
|                     var p = oldType.GetProperty(property.Name); |                     var p = oldType.GetProperty(property.Name); | ||||||
|                     if (property.CanWrite && p != null && p.CanRead) |                     if (property.CanWrite && p?.CanRead == true) | ||||||
|                     { |                     { | ||||||
|                         property.SetValue(o, ChangeType(p.GetValue(obj, null), property.PropertyType), null); |                         property.SetValue(o, ChangeType(p.GetValue(obj, null), property.PropertyType), null); | ||||||
|                     } |                     } | ||||||
| @@ -647,7 +647,7 @@ public static class ObjectExtensions | |||||||
|     /// <returns><see cref="bool"/> 实例,true 表示空集合,false 表示非空集合</returns> |     /// <returns><see cref="bool"/> 实例,true 表示空集合,false 表示非空集合</returns> | ||||||
|     internal static bool IsEmpty<T>(this IEnumerable<T> collection) |     internal static bool IsEmpty<T>(this IEnumerable<T> collection) | ||||||
|     { |     { | ||||||
|         return collection == null || !collection.Any(); |         return collection?.Any() != true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -54,8 +54,7 @@ public class FromConvertBinderProvider : IModelBinderProvider | |||||||
|  |  | ||||||
|         // 判断是否定义 [FromConvert] 特性 |         // 判断是否定义 [FromConvert] 特性 | ||||||
|         if (context.Metadata is DefaultModelMetadata actMetadata |         if (context.Metadata is DefaultModelMetadata actMetadata | ||||||
|             && actMetadata.Attributes.ParameterAttributes != null |             && actMetadata.Attributes.ParameterAttributes?.Count > 0 | ||||||
|             && actMetadata.Attributes.ParameterAttributes.Count > 0 |  | ||||||
|             && actMetadata.Attributes.ParameterAttributes.Any(u => u.GetType() == typeof(FromConvertAttribute))) |             && actMetadata.Attributes.ParameterAttributes.Any(u => u.GetType() == typeof(FromConvertAttribute))) | ||||||
|         { |         { | ||||||
|             return new FromConvertBinder(_modelBinderConverts); |             return new FromConvertBinder(_modelBinderConverts); | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ public sealed class AppAuthorizeAttribute : AuthorizeAttribute | |||||||
|     /// <param name="policies">多个策略</param> |     /// <param name="policies">多个策略</param> | ||||||
|     public AppAuthorizeAttribute(params string[] policies) |     public AppAuthorizeAttribute(params string[] policies) | ||||||
|     { |     { | ||||||
|         if (policies != null && policies.Length > 0) Policies = policies; |         if (policies?.Length > 0) Policies = policies; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ public sealed class DependsOnAttribute : Attribute | |||||||
|         var components = new List<Type>(); |         var components = new List<Type>(); | ||||||
|  |  | ||||||
|         // 遍历所有依赖组件 |         // 遍历所有依赖组件 | ||||||
|         if (dependComponents != null && dependComponents.Length > 0) |         if (dependComponents?.Length > 0) | ||||||
|         { |         { | ||||||
|             foreach (var component in dependComponents) |             foreach (var component in dependComponents) | ||||||
|             { |             { | ||||||
| @@ -102,7 +102,7 @@ public sealed class DependsOnAttribute : Attribute | |||||||
|             var components = new List<Type>(); |             var components = new List<Type>(); | ||||||
|  |  | ||||||
|             // 遍历所有依赖组件 |             // 遍历所有依赖组件 | ||||||
|             if (value != null && value.Length > 0) |             if (value?.Length > 0) | ||||||
|             { |             { | ||||||
|                 foreach (var component in value) |                 foreach (var component in value) | ||||||
|                 { |                 { | ||||||
|   | |||||||
| @@ -65,7 +65,7 @@ internal static class Penetrates | |||||||
|         IEnumerable<string> exposedHeaders = corsAccessorSettings.FixedClientToken == true |         IEnumerable<string> exposedHeaders = corsAccessorSettings.FixedClientToken == true | ||||||
|             ? _defaultExposedHeaders |             ? _defaultExposedHeaders | ||||||
|             : Array.Empty<string>(); |             : Array.Empty<string>(); | ||||||
|         if (corsAccessorSettings.WithExposedHeaders != null && corsAccessorSettings.WithExposedHeaders.Length > 0) |         if (corsAccessorSettings.WithExposedHeaders?.Length > 0) | ||||||
|         { |         { | ||||||
|             exposedHeaders = exposedHeaders.Concat(corsAccessorSettings.WithExposedHeaders).Distinct(StringComparer.OrdinalIgnoreCase); |             exposedHeaders = exposedHeaders.Concat(corsAccessorSettings.WithExposedHeaders).Distinct(StringComparer.OrdinalIgnoreCase); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ namespace ThingsGateway.DataEncryption; | |||||||
| /// AES 加解密 | /// AES 加解密 | ||||||
| /// </summary> | /// </summary> | ||||||
| [SuppressSniffer] | [SuppressSniffer] | ||||||
| public class AESEncryption | public static class AESEncryption | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 加密 |     /// 加密 | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ namespace ThingsGateway.DataEncryption; | |||||||
| /// DES 加解密 | /// DES 加解密 | ||||||
| /// </summary> | /// </summary> | ||||||
| [SuppressSniffer] | [SuppressSniffer] | ||||||
| public class DESEncryption | public static class DESEncryption | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 加密 |     /// 加密 | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ namespace ThingsGateway.DataEncryption; | |||||||
| /// KSort 加密(数据签名) | /// KSort 加密(数据签名) | ||||||
| /// </summary> | /// </summary> | ||||||
| [SuppressSniffer] | [SuppressSniffer] | ||||||
| public class KSortEncryption | public static class KSortEncryption | ||||||
| { | { | ||||||
|     private static DateTime _timeStampStartTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); |     private static DateTime _timeStampStartTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ namespace ThingsGateway.DataEncryption; | |||||||
| /// PBKDF2 加密 | /// PBKDF2 加密 | ||||||
| /// </summary> | /// </summary> | ||||||
| [SuppressSniffer] | [SuppressSniffer] | ||||||
| public class PBKDF2Encryption | public static class PBKDF2Encryption | ||||||
| { | { | ||||||
|     private const string SaltHashSeparator = ":"; |     private const string SaltHashSeparator = ":"; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ namespace ThingsGateway.DataEncryption; | |||||||
| /// SHA1 加密 | /// SHA1 加密 | ||||||
| /// </summary> | /// </summary> | ||||||
| [SuppressSniffer] | [SuppressSniffer] | ||||||
| public class SHA1Encryption | public static class SHA1Encryption | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// SHA1 加密 |     /// SHA1 加密 | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ namespace System.ComponentModel.DataAnnotations; | |||||||
| /// 数据类型验证特性 | /// 数据类型验证特性 | ||||||
| /// </summary> | /// </summary> | ||||||
| [SuppressSniffer] | [SuppressSniffer] | ||||||
|  | [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] | ||||||
| public sealed class DataValidationAttribute : ValidationAttribute | public sealed class DataValidationAttribute : ValidationAttribute | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|   | |||||||
| @@ -215,7 +215,7 @@ public static class DependencyInjectionServiceCollectionExtensions | |||||||
|     private static void AddDispatchProxy(IServiceCollection services, Type dependencyType, Type type, Type proxyType, Type inter, bool hasTarget = true) |     private static void AddDispatchProxy(IServiceCollection services, Type dependencyType, Type type, Type proxyType, Type inter, bool hasTarget = true) | ||||||
|     { |     { | ||||||
|         proxyType ??= GlobalServiceProxyType; |         proxyType ??= GlobalServiceProxyType; | ||||||
|         if (proxyType == null || (type != null && type.IsDefined(typeof(SuppressProxyAttribute), true))) return; |         if (proxyType == null || (type?.IsDefined(typeof(SuppressProxyAttribute), true) == true)) return; | ||||||
|  |  | ||||||
|         var lifetime = TryGetServiceLifetime(dependencyType); |         var lifetime = TryGetServiceLifetime(dependencyType); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -220,8 +220,7 @@ internal sealed class DynamicApiControllerApplicationModelConvention : IApplicat | |||||||
|         // 解决 Gitee 该 Issue:https://gitee.com/dotnetchina/Furion/issues/I59B74 |         // 解决 Gitee 该 Issue:https://gitee.com/dotnetchina/Furion/issues/I59B74 | ||||||
|         if (CheckIsForceWithDefaultRoute(controllerApiDescriptionSettings) |         if (CheckIsForceWithDefaultRoute(controllerApiDescriptionSettings) | ||||||
|             && !string.IsNullOrWhiteSpace(_dynamicApiControllerSettings.DefaultRoutePrefix) |             && !string.IsNullOrWhiteSpace(_dynamicApiControllerSettings.DefaultRoutePrefix) | ||||||
|             && controller.Selectors[0] != null |             && controller.Selectors[0]?.AttributeRouteModel != null | ||||||
|             && controller.Selectors[0].AttributeRouteModel != null |  | ||||||
|             && !ForceWithDefaultPrefixRouteControllerTypes.Contains(controller.ControllerType)) |             && !ForceWithDefaultPrefixRouteControllerTypes.Contains(controller.ControllerType)) | ||||||
|         { |         { | ||||||
|             // 读取模块 |             // 读取模块 | ||||||
|   | |||||||
| @@ -126,7 +126,7 @@ public static class DynamicApiControllerServiceCollectionExtensions | |||||||
|     { |     { | ||||||
|         var partManager = mvcBuilder.PartManager; |         var partManager = mvcBuilder.PartManager; | ||||||
|         // 载入程序集部件 |         // 载入程序集部件 | ||||||
|         if (partManager != null && assemblies != null && assemblies.Any()) |         if (partManager != null && assemblies?.Any() == true) | ||||||
|         { |         { | ||||||
|             foreach (var assembly in assemblies) |             foreach (var assembly in assemblies) | ||||||
|             { |             { | ||||||
|   | |||||||
| @@ -48,7 +48,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange | |||||||
|     /// <param name="assemblies">程序集</param> |     /// <param name="assemblies">程序集</param> | ||||||
|     public void AddAssemblies(params Assembly[] assemblies) |     public void AddAssemblies(params Assembly[] assemblies) | ||||||
|     { |     { | ||||||
|         if (assemblies != null && assemblies.Length > 0) |         if (assemblies?.Length > 0) | ||||||
|         { |         { | ||||||
|             foreach (var assembly in assemblies) |             foreach (var assembly in assemblies) | ||||||
|             { |             { | ||||||
| @@ -63,7 +63,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange | |||||||
|     /// <param name="assemblies">程序集</param> |     /// <param name="assemblies">程序集</param> | ||||||
|     public void AddAssembliesWithNotifyChanges(params Assembly[] assemblies) |     public void AddAssembliesWithNotifyChanges(params Assembly[] assemblies) | ||||||
|     { |     { | ||||||
|         if (assemblies != null && assemblies.Length > 0) |         if (assemblies?.Length > 0) | ||||||
|         { |         { | ||||||
|             AddAssemblies(assemblies); |             AddAssemblies(assemblies); | ||||||
|             NotifyChanges(); |             NotifyChanges(); | ||||||
| @@ -76,7 +76,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange | |||||||
|     /// <param name="assemblyNames">程序集名称</param> |     /// <param name="assemblyNames">程序集名称</param> | ||||||
|     public void RemoveAssemblies(params string[] assemblyNames) |     public void RemoveAssemblies(params string[] assemblyNames) | ||||||
|     { |     { | ||||||
|         if (assemblyNames != null && assemblyNames.Length > 0) |         if (assemblyNames?.Length > 0) | ||||||
|         { |         { | ||||||
|             foreach (var assemblyName in assemblyNames) |             foreach (var assemblyName in assemblyNames) | ||||||
|             { |             { | ||||||
| @@ -93,7 +93,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange | |||||||
|     /// <param name="assemblies">程序集</param> |     /// <param name="assemblies">程序集</param> | ||||||
|     public void RemoveAssemblies(params Assembly[] assemblies) |     public void RemoveAssemblies(params Assembly[] assemblies) | ||||||
|     { |     { | ||||||
|         if (assemblies != null && assemblies.Length > 0) |         if (assemblies?.Length > 0) | ||||||
|         { |         { | ||||||
|             RemoveAssemblies(assemblies.Select(ass => ass.GetName().Name).ToArray()); |             RemoveAssemblies(assemblies.Select(ass => ass.GetName().Name).ToArray()); | ||||||
|         } |         } | ||||||
| @@ -105,7 +105,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange | |||||||
|     /// <param name="assemblyNames">程序集名称</param> |     /// <param name="assemblyNames">程序集名称</param> | ||||||
|     public void RemoveAssembliesWithNotifyChanges(params string[] assemblyNames) |     public void RemoveAssembliesWithNotifyChanges(params string[] assemblyNames) | ||||||
|     { |     { | ||||||
|         if (assemblyNames != null && assemblyNames.Length > 0) |         if (assemblyNames?.Length > 0) | ||||||
|         { |         { | ||||||
|             RemoveAssemblies(assemblyNames); |             RemoveAssemblies(assemblyNames); | ||||||
|             NotifyChanges(); |             NotifyChanges(); | ||||||
| @@ -118,7 +118,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange | |||||||
|     /// <param name="assemblies">程序集</param> |     /// <param name="assemblies">程序集</param> | ||||||
|     public void RemoveAssembliesWithNotifyChanges(params Assembly[] assemblies) |     public void RemoveAssembliesWithNotifyChanges(params Assembly[] assemblies) | ||||||
|     { |     { | ||||||
|         if (assemblies != null && assemblies.Length > 0) |         if (assemblies?.Length > 0) | ||||||
|         { |         { | ||||||
|             RemoveAssemblies(assemblies); |             RemoveAssemblies(assemblies); | ||||||
|             NotifyChanges(); |             NotifyChanges(); | ||||||
|   | |||||||
| @@ -49,6 +49,14 @@ public class AppFriendlyException : Exception | |||||||
|         ErrorCode = OriginErrorCode = errorCode; |         ErrorCode = OriginErrorCode = errorCode; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public AppFriendlyException(string? message) : base(message) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public AppFriendlyException(string? message, Exception? innerException) : base(message, innerException) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 错误码 |     /// 错误码 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|   | |||||||
| @@ -102,7 +102,7 @@ public sealed class Retry | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 // 如果填写了 exceptionTypes 且异常类型不在 exceptionTypes 之内,则终止重试 |                 // 如果填写了 exceptionTypes 且异常类型不在 exceptionTypes 之内,则终止重试 | ||||||
|                 if (exceptionTypes != null && exceptionTypes.Length > 0 && !exceptionTypes.Any(u => u.IsAssignableFrom(ex.GetType()))) |                 if (exceptionTypes?.Length > 0 && !exceptionTypes.Any(u => u.IsAssignableFrom(ex.GetType()))) | ||||||
|                 { |                 { | ||||||
|                     if (finalThrow) |                     if (finalThrow) | ||||||
|                     { |                     { | ||||||
|   | |||||||
| @@ -604,7 +604,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs | |||||||
|     private string TrySerializeObject(object obj, LoggingMonitorMethod monitorMethod, out bool succeed) |     private string TrySerializeObject(object obj, LoggingMonitorMethod monitorMethod, out bool succeed) | ||||||
|     { |     { | ||||||
|         // 排除 IQueryable<> 泛型 |         // 排除 IQueryable<> 泛型 | ||||||
|         if (obj != null && obj.GetType().HasImplementedRawGeneric(typeof(IQueryable<>))) |         if (obj?.GetType().HasImplementedRawGeneric(typeof(IQueryable<>)) == true) | ||||||
|         { |         { | ||||||
|             succeed = true; |             succeed = true; | ||||||
|             return "{}"; |             return "{}"; | ||||||
| @@ -961,8 +961,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs | |||||||
|         // token 信息 |         // token 信息 | ||||||
|         // 判断是否是授权访问 |         // 判断是否是授权访问 | ||||||
|         var isAuth = actionMethod.GetFoundAttribute<AllowAnonymousAttribute>(true) == null |         var isAuth = actionMethod.GetFoundAttribute<AllowAnonymousAttribute>(true) == null | ||||||
|             && resultHttpContext.User != null |             && resultHttpContext.User?.Identity.IsAuthenticated == true; | ||||||
|             && resultHttpContext.User.Identity.IsAuthenticated; |  | ||||||
|         // 获取响应头信息 |         // 获取响应头信息 | ||||||
|         var accessToken = resultHttpContext.Response.Headers["access-token"].ToString(); |         var accessToken = resultHttpContext.Response.Headers["access-token"].ToString(); | ||||||
|         var authorization = string.IsNullOrWhiteSpace(accessToken) |         var authorization = string.IsNullOrWhiteSpace(accessToken) | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ public sealed partial class StringLoggingPart | |||||||
|     /// <param name="args"></param> |     /// <param name="args"></param> | ||||||
|     public StringLoggingPart SetArgs(params object[] args) |     public StringLoggingPart SetArgs(params object[] args) | ||||||
|     { |     { | ||||||
|         if (args != null && args.Length > 0) Args = args; |         if (args?.Length > 0) Args = args; | ||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ public static class ObjectMapperServiceCollectionExtensions | |||||||
|         var config = TypeAdapterConfig.GlobalSettings; |         var config = TypeAdapterConfig.GlobalSettings; | ||||||
|  |  | ||||||
|         // 扫描所有继承  IRegister 接口的对象映射配置 |         // 扫描所有继承  IRegister 接口的对象映射配置 | ||||||
|         if (assemblies != null && assemblies.Length > 0) config.Scan(assemblies); |         if (assemblies?.Length > 0) config.Scan(assemblies); | ||||||
|  |  | ||||||
|         // 配置支持依赖注入 |         // 配置支持依赖注入 | ||||||
|         services.AddSingleton(config); |         services.AddSingleton(config); | ||||||
|   | |||||||
| @@ -235,7 +235,7 @@ public sealed class SchedulerBuilder | |||||||
|         var schedulerBuilder = new SchedulerBuilder(jobBuilder); |         var schedulerBuilder = new SchedulerBuilder(jobBuilder); | ||||||
|  |  | ||||||
|         // 批量添加触发器 |         // 批量添加触发器 | ||||||
|         if (triggerBuilders != null && triggerBuilders.Length > 0) |         if (triggerBuilders?.Length > 0) | ||||||
|         { |         { | ||||||
|             schedulerBuilder.TriggerBuilders.AddRange(triggerBuilders); |             schedulerBuilder.TriggerBuilders.AddRange(triggerBuilders); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -192,7 +192,7 @@ public static class ScheduleExtensions | |||||||
|             var underScoreCasePropertyName = Penetrates.GetNaming(propertyName, NamingConventions.UnderScoreCase); |             var underScoreCasePropertyName = Penetrates.GetNaming(propertyName, NamingConventions.UnderScoreCase); | ||||||
|  |  | ||||||
|             // 处理忽略属性问题 |             // 处理忽略属性问题 | ||||||
|             if (ignorePropertyNames != null && ignorePropertyNames.Length > 0) |             if (ignorePropertyNames?.Length > 0) | ||||||
|             { |             { | ||||||
|                 if (ignorePropertyNames.Contains(propertyName, StringComparer.OrdinalIgnoreCase) |                 if (ignorePropertyNames.Contains(propertyName, StringComparer.OrdinalIgnoreCase) | ||||||
|                     || ignorePropertyNames.Contains(camelCasePropertyName, StringComparer.OrdinalIgnoreCase) |                     || ignorePropertyNames.Contains(camelCasePropertyName, StringComparer.OrdinalIgnoreCase) | ||||||
|   | |||||||
| @@ -327,7 +327,7 @@ internal sealed class ScheduleHostedService : BackgroundService | |||||||
|                                 }; |                                 }; | ||||||
|  |  | ||||||
|                                 // 是否定义 FallbackAsync 方法 |                                 // 是否定义 FallbackAsync 方法 | ||||||
|                                 var isDefinedFallbackAsyncMethod = jobHandler != null && jobHandler.GetType().GetMethod(nameof(IJob.FallbackAsync) |                                 var isDefinedFallbackAsyncMethod = jobHandler?.GetType().GetMethod(nameof(IJob.FallbackAsync) | ||||||
|                                     , BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly |                                     , BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly | ||||||
|                                     , null |                                     , null | ||||||
|                                     , new[] { typeof(JobExecutedContext), typeof(CancellationToken) } |                                     , new[] { typeof(JobExecutedContext), typeof(CancellationToken) } | ||||||
|   | |||||||
| @@ -101,7 +101,7 @@ public class HttpJob : IJob | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 添加请求头 |         // 添加请求头 | ||||||
|         if (httpJobMessage.Headers != null && httpJobMessage.Headers.Count > 0) |         if (httpJobMessage.Headers?.Count > 0) | ||||||
|         { |         { | ||||||
|             foreach (var (name, value) in httpJobMessage.Headers) |             foreach (var (name, value) in httpJobMessage.Headers) | ||||||
|             { |             { | ||||||
|   | |||||||
| @@ -711,7 +711,7 @@ public static class SpecificationDocumentBuilder | |||||||
|             .Union( |             .Union( | ||||||
|                 actions.SelectMany(u => GetActionGroups(u)) |                 actions.SelectMany(u => GetActionGroups(u)) | ||||||
|             ) |             ) | ||||||
|             .Where(u => u != null && u.Visible) |             .Where(u => u?.Visible == true) | ||||||
|             // 分组后取最大排序 |             // 分组后取最大排序 | ||||||
|             .GroupBy(u => u.Group) |             .GroupBy(u => u.Group) | ||||||
|             .Select(u => new GroupExtraInfo |             .Select(u => new GroupExtraInfo | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ public static class TP | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 添加项 |         // 添加项 | ||||||
|         if (items != null && items.Length > 0) |         if (items?.Length > 0) | ||||||
|         { |         { | ||||||
|             var propMaxLength = items.Where(u => _lazyRegex.Value.IsMatch(u)) |             var propMaxLength = items.Where(u => _lazyRegex.Value.IsMatch(u)) | ||||||
|                 .DefaultIfEmpty(string.Empty) |                 .DefaultIfEmpty(string.Empty) | ||||||
|   | |||||||
| @@ -39,7 +39,7 @@ | |||||||
| 		<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" /> | 		<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" /> | ||||||
| 		<PackageReference Include="Mapster" Version="7.4.0" /> | 		<PackageReference Include="Mapster" Version="7.4.0" /> | ||||||
| 		<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.5.4" /> | 		<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.5.4" /> | ||||||
| 		<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.3" /> | 		<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.4" /> | ||||||
| 	</ItemGroup> | 	</ItemGroup> | ||||||
|  |  | ||||||
| 	<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' "> | 	<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' "> | ||||||
|   | |||||||
| @@ -343,7 +343,7 @@ public partial class Crontab | |||||||
|  |  | ||||||
|             // 如果存在且唯一,则进入下一轮判断 |             // 如果存在且唯一,则进入下一轮判断 | ||||||
|             // 接下来的判断是处理 SUN + L 的情况,如 SUNL == 0L == SUNDAY,它们都是合法的 Cron 值 |             // 接下来的判断是处理 SUN + L 的情况,如 SUNL == 0L == SUNDAY,它们都是合法的 Cron 值 | ||||||
|             if (replaceVal != null && replaceVal.Count == 1) |             if (replaceVal?.Count == 1) | ||||||
|             { |             { | ||||||
|                 var missingParser = ""; |                 var missingParser = ""; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -140,10 +140,10 @@ public static class UnifyContext | |||||||
|         if (unifyResultSettings == null) return; |         if (unifyResultSettings == null) return; | ||||||
|  |  | ||||||
|         // 篡改响应状态码 |         // 篡改响应状态码 | ||||||
|         if (unifyResultSettings.AdaptStatusCodes != null && unifyResultSettings.AdaptStatusCodes.Length > 0) |         if (unifyResultSettings.AdaptStatusCodes?.Length > 0) | ||||||
|         { |         { | ||||||
|             var adaptStatusCode = unifyResultSettings.AdaptStatusCodes.FirstOrDefault(u => u[0] == statusCode); |             var adaptStatusCode = unifyResultSettings.AdaptStatusCodes.FirstOrDefault(u => u[0] == statusCode); | ||||||
|             if (adaptStatusCode != null && adaptStatusCode.Length > 0 && adaptStatusCode[0] > 0) |             if (adaptStatusCode?.Length > 0 && adaptStatusCode[0] > 0) | ||||||
|             { |             { | ||||||
|                 context.Response.StatusCode = adaptStatusCode[1]; |                 context.Response.StatusCode = adaptStatusCode[1]; | ||||||
|                 return; |                 return; | ||||||
|   | |||||||
| @@ -139,8 +139,7 @@ internal static class TypeExtensions | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 类型限定名是否以 <> 开头且以 AnonymousType 结尾 |         // 类型限定名是否以 <> 开头且以 AnonymousType 结尾 | ||||||
|         return type.FullName is not null |         return type.FullName?.StartsWith("<>") == true | ||||||
|                && type.FullName.StartsWith("<>") |  | ||||||
|                && type.FullName.Contains("AnonymousType"); |                && type.FullName.Contains("AnonymousType"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -463,7 +462,7 @@ internal static class TypeExtensions | |||||||
|             var elementType = type.GetElementType(); |             var elementType = type.GetElementType(); | ||||||
|  |  | ||||||
|             // 检查元素类型是否是 KeyValuePair<,> 类型 |             // 检查元素类型是否是 KeyValuePair<,> 类型 | ||||||
|             if (elementType is null || !elementType.IsKeyValuePair()) |             if (elementType?.IsKeyValuePair() != true) | ||||||
|             { |             { | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -135,7 +135,7 @@ internal static class V5_ObjectExtensions | |||||||
|         var runtimeProperty = obj.GetType().GetRuntimeProperty("Count"); |         var runtimeProperty = obj.GetType().GetRuntimeProperty("Count"); | ||||||
|  |  | ||||||
|         // 反射获取 Count 属性值 |         // 反射获取 Count 属性值 | ||||||
|         if (runtimeProperty is not null && runtimeProperty.CanRead && runtimeProperty.PropertyType == typeof(int)) |         if (runtimeProperty?.CanRead == true && runtimeProperty.PropertyType == typeof(int)) | ||||||
|         { |         { | ||||||
|             count = (int)runtimeProperty.GetValue(obj)!; |             count = (int)runtimeProperty.GetValue(obj)!; | ||||||
|             return true; |             return true; | ||||||
| @@ -332,7 +332,7 @@ internal static class V5_ObjectExtensions | |||||||
|             var property = current.GetType().GetProperty(part, bindingFlags); |             var property = current.GetType().GetProperty(part, bindingFlags); | ||||||
|  |  | ||||||
|             // 空检查 |             // 空检查 | ||||||
|             if (property is null || !property.CanRead) |             if (property?.CanRead != true) | ||||||
|             { |             { | ||||||
|                 return null; |                 return null; | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -1591,7 +1591,7 @@ public sealed partial class HttpRequestBuilder | |||||||
|     public HttpRequestBuilder SetBaseAddress(Uri? baseAddress) |     public HttpRequestBuilder SetBaseAddress(Uri? baseAddress) | ||||||
|     { |     { | ||||||
|         // 检查基地址是否是绝对路径地址 |         // 检查基地址是否是绝对路径地址 | ||||||
|         if (baseAddress is not null && !baseAddress.IsAbsoluteUri) |         if (baseAddress?.IsAbsoluteUri == false) | ||||||
|         { |         { | ||||||
|             throw new ArgumentException("The base address must be absolute.", nameof(baseAddress)); |             throw new ArgumentException("The base address must be absolute.", nameof(baseAddress)); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -136,7 +136,7 @@ public partial class Clay | |||||||
|         { |         { | ||||||
|             handler(this, new ClayEventArgs(identifier, Contains(identifier))); |             handler(this, new ClayEventArgs(identifier, Contains(identifier))); | ||||||
|         } |         } | ||||||
|         catch (Exception) |         catch | ||||||
|         { |         { | ||||||
|             // ignored |             // ignored | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -177,15 +177,6 @@ public partial class Clay | |||||||
|         enumerableClay = this; |         enumerableClay = this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="clay">dynamic 类型的 <see cref="Clay" /></param> |  | ||||||
|     /// <param name="enumerableClay"> |  | ||||||
|     ///     <see cref="IEnumerable{T}" /> |  | ||||||
|     /// </param> |  | ||||||
|     /// <param name="rawClay"> |  | ||||||
|     ///     <see cref="Clay" /> |  | ||||||
|     /// </param> |  | ||||||
|     public void Deconstruct(out dynamic clay, out IEnumerable<dynamic?> enumerableClay, out Clay rawClay) |     public void Deconstruct(out dynamic clay, out IEnumerable<dynamic?> enumerableClay, out Clay rawClay) | ||||||
|     { |     { | ||||||
|         clay = this; |         clay = this; | ||||||
| @@ -888,7 +879,7 @@ public partial class Clay | |||||||
|         foreach (var item in values) |         foreach (var item in values) | ||||||
|         { |         { | ||||||
|             // 检查值是否为空值或基本类型的值 |             // 检查值是否为空值或基本类型的值 | ||||||
|             if (item is null || item.GetType().IsBasicType()) |             if (item?.GetType().IsBasicType() != false) | ||||||
|             { |             { | ||||||
|                 throw new InvalidOperationException("Cannot extend a single object with null or basic type values."); |                 throw new InvalidOperationException("Cannot extend a single object with null or basic type values."); | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -12,9 +12,11 @@ using System.ComponentModel.DataAnnotations; | |||||||
|  |  | ||||||
| namespace ThingsGateway; | namespace ThingsGateway; | ||||||
|  |  | ||||||
|  |  | ||||||
| /// <summary> | /// <summary> | ||||||
| /// 最小值校验 | /// 最小值校验 | ||||||
| /// </summary> | /// </summary> | ||||||
|  | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] | ||||||
| public sealed class MinValueAttribute : ValidationAttribute | public sealed class MinValueAttribute : ValidationAttribute | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|   | |||||||
| @@ -244,7 +244,6 @@ public ref struct SpanReader | |||||||
|         while (true) |         while (true) | ||||||
|         { |         { | ||||||
|             var bt = ReadByte(); |             var bt = ReadByte(); | ||||||
|             if (bt < 0) throw new Exception($"The data stream is out of range! The integer read is {rs: n0}"); |  | ||||||
|             b = (Byte)bt; |             b = (Byte)bt; | ||||||
|  |  | ||||||
|             // 必须转为Int32,否则可能溢出 |             // 必须转为Int32,否则可能溢出 | ||||||
|   | |||||||
| @@ -120,7 +120,7 @@ public class MemoryCache : Cache | |||||||
|     /// <summary>是否包含缓存项</summary> |     /// <summary>是否包含缓存项</summary> | ||||||
|     /// <param name="key"></param> |     /// <param name="key"></param> | ||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     public override Boolean ContainsKey(String key) => _cache.TryGetValue(key, out var item) && item != null && !item.Expired; |     public override Boolean ContainsKey(String key) => _cache.TryGetValue(key, out var item) && item?.Expired == false; | ||||||
|  |  | ||||||
|     /// <summary>添加缓存项,已存在时更新</summary> |     /// <summary>添加缓存项,已存在时更新</summary> | ||||||
|     /// <typeparam name="T">值类型</typeparam> |     /// <typeparam name="T">值类型</typeparam> | ||||||
| @@ -166,7 +166,7 @@ public class MemoryCache : Cache | |||||||
|     [return: MaybeNull] |     [return: MaybeNull] | ||||||
|     public override T Get<T>(String key) |     public override T Get<T>(String key) | ||||||
|     { |     { | ||||||
|         if (!_cache.TryGetValue(key, out var item) || item == null || item.Expired) return default; |         if (!_cache.TryGetValue(key, out var item) || item?.Expired != false) return default; | ||||||
|  |  | ||||||
|         return item.Visit<T>(); |         return item.Visit<T>(); | ||||||
|     } |     } | ||||||
| @@ -712,7 +712,7 @@ public class MemoryCache : Cache | |||||||
|             for (var i = 0; i < slist.Count && over > 0; i++) |             for (var i = 0; i < slist.Count && over > 0; i++) | ||||||
|             { |             { | ||||||
|                 var ss = slist.Values[i]; |                 var ss = slist.Values[i]; | ||||||
|                 if (ss != null && ss.Count > 0) |                 if (ss?.Count > 0) | ||||||
|                 { |                 { | ||||||
|                     foreach (var item in ss) |                     foreach (var item in ss) | ||||||
|                     { |                     { | ||||||
|   | |||||||
| @@ -402,7 +402,7 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull | |||||||
|     /// <param name="args"></param> |     /// <param name="args"></param> | ||||||
|     public void WriteLog(String format, params Object?[] args) |     public void WriteLog(String format, params Object?[] args) | ||||||
|     { |     { | ||||||
|         if (Log == null || !Log.Enable) return; |         if (Log?.Enable != true) return; | ||||||
|  |  | ||||||
|         Log.Info(Name + "." + format, args); |         Log.Info(Name + "." + format, args); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ namespace ThingsGateway.NewLife; | |||||||
| /// <summary> | /// <summary> | ||||||
| /// FileUtil | /// FileUtil | ||||||
| /// </summary> | /// </summary> | ||||||
| public class FileUtil | public static class FileUtil | ||||||
| { | { | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 读取文件 |     /// 读取文件 | ||||||
|   | |||||||
| @@ -633,7 +633,7 @@ public class MachineInfo | |||||||
|         if (!_excludes.Contains(nameof(Temperature))) |         if (!_excludes.Contains(nameof(Temperature))) | ||||||
|         { |         { | ||||||
|             var temp = ReadWmic(@"/namespace:\\root\wmi path MSAcpi_ThermalZoneTemperature", "CurrentTemperature"); |             var temp = ReadWmic(@"/namespace:\\root\wmi path MSAcpi_ThermalZoneTemperature", "CurrentTemperature"); | ||||||
|             if (temp != null && temp.Count > 0) |             if (temp?.Count > 0) | ||||||
|             { |             { | ||||||
|                 if (temp.TryGetValue("CurrentTemperature", out var str) && !str.IsNullOrEmpty()) |                 if (temp.TryGetValue("CurrentTemperature", out var str) && !str.IsNullOrEmpty()) | ||||||
|                     Temperature = (str.SplitAsInt().Average() - 2732) / 10.0; |                     Temperature = (str.SplitAsInt().Average() - 2732) / 10.0; | ||||||
| @@ -651,7 +651,7 @@ public class MachineInfo | |||||||
|         else if (!_excludes.Contains(nameof(Battery))) |         else if (!_excludes.Contains(nameof(Battery))) | ||||||
|         { |         { | ||||||
|             var battery = ReadWmic("path win32_battery", "EstimatedChargeRemaining"); |             var battery = ReadWmic("path win32_battery", "EstimatedChargeRemaining"); | ||||||
|             if (battery != null && battery.Count > 0) |             if (battery?.Count > 0) | ||||||
|             { |             { | ||||||
|                 if (battery.TryGetValue("EstimatedChargeRemaining", out var str) && !str.IsNullOrEmpty()) |                 if (battery.TryGetValue("EstimatedChargeRemaining", out var str) && !str.IsNullOrEmpty()) | ||||||
|                     Battery = str.SplitAsInt().Average() / 100.0; |                     Battery = str.SplitAsInt().Average() / 100.0; | ||||||
| @@ -937,7 +937,7 @@ public class MachineInfo | |||||||
|         foreach (var item in ss) |         foreach (var item in ss) | ||||||
|         { |         { | ||||||
|             var ks = item?.Split('='); |             var ks = item?.Split('='); | ||||||
|             if (ks != null && ks.Length >= 2) |             if (ks?.Length >= 2) | ||||||
|             { |             { | ||||||
|                 var k = ks[0].Trim(); |                 var k = ks[0].Trim(); | ||||||
|                 var v = ks[1].Trim().TrimInvisible(); |                 var v = ks[1].Trim().TrimInvisible(); | ||||||
| @@ -1079,7 +1079,7 @@ public class MachineInfo | |||||||
|         if (root.IsNullOrEmpty()) return 0; |         if (root.IsNullOrEmpty()) return 0; | ||||||
|  |  | ||||||
|         var driveInfo = new DriveInfo(root); |         var driveInfo = new DriveInfo(root); | ||||||
|         if (driveInfo == null || !driveInfo.IsReady) return -1; |         if (driveInfo?.IsReady != true) return -1; | ||||||
|  |  | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ namespace ThingsGateway.NewLife.Common; | |||||||
| /// <remarks> | /// <remarks> | ||||||
| /// 文档 https://newlifex.com/core/pinyin | /// 文档 https://newlifex.com/core/pinyin | ||||||
| /// </remarks> | /// </remarks> | ||||||
| public class PinYin | public static class PinYin | ||||||
| { | { | ||||||
|     #region 数组信息 |     #region 数组信息 | ||||||
|     private static readonly Int32[] pyValue = new[] { |     private static readonly Int32[] pyValue = new[] { | ||||||
|   | |||||||
| @@ -199,7 +199,7 @@ public static class Runtime | |||||||
|         if (processId != ProcessId) gc = false; |         if (processId != ProcessId) gc = false; | ||||||
|  |  | ||||||
|         var log = XTrace.Log; |         var log = XTrace.Log; | ||||||
|         if (log != null && log.Enable && log.Level <= LogLevel.Debug) |         if (log?.Enable == true && log.Level <= LogLevel.Debug) | ||||||
|         { |         { | ||||||
|             p ??= Process.GetCurrentProcess(); |             p ??= Process.GetCurrentProcess(); | ||||||
|             var gcm = GC.GetTotalMemory(false) / 1024; |             var gcm = GC.GetTotalMemory(false) / 1024; | ||||||
| @@ -243,7 +243,7 @@ public static class Runtime | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (log != null && log.Enable && log.Level <= LogLevel.Debug) |         if (log?.Enable == true && log.Level <= LogLevel.Debug) | ||||||
|         { |         { | ||||||
|             p ??= Process.GetProcessById(processId); |             p ??= Process.GetProcessById(processId); | ||||||
|             p.Refresh(); |             p.Refresh(); | ||||||
|   | |||||||
| @@ -108,7 +108,7 @@ public static class ConfigHelper | |||||||
|  |  | ||||||
|                 dic[cfg.Key] = cfg.Value; |                 dic[cfg.Key] = cfg.Value; | ||||||
|  |  | ||||||
|                 if (cfg.Childs != null && cfg.Childs.Count > 0) |                 if (cfg.Childs?.Count > 0) | ||||||
|                     dic[cfg.Key] = cfg.Childs; |                     dic[cfg.Key] = cfg.Childs; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -54,6 +54,6 @@ public class ConfigSection : IConfigSection | |||||||
|  |  | ||||||
|     /// <summary>已重载。</summary> |     /// <summary>已重载。</summary> | ||||||
|     /// <returns></returns> |     /// <returns></returns> | ||||||
|     public override String ToString() => Childs != null && Childs.Count > 0 ? $"{Key}[{Childs.Count}]" : $"{Key}={Value}"; |     public override String ToString() => Childs?.Count > 0 ? $"{Key}[{Childs.Count}]" : $"{Key}={Value}"; | ||||||
|     #endregion |     #endregion | ||||||
| } | } | ||||||
| @@ -75,7 +75,7 @@ public class InIConfigProvider : FileConfigProvider | |||||||
|         var sb = new StringBuilder(); |         var sb = new StringBuilder(); | ||||||
|         foreach (var item in section.Childs.ToArray()) |         foreach (var item in section.Childs.ToArray()) | ||||||
|         { |         { | ||||||
|             if (item.Childs != null && item.Childs.Count > 0) |             if (item.Childs?.Count > 0) | ||||||
|             { |             { | ||||||
|                 // 段前空一行 |                 // 段前空一行 | ||||||
|                 sb.AppendLine(); |                 sb.AppendLine(); | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ public class WeakAction<TArgs> | |||||||
|             var target = Target; |             var target = Target; | ||||||
|             if (target == null && Method.IsStatic) return true; |             if (target == null && Method.IsStatic) return true; | ||||||
|  |  | ||||||
|             return target != null && target.IsAlive; |             return target?.IsAlive == true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     #endregion |     #endregion | ||||||
|   | |||||||
| @@ -65,7 +65,7 @@ public static class DateExtensions | |||||||
|     /// <param name="beginTime">开始时间</param> |     /// <param name="beginTime">开始时间</param> | ||||||
|     /// <param name="endTime">结束时间</param> |     /// <param name="endTime">结束时间</param> | ||||||
|     /// <returns>时间差</returns> |     /// <returns>时间差</returns> | ||||||
|     public static string GetDiffTime(this in DateTime beginTime, in DateTime endTime) |     public static string GetDiffTime(this DateTime beginTime, DateTime endTime) | ||||||
|     { |     { | ||||||
|         TimeSpan timeDifference = endTime - beginTime; |         TimeSpan timeDifference = endTime - beginTime; | ||||||
|         if (timeDifference.TotalDays >= 1) |         if (timeDifference.TotalDays >= 1) | ||||||
| @@ -88,7 +88,7 @@ public static class DateExtensions | |||||||
|     /// <param name="beginTime">开始时间</param> |     /// <param name="beginTime">开始时间</param> | ||||||
|     /// <param name="endTime">结束时间</param> |     /// <param name="endTime">结束时间</param> | ||||||
|     /// <returns>时间差</returns> |     /// <returns>时间差</returns> | ||||||
|     public static string GetDiffTime(this in DateTimeOffset beginTime, in DateTimeOffset endTime) |     public static string GetDiffTime(this DateTimeOffset beginTime, DateTimeOffset endTime) | ||||||
|     { |     { | ||||||
|         TimeSpan timeDifference = endTime - beginTime; |         TimeSpan timeDifference = endTime - beginTime; | ||||||
|         if (timeDifference.TotalDays >= 1) |         if (timeDifference.TotalDays >= 1) | ||||||
| @@ -108,7 +108,7 @@ public static class DateExtensions | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 返回yyyy-MM-ddTHH:mm:ss.fffffffzzz时间格式字符串 |     /// 返回yyyy-MM-ddTHH:mm:ss.fffffffzzz时间格式字符串 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     public static string ToDefaultDateTimeFormat(this in DateTime dt, TimeSpan offset) |     public static string ToDefaultDateTimeFormat(this DateTime dt, TimeSpan offset) | ||||||
|     { |     { | ||||||
|         if (dt.Kind == DateTimeKind.Utc) |         if (dt.Kind == DateTimeKind.Utc) | ||||||
|             return new DateTimeOffset(dt.ToLocalTime(), offset).ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz"); |             return new DateTimeOffset(dt.ToLocalTime(), offset).ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz"); | ||||||
| @@ -129,7 +129,7 @@ public static class DateExtensions | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 返回yyyy-MM-ddTHH:mm:ss.fffffffzzz时间格式字符串 |     /// 返回yyyy-MM-ddTHH:mm:ss.fffffffzzz时间格式字符串 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     public static string ToDefaultDateTimeFormat(this in DateTime dt) |     public static string ToDefaultDateTimeFormat(this DateTime dt) | ||||||
|     { |     { | ||||||
|         return dt.ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz"); |         return dt.ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz"); | ||||||
|     } |     } | ||||||
| @@ -137,7 +137,7 @@ public static class DateExtensions | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 返回yyyy-MM-dd HH-mm-ss-fff zz时间格式字符串 |     /// 返回yyyy-MM-dd HH-mm-ss-fff zz时间格式字符串 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     public static string ToFileDateTimeFormat(this in DateTime dt) |     public static string ToFileDateTimeFormat(this DateTime dt) | ||||||
|     { |     { | ||||||
|         return ToDefaultDateTimeFormat(dt).Replace(":", "-"); |         return ToDefaultDateTimeFormat(dt).Replace(":", "-"); | ||||||
|     } |     } | ||||||
| @@ -145,7 +145,7 @@ public static class DateExtensions | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 返回yyyy-MM-dd HH-mm-ss-fff zz时间格式字符串 |     /// 返回yyyy-MM-dd HH-mm-ss-fff zz时间格式字符串 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     public static string ToFileDateTimeFormat(this in DateTime dt, TimeSpan offset) |     public static string ToFileDateTimeFormat(this DateTime dt, TimeSpan offset) | ||||||
|     { |     { | ||||||
|         return ToDefaultDateTimeFormat(dt, offset).Replace(":", "-"); |         return ToDefaultDateTimeFormat(dt, offset).Replace(":", "-"); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -6,31 +6,18 @@ namespace ThingsGateway.NewLife.Extension; | |||||||
| /// <summary>网络结点扩展</summary> | /// <summary>网络结点扩展</summary> | ||||||
| public static class EndPointExtensions | public static class EndPointExtensions | ||||||
| { | { | ||||||
|     /// <summary> |  | ||||||
|     ///  |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="endpoint"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static String ToAddress(this EndPoint endpoint) |     public static String ToAddress(this EndPoint endpoint) | ||||||
|     { |     { | ||||||
|         return ((IPEndPoint)endpoint).ToAddress(); |         return ((IPEndPoint)endpoint).ToAddress(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     ///  |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="endpoint"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static String ToAddress(this IPEndPoint endpoint) |     public static String ToAddress(this IPEndPoint endpoint) | ||||||
|     { |     { | ||||||
|         return String.Format("{0}:{1}", endpoint.Address, endpoint.Port); |         return String.Format("{0}:{1}", endpoint.Address, endpoint.Port); | ||||||
|     } |     } | ||||||
|     private static readonly String[] SplitColon = new String[] { ":" }; |     private static readonly String[] SplitColon = new String[] { ":" }; | ||||||
|     /// <summary> |  | ||||||
|     ///  |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="address"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static IPEndPoint ToEndPoint(this String address) |     public static IPEndPoint ToEndPoint(this String address) | ||||||
|     { |     { | ||||||
|         var array = address.Split(SplitColon, StringSplitOptions.RemoveEmptyEntries); |         var array = address.Split(SplitColon, StringSplitOptions.RemoveEmptyEntries); | ||||||
| @@ -44,11 +31,7 @@ public static class EndPointExtensions | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static readonly String[] SplitComma = new String[] { "," }; |     private static readonly String[] SplitComma = new String[] { "," }; | ||||||
|     /// <summary> |  | ||||||
|     ///  |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="addresses"></param> |  | ||||||
|     /// <returns></returns> |  | ||||||
|     public static IEnumerable<IPEndPoint> ToEndPoints(this String addresses) |     public static IEnumerable<IPEndPoint> ToEndPoints(this String addresses) | ||||||
|     { |     { | ||||||
|         var array = addresses.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries); |         var array = addresses.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries); | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user