From 3b10c5f42868d6251a21b8d47ac784af6a9d5f29 Mon Sep 17 00:00:00 2001 From: d4ilys <963922242@qq.com> Date: Thu, 16 Oct 2025 09:49:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96QuestDb=20IHttpClientFactory?= =?UTF-8?q?=E5=8F=8AIServiceCollection=E7=9B=B8=E5=85=B3=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../QuestDbGlobalExtensions.cs | 143 ++++++++++-------- .../QuestDbProvider.cs | 4 - ...uestDbContainer.cs => ServiceContainer.cs} | 12 +- 3 files changed, 89 insertions(+), 70 deletions(-) rename Providers/FreeSql.Provider.QuestDb/{QuestDbContainer.cs => ServiceContainer.cs} (62%) diff --git a/Providers/FreeSql.Provider.QuestDb/QuestDbGlobalExtensions.cs b/Providers/FreeSql.Provider.QuestDb/QuestDbGlobalExtensions.cs index a047356a0..4d0a20efe 100644 --- a/Providers/FreeSql.Provider.QuestDb/QuestDbGlobalExtensions.cs +++ b/Providers/FreeSql.Provider.QuestDb/QuestDbGlobalExtensions.cs @@ -21,6 +21,7 @@ using System.Threading.Tasks; using System.Web; using FreeSql.Provider.QuestDb; using System.Net; +using Microsoft.Extensions.DependencyInjection; public static partial class QuestDbGlobalExtensions { @@ -35,8 +36,51 @@ public static partial class QuestDbGlobalExtensions private static readonly QuestDbAdo _questDbAdo = new QuestDbAdo(); - public static FreeSqlBuilder UseQuestDbRestAPI(this FreeSqlBuilder build, string host, string username = "", - string password = "") => RestAPIExtension.UseQuestDbRestAPI(build, host, username, password); + /// + /// 启动QuestDb Http功能 + /// + /// + /// + /// + /// + /// + public static FreeSqlBuilder UseQuestDbRestAPI(this FreeSqlBuilder builder, string host, string username = "", + string password = "") + { + //初始化容器,添加HttpClient + ServiceContainer.Initialize(service => + { + service.AddHttpClient("QuestDb", client => client.BaseAddress = new Uri(host)) + .ConfigurePrimaryHttpMessageHandler(handlerBuilder => + { + //忽略SSL验证 + return new HttpClientHandler + { + ClientCertificateOptions = ClientCertificateOption.Manual, + ServerCertificateCustomValidationCallback = + (httpRequestMessage, cert, certChain, policyErrors) => true + }; + }); + + var description = new QuestResetApiFeatures() + { + BaseAddress = host + }; + + if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password)) + { + var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}")); + description.BasicToken = $"Basic {base64}"; + } + + service.AddSingleton(description); + }); + + //RestApi需要无参数 + builder.UseNoneCommandParameter(true); + + return builder; + } /// /// 对于多个时间序列存储在同一个表中的场景,根据时间戳检索给定键或键组合的最新项。 @@ -50,7 +94,7 @@ public static partial class QuestDbGlobalExtensions public static ISelect LatestOn(this ISelect select, Expression> timestamp, Expression> partition) { - LatestOnExtension.InternelImpl(timestamp, partition); + LatestOnExtension.InternalImpl(timestamp, partition); return select; } @@ -67,7 +111,7 @@ public static partial class QuestDbGlobalExtensions Expression> timestamp, Expression> partition) where T2 : class { - LatestOnExtension.InternelImpl(timestamp, partition); + LatestOnExtension.InternalImpl(timestamp, partition); return select; } @@ -84,7 +128,7 @@ public static partial class QuestDbGlobalExtensions Expression> timestamp, Expression> partition) where T2 : class where T3 : class { - LatestOnExtension.InternelImpl(timestamp, partition); + LatestOnExtension.InternalImpl(timestamp, partition); return select; } @@ -101,7 +145,7 @@ public static partial class QuestDbGlobalExtensions Expression> timestamp, Expression> partition) where T2 : class where T3 : class where T4 : class { - LatestOnExtension.InternelImpl(timestamp, partition); + LatestOnExtension.InternalImpl(timestamp, partition); return select; } @@ -162,19 +206,19 @@ public static partial class QuestDbGlobalExtensions public static async Task ExecuteQuestDbBulkCopyAsync(this IInsert that, string dateFormat = "yyyy/M/d H:mm:ss") where T : class { - //思路:通过提供的RestAPI imp,实现快速复制 - if (string.IsNullOrWhiteSpace(RestAPIExtension.BaseUrl)) + var features = ServiceContainer.GetService(); + + if (string.IsNullOrWhiteSpace(features.BaseAddress)) { throw new Exception( - "BulkCopy功能需要启用RestAPI,启用方式:new FreeSqlBuilder().UseQuestDbRestAPI(\"localhost:9000\", \"username\", \"password\")"); + @"BulkCopy功能需要启用RestAPI,启用方式:new FreeSqlBuilder().UseQuestDbRestAPI(""localhost:9000"", ""username"", ""password"")"); } var result = 0; try { - var client = QuestDbContainer.GetService().CreateClient(); - var boundary = "---------------" + DateTime.Now.Ticks.ToString("x"); + var boundary = $"---------------{DateTime.Now.Ticks:x}"; var list = new List(); var insert = that as QuestDbInsert; var name = insert.InternalTableRuleInvoke(); //获取表名 @@ -199,7 +243,7 @@ public static partial class QuestDbGlobalExtensions } }); var schema = JsonConvert.SerializeObject(list); - using (MemoryStream stream = new MemoryStream()) + using (var stream = new MemoryStream()) { //写入CSV文件 using (var writer = new StreamWriter(stream)) @@ -208,29 +252,27 @@ public static partial class QuestDbGlobalExtensions await csv.WriteRecordsAsync(insert._source); } + var client = features.HttpClient; var httpContent = new MultipartFormDataContent(boundary); - if (!string.IsNullOrWhiteSpace(RestAPIExtension.authorization)) - client.DefaultRequestHeaders.Add("Authorization", RestAPIExtension.authorization); + if (!string.IsNullOrWhiteSpace(features.BasicToken)) + client.DefaultRequestHeaders.Add("Authorization", features.BasicToken); httpContent.Add(new StringContent(schema), "schema"); httpContent.Add(new ByteArrayContent(stream.ToArray()), "data"); - //boundary带双引号 可能导致服务器错误情况 httpContent.Headers.Remove("Content-Type"); httpContent.Headers.TryAddWithoutValidation("Content-Type", - "multipart/form-data; boundary=" + boundary); + $"multipart/form-data; boundary={boundary}"); var httpResponseMessage = - await client.PostAsync($"{RestAPIExtension.BaseUrl}/imp?name={name}", httpContent); + await client.PostAsync($"imp?name={name}", httpContent); var readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync(); var splitByLine = SplitByLine(readAsStringAsync); - foreach (var s in splitByLine) + foreach (var strings in from s in splitByLine + where s.Contains("Rows") + select s.Split('|') + into strings + where strings[1].Trim() == "Rows imported" + select strings) { - if (s.Contains("Rows")) - { - var strings = s.Split('|'); - if (strings[1].Trim() == "Rows imported") - { - result = Convert.ToInt32(strings[2].Trim()); - } - } + result = Convert.ToInt32(strings[2].Trim()); } } } @@ -249,7 +291,8 @@ public static partial class QuestDbGlobalExtensions /// /// 导入时,时间格式 默认:yyyy/M/d H:mm:ss /// - public static int ExecuteQuestDbBulkCopy(this IInsert insert, string dateFormat = "yyyy/M/d H:mm:ss") where T : class + public static int ExecuteQuestDbBulkCopy(this IInsert insert, string dateFormat = "yyyy/M/d H:mm:ss") + where T : class { return ExecuteQuestDbBulkCopyAsync(insert, dateFormat).ConfigureAwait(false).GetAwaiter().GetResult(); } @@ -294,7 +337,7 @@ static class LatestOnExtension LatestOnString.Value = string.Empty; } - internal static void InternelImpl(Expression> timestamp, + internal static void InternalImpl(Expression> timestamp, Expression> partition) { IsExistence.Value = true; @@ -308,42 +351,22 @@ static class LatestOnExtension } } -static class RestAPIExtension +class QuestResetApiFeatures { - internal static string BaseUrl = string.Empty; - internal static string authorization = string.Empty; + internal string BaseAddress { get; set; } - internal static async Task ExecAsync(string sql) + internal string BasicToken { get; set; } + + internal HttpClient HttpClient => ServiceContainer.GetService().CreateClient("QuestDb"); + + internal async Task ExecAsync(string sql) { //HTTP GET 执行SQL - var result = string.Empty; - var client = QuestDbContainer.GetService().CreateClient(); - var url = $"{BaseUrl}/exec?query={HttpUtility.UrlEncode(sql)}"; - if (!string.IsNullOrWhiteSpace(authorization)) - client.DefaultRequestHeaders.Add("Authorization", authorization); - var httpResponseMessage = await client.GetAsync(url); - result = await httpResponseMessage.Content.ReadAsStringAsync(); + var url = $"exec?query={HttpUtility.UrlEncode(sql)}"; + if (!string.IsNullOrWhiteSpace(BasicToken)) + HttpClient.DefaultRequestHeaders.Add("Authorization", BasicToken); + var httpResponseMessage = await HttpClient.GetAsync(url); + var result = await httpResponseMessage.Content.ReadAsStringAsync(); return result; } - - internal static FreeSqlBuilder UseQuestDbRestAPI(FreeSqlBuilder buider, string host, string username = "", - string password = "") - { - BaseUrl = host; - if (BaseUrl.EndsWith("/")) - BaseUrl = BaseUrl.Remove(BaseUrl.Length - 1); - - if (!BaseUrl.ToLower().StartsWith("http")) - BaseUrl = $"http://{BaseUrl}"; - //生成TOKEN - if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password)) - { - var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}")); - authorization = $"Basic {base64}"; - } - - //RestApi需要无参数 - buider.UseNoneCommandParameter(true); - return buider; - } } \ No newline at end of file diff --git a/Providers/FreeSql.Provider.QuestDb/QuestDbProvider.cs b/Providers/FreeSql.Provider.QuestDb/QuestDbProvider.cs index f937b8290..31c038006 100644 --- a/Providers/FreeSql.Provider.QuestDb/QuestDbProvider.cs +++ b/Providers/FreeSql.Provider.QuestDb/QuestDbProvider.cs @@ -118,10 +118,6 @@ namespace FreeSql.QuestDb Select0Provider._dicMethodDataReaderGetValue[typeof(Guid)] = typeof(DbDataReader).GetMethod("GetGuid", new Type[] { typeof(int) }); - QuestDbContainer.Initialize(service => - { - service.AddHttpClient(); - }); } } diff --git a/Providers/FreeSql.Provider.QuestDb/QuestDbContainer.cs b/Providers/FreeSql.Provider.QuestDb/ServiceContainer.cs similarity index 62% rename from Providers/FreeSql.Provider.QuestDb/QuestDbContainer.cs rename to Providers/FreeSql.Provider.QuestDb/ServiceContainer.cs index fb678d721..31049dcf3 100644 --- a/Providers/FreeSql.Provider.QuestDb/QuestDbContainer.cs +++ b/Providers/FreeSql.Provider.QuestDb/ServiceContainer.cs @@ -1,21 +1,21 @@ using System; using System.Collections.Generic; +using System.Net.Http; using System.Text; using Microsoft.Extensions.DependencyInjection; namespace FreeSql.Provider.QuestDb { - internal class QuestDbContainer + internal class ServiceContainer { - //作用于HttpClientFatory - private static IServiceCollection Services; + private static IServiceCollection _services; internal static IServiceProvider ServiceProvider { get; private set; } internal static void Initialize(Action service) { - Services = new ServiceCollection(); - service?.Invoke(Services); - ServiceProvider = Services.BuildServiceProvider(); + _services = new ServiceCollection(); + service?.Invoke(_services); + ServiceProvider = _services.BuildServiceProvider(); } internal static T GetService()