2121using System . Web ;
2222using FreeSql . Provider . QuestDb ;
2323using System . Net ;
24+ using Microsoft . Extensions . DependencyInjection ;
2425
2526public 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}
0 commit comments