Skip to content

Commit 3b10c5f

Browse files
committed
优化QuestDb IHttpClientFactory及IServiceCollection相关逻辑
1 parent 704bb5f commit 3b10c5f

File tree

3 files changed

+89
-70
lines changed

3 files changed

+89
-70
lines changed

Providers/FreeSql.Provider.QuestDb/QuestDbGlobalExtensions.cs

Lines changed: 83 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using System.Web;
2222
using FreeSql.Provider.QuestDb;
2323
using System.Net;
24+
using Microsoft.Extensions.DependencyInjection;
2425

2526
public static partial class QuestDbGlobalExtensions
2627
{
@@ -35,8 +36,51 @@ public static string FormatQuestDb(this string that, params object[] args) =>
3536

3637
private static readonly QuestDbAdo _questDbAdo = new QuestDbAdo();
3738

38-
public static FreeSqlBuilder UseQuestDbRestAPI(this FreeSqlBuilder build, string host, string username = "",
39-
string password = "") => RestAPIExtension.UseQuestDbRestAPI(build, host, username, password);
39+
/// <summary>
40+
/// 启动QuestDb Http功能
41+
/// </summary>
42+
/// <param name="builder"></param>
43+
/// <param name="host"></param>
44+
/// <param name="username"></param>
45+
/// <param name="password"></param>
46+
/// <returns></returns>
47+
public static FreeSqlBuilder UseQuestDbRestAPI(this FreeSqlBuilder builder, string host, string username = "",
48+
string password = "")
49+
{
50+
//初始化容器,添加HttpClient
51+
ServiceContainer.Initialize(service =>
52+
{
53+
service.AddHttpClient("QuestDb", client => client.BaseAddress = new Uri(host))
54+
.ConfigurePrimaryHttpMessageHandler(handlerBuilder =>
55+
{
56+
//忽略SSL验证
57+
return new HttpClientHandler
58+
{
59+
ClientCertificateOptions = ClientCertificateOption.Manual,
60+
ServerCertificateCustomValidationCallback =
61+
(httpRequestMessage, cert, certChain, policyErrors) => true
62+
};
63+
});
64+
65+
var description = new QuestResetApiFeatures()
66+
{
67+
BaseAddress = host
68+
};
69+
70+
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
71+
{
72+
var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
73+
description.BasicToken = $"Basic {base64}";
74+
}
75+
76+
service.AddSingleton(description);
77+
});
78+
79+
//RestApi需要无参数
80+
builder.UseNoneCommandParameter(true);
81+
82+
return builder;
83+
}
4084

4185
/// <summary>
4286
/// 对于多个时间序列存储在同一个表中的场景,根据时间戳检索给定键或键组合的最新项。
@@ -50,7 +94,7 @@ public static FreeSqlBuilder UseQuestDbRestAPI(this FreeSqlBuilder build, string
5094
public static ISelect<T1> LatestOn<T1, TKey>(this ISelect<T1> select, Expression<Func<T1, DateTime?>> timestamp,
5195
Expression<Func<T1, TKey>> partition)
5296
{
53-
LatestOnExtension.InternelImpl(timestamp, partition);
97+
LatestOnExtension.InternalImpl(timestamp, partition);
5498
return select;
5599
}
56100

@@ -67,7 +111,7 @@ public static ISelect<T1, T2> LatestOn<T1, T2, TKey>(this ISelect<T1, T2> select
67111
Expression<Func<T1, DateTime?>> timestamp,
68112
Expression<Func<T1, TKey>> partition) where T2 : class
69113
{
70-
LatestOnExtension.InternelImpl(timestamp, partition);
114+
LatestOnExtension.InternalImpl(timestamp, partition);
71115
return select;
72116
}
73117

@@ -84,7 +128,7 @@ public static ISelect<T1, T2, T3> LatestOn<T1, T2, T3, TKey>(this ISelect<T1, T2
84128
Expression<Func<T1, DateTime?>> timestamp,
85129
Expression<Func<T1, TKey>> partition) where T2 : class where T3 : class
86130
{
87-
LatestOnExtension.InternelImpl(timestamp, partition);
131+
LatestOnExtension.InternalImpl(timestamp, partition);
88132
return select;
89133
}
90134

@@ -101,7 +145,7 @@ public static ISelect<T1, T2, T3, T4> LatestOn<T1, T2, T3, T4, TKey>(this ISelec
101145
Expression<Func<T1, DateTime?>> timestamp,
102146
Expression<Func<T1, TKey>> partition) where T2 : class where T3 : class where T4 : class
103147
{
104-
LatestOnExtension.InternelImpl(timestamp, partition);
148+
LatestOnExtension.InternalImpl(timestamp, partition);
105149
return select;
106150
}
107151

@@ -162,19 +206,19 @@ private static List<string> SplitByLine(string text)
162206
public static async Task<int> ExecuteQuestDbBulkCopyAsync<T>(this IInsert<T> that,
163207
string dateFormat = "yyyy/M/d H:mm:ss") where T : class
164208
{
165-
//思路:通过提供的RestAPI imp,实现快速复制
166-
if (string.IsNullOrWhiteSpace(RestAPIExtension.BaseUrl))
209+
var features = ServiceContainer.GetService<QuestResetApiFeatures>();
210+
211+
if (string.IsNullOrWhiteSpace(features.BaseAddress))
167212
{
168213
throw new Exception(
169-
"BulkCopy功能需要启用RestAPI,启用方式:new FreeSqlBuilder().UseQuestDbRestAPI(\"localhost:9000\", \"username\", \"password\")");
214+
@"BulkCopy功能需要启用RestAPI,启用方式:new FreeSqlBuilder().UseQuestDbRestAPI(""localhost:9000"", ""username"", ""password"")");
170215
}
171216

172217
var result = 0;
173218

174219
try
175220
{
176-
var client = QuestDbContainer.GetService<IHttpClientFactory>().CreateClient();
177-
var boundary = "---------------" + DateTime.Now.Ticks.ToString("x");
221+
var boundary = $"---------------{DateTime.Now.Ticks:x}";
178222
var list = new List<Hashtable>();
179223
var insert = that as QuestDbInsert<T>;
180224
var name = insert.InternalTableRuleInvoke(); //获取表名
@@ -199,7 +243,7 @@ public static async Task<int> ExecuteQuestDbBulkCopyAsync<T>(this IInsert<T> tha
199243
}
200244
});
201245
var schema = JsonConvert.SerializeObject(list);
202-
using (MemoryStream stream = new MemoryStream())
246+
using (var stream = new MemoryStream())
203247
{
204248
//写入CSV文件
205249
using (var writer = new StreamWriter(stream))
@@ -208,29 +252,27 @@ public static async Task<int> ExecuteQuestDbBulkCopyAsync<T>(this IInsert<T> tha
208252
await csv.WriteRecordsAsync(insert._source);
209253
}
210254

255+
var client = features.HttpClient;
211256
var httpContent = new MultipartFormDataContent(boundary);
212-
if (!string.IsNullOrWhiteSpace(RestAPIExtension.authorization))
213-
client.DefaultRequestHeaders.Add("Authorization", RestAPIExtension.authorization);
257+
if (!string.IsNullOrWhiteSpace(features.BasicToken))
258+
client.DefaultRequestHeaders.Add("Authorization", features.BasicToken);
214259
httpContent.Add(new StringContent(schema), "schema");
215260
httpContent.Add(new ByteArrayContent(stream.ToArray()), "data");
216-
//boundary带双引号 可能导致服务器错误情况
217261
httpContent.Headers.Remove("Content-Type");
218262
httpContent.Headers.TryAddWithoutValidation("Content-Type",
219-
"multipart/form-data; boundary=" + boundary);
263+
$"multipart/form-data; boundary={boundary}");
220264
var httpResponseMessage =
221-
await client.PostAsync($"{RestAPIExtension.BaseUrl}/imp?name={name}", httpContent);
265+
await client.PostAsync($"imp?name={name}", httpContent);
222266
var readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync();
223267
var splitByLine = SplitByLine(readAsStringAsync);
224-
foreach (var s in splitByLine)
268+
foreach (var strings in from s in splitByLine
269+
where s.Contains("Rows")
270+
select s.Split('|')
271+
into strings
272+
where strings[1].Trim() == "Rows imported"
273+
select strings)
225274
{
226-
if (s.Contains("Rows"))
227-
{
228-
var strings = s.Split('|');
229-
if (strings[1].Trim() == "Rows imported")
230-
{
231-
result = Convert.ToInt32(strings[2].Trim());
232-
}
233-
}
275+
result = Convert.ToInt32(strings[2].Trim());
234276
}
235277
}
236278
}
@@ -249,7 +291,8 @@ public static async Task<int> ExecuteQuestDbBulkCopyAsync<T>(this IInsert<T> tha
249291
/// <param name="insert"></param>
250292
/// <param name="dateFormat">导入时,时间格式 默认:yyyy/M/d H:mm:ss</param>
251293
/// <returns></returns>
252-
public static int ExecuteQuestDbBulkCopy<T>(this IInsert<T> insert, string dateFormat = "yyyy/M/d H:mm:ss") where T : class
294+
public static int ExecuteQuestDbBulkCopy<T>(this IInsert<T> insert, string dateFormat = "yyyy/M/d H:mm:ss")
295+
where T : class
253296
{
254297
return ExecuteQuestDbBulkCopyAsync(insert, dateFormat).ConfigureAwait(false).GetAwaiter().GetResult();
255298
}
@@ -294,7 +337,7 @@ internal static void Initialize()
294337
LatestOnString.Value = string.Empty;
295338
}
296339

297-
internal static void InternelImpl<T1, TKey>(Expression<Func<T1, DateTime?>> timestamp,
340+
internal static void InternalImpl<T1, TKey>(Expression<Func<T1, DateTime?>> timestamp,
298341
Expression<Func<T1, TKey>> partition)
299342
{
300343
IsExistence.Value = true;
@@ -308,42 +351,22 @@ internal static void InternelImpl<T1, TKey>(Expression<Func<T1, DateTime?>> time
308351
}
309352
}
310353

311-
static class RestAPIExtension
354+
class QuestResetApiFeatures
312355
{
313-
internal static string BaseUrl = string.Empty;
314-
internal static string authorization = string.Empty;
356+
internal string BaseAddress { get; set; }
357+
358+
internal string BasicToken { get; set; }
315359

316-
internal static async Task<string> ExecAsync(string sql)
360+
internal HttpClient HttpClient => ServiceContainer.GetService<IHttpClientFactory>().CreateClient("QuestDb");
361+
362+
internal async Task<string> ExecAsync(string sql)
317363
{
318364
//HTTP GET 执行SQL
319-
var result = string.Empty;
320-
var client = QuestDbContainer.GetService<IHttpClientFactory>().CreateClient();
321-
var url = $"{BaseUrl}/exec?query={HttpUtility.UrlEncode(sql)}";
322-
if (!string.IsNullOrWhiteSpace(authorization))
323-
client.DefaultRequestHeaders.Add("Authorization", authorization);
324-
var httpResponseMessage = await client.GetAsync(url);
325-
result = await httpResponseMessage.Content.ReadAsStringAsync();
365+
var url = $"exec?query={HttpUtility.UrlEncode(sql)}";
366+
if (!string.IsNullOrWhiteSpace(BasicToken))
367+
HttpClient.DefaultRequestHeaders.Add("Authorization", BasicToken);
368+
var httpResponseMessage = await HttpClient.GetAsync(url);
369+
var result = await httpResponseMessage.Content.ReadAsStringAsync();
326370
return result;
327371
}
328-
329-
internal static FreeSqlBuilder UseQuestDbRestAPI(FreeSqlBuilder buider, string host, string username = "",
330-
string password = "")
331-
{
332-
BaseUrl = host;
333-
if (BaseUrl.EndsWith("/"))
334-
BaseUrl = BaseUrl.Remove(BaseUrl.Length - 1);
335-
336-
if (!BaseUrl.ToLower().StartsWith("http"))
337-
BaseUrl = $"http://{BaseUrl}";
338-
//生成TOKEN
339-
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
340-
{
341-
var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
342-
authorization = $"Basic {base64}";
343-
}
344-
345-
//RestApi需要无参数
346-
buider.UseNoneCommandParameter(true);
347-
return buider;
348-
}
349372
}

Providers/FreeSql.Provider.QuestDb/QuestDbProvider.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,6 @@ static void InitInternal()
118118
Select0Provider._dicMethodDataReaderGetValue[typeof(Guid)] =
119119
typeof(DbDataReader).GetMethod("GetGuid", new Type[] { typeof(int) });
120120

121-
QuestDbContainer.Initialize(service =>
122-
{
123-
service.AddHttpClient();
124-
});
125121
}
126122
}
127123

Providers/FreeSql.Provider.QuestDb/QuestDbContainer.cs renamed to Providers/FreeSql.Provider.QuestDb/ServiceContainer.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Net.Http;
34
using System.Text;
45
using Microsoft.Extensions.DependencyInjection;
56

67
namespace FreeSql.Provider.QuestDb
78
{
8-
internal class QuestDbContainer
9+
internal class ServiceContainer
910
{
10-
//作用于HttpClientFatory
11-
private static IServiceCollection Services;
11+
private static IServiceCollection _services;
1212
internal static IServiceProvider ServiceProvider { get; private set; }
1313

1414
internal static void Initialize(Action<IServiceCollection> service)
1515
{
16-
Services = new ServiceCollection();
17-
service?.Invoke(Services);
18-
ServiceProvider = Services.BuildServiceProvider();
16+
_services = new ServiceCollection();
17+
service?.Invoke(_services);
18+
ServiceProvider = _services.BuildServiceProvider();
1919
}
2020

2121
internal static T GetService<T>()

0 commit comments

Comments
 (0)