From e7be0ce07e78c05e3ce3384eb5b2db88228ed383 Mon Sep 17 00:00:00 2001
From: sunkaixuan <610262374@qq.com>
Date: Thu, 21 Mar 2024 02:21:52 +0800
Subject: [PATCH] Add QuestDb rest api
---
.../HttpExtensions.cs | 15 ++
.../QuestDbRestAPHelper.cs | 45 +++++
.../QuestDbRestAPI.cs | 166 ++++++++++++++++++
.../SqlSugar.QuestDb.RestAPI.csproj | 16 ++
Src/Asp.NetCore2/SqlSugarCore.sln | 17 +-
5 files changed, 258 insertions(+), 1 deletion(-)
create mode 100644 Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/HttpExtensions.cs
create mode 100644 Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/QuestDbRestAPHelper.cs
create mode 100644 Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/QuestDbRestAPI.cs
create mode 100644 Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/SqlSugar.QuestDb.RestAPI.csproj
diff --git a/Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/HttpExtensions.cs b/Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/HttpExtensions.cs
new file mode 100644
index 000000000..1643d0a5e
--- /dev/null
+++ b/Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/HttpExtensions.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SqlSugar
+{
+ public static class ISqlSugarClientExtensions
+ {
+ public static QuestDbRestAPI RestApi(this ISqlSugarClient db)
+ {
+ return new QuestDbRestAPI(db);
+ }
+ }
+}
diff --git a/Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/QuestDbRestAPHelper.cs b/Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/QuestDbRestAPHelper.cs
new file mode 100644
index 000000000..89c8477ab
--- /dev/null
+++ b/Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/QuestDbRestAPHelper.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+using Newtonsoft.Json;
+using CsvHelper;
+
+namespace SqlSugar
+{
+ internal class QuestDbRestAPHelper
+ {
+
+ ///
+ /// 逐行读取,包含空行
+ ///
+ ///
+ ///
+ public static List SplitByLine(string text)
+ {
+ List lines = new List();
+ byte[] array = Encoding.UTF8.GetBytes(text);
+ using (MemoryStream stream = new MemoryStream(array))
+ {
+ using (var sr = new StreamReader(stream))
+ {
+ string line = sr.ReadLine();
+ while (line != null)
+ {
+ lines.Add(line);
+ line = sr.ReadLine();
+ }
+ }
+ }
+
+ return lines;
+ }
+
+
+ }
+}
diff --git a/Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/QuestDbRestAPI.cs b/Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/QuestDbRestAPI.cs
new file mode 100644
index 000000000..46ece2d1b
--- /dev/null
+++ b/Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/QuestDbRestAPI.cs
@@ -0,0 +1,166 @@
+using CsvHelper;
+using Newtonsoft.Json;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+
+namespace SqlSugar
+{
+
+ public class QuestDbRestAPI
+ {
+ internal string url = string.Empty;
+ internal string authorization = string.Empty;
+ ISqlSugarClient db;
+ public QuestDbRestAPI(ISqlSugarClient db)
+ {
+ this.db = db;
+ string host = "";
+ string username = "";
+ string password = "";
+
+ url = host;
+ if (url.EndsWith("/"))
+ url = url.Remove(url.Length - 1);
+
+ if (!url.ToLower().StartsWith("http"))
+ url = $"http://{url}";
+ //生成TOKEN
+ if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
+ {
+ var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
+ authorization = $"Basic {base64}";
+ }
+ }
+
+
+ public async Task ExecuteCommandAsync(string sql)
+ {
+ //HTTP GET 执行SQL
+ var result = string.Empty;
+ var client = new HttpClient();
+ var url = $"{this.url}/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();
+ return result;
+ }
+
+ public string ExecuteCommand(string sql)
+ {
+ return ExecuteCommandAsync(sql).GetAwaiter().GetResult();
+ }
+ ///
+ /// 批量快速插入
+ ///
+ ///
+ ///
+ /// 导入时,时间格式 默认:yyyy/M/d H:mm:ss
+ ///
+ public async Task BulkCopyAsync(List insertList, string dateFormat = "yyyy/M/d H:mm:ss") where T : class
+ {
+
+ if (string.IsNullOrWhiteSpace(url))
+ {
+ throw new Exception("BulkCopy功能需要启用RestAPI,程序启动时执行:RestAPIExtension.UseQuestDbRestAPI(\"localhost:9000\", \"username\", \"password\")");
+ }
+ var result = 0;
+ var fileName = $"{Guid.NewGuid()}.csv";
+ var filePath = Path.Combine(AppContext.BaseDirectory, fileName);
+ try
+ {
+ var client = new HttpClient();
+ var boundary = "---------------" + DateTime.Now.Ticks.ToString("x");
+ var list = new List();
+ var name = db.EntityMaintenance.GetEntityInfo().DbTableName; //获取表名
+ db.DbMaintenance.GetColumnInfosByTableName(name).ForEach(d =>
+ {
+ if (d.DataType == "TIMESTAMP")
+ {
+ list.Add(new Hashtable()
+ {
+ { "name", d.DbColumnName },
+ { "type", d.DataType },
+ { "pattern", dateFormat}
+ });
+ }
+ else
+ {
+ list.Add(new Hashtable()
+ {
+ { "name", d.DbColumnName },
+ { "type", d.DataType }
+ });
+ }
+ });
+ var schema = JsonConvert.SerializeObject(list);
+ //写入CSV文件
+ using (var writer = new StreamWriter(filePath))
+ using (var csv = new CsvWriter(writer, CultureInfo.CurrentCulture))
+ {
+ await csv.WriteRecordsAsync(insertList);
+ }
+
+ var httpContent = new MultipartFormDataContent(boundary);
+ if (!string.IsNullOrWhiteSpace(this.authorization))
+ client.DefaultRequestHeaders.Add("Authorization", this.authorization);
+ httpContent.Add(new StringContent(schema), "schema");
+ httpContent.Add(new ByteArrayContent(File.ReadAllBytes(filePath)), "data");
+ //boundary带双引号 可能导致服务器错误情况
+ httpContent.Headers.Remove("Content-Type");
+ httpContent.Headers.TryAddWithoutValidation("Content-Type",
+ "multipart/form-data; boundary=" + boundary);
+ var httpResponseMessage =
+ await client.PostAsync($"{this.url}/imp?name={name}", httpContent);
+ var readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync();
+ var splitByLine = QuestDbRestAPHelper.SplitByLine(readAsStringAsync);
+ foreach (var s in splitByLine)
+ {
+ if (s.Contains("Rows"))
+ {
+ var strings = s.Split('|');
+ if (strings[1].Trim() == "Rows imported")
+ {
+ result = Convert.ToInt32(strings[2].Trim());
+ }
+ }
+ }
+ }
+ catch (Exception)
+ {
+ throw;
+ }
+ finally
+ {
+ try
+ {
+ File.Delete(filePath);
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// 批量快速插入
+ ///
+ ///
+ ///
+ /// 导入时,时间格式 默认:yyyy/M/d H:mm:ss
+ ///
+ public int BulkCopy(List insertList, string dateFormat = "yyyy/M/d H:mm:ss") where T : class
+ {
+ return BulkCopyAsync(insertList, dateFormat).GetAwaiter().GetResult();
+ }
+ }
+}
diff --git a/Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/SqlSugar.QuestDb.RestAPI.csproj b/Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/SqlSugar.QuestDb.RestAPI.csproj
new file mode 100644
index 000000000..4c2167ecf
--- /dev/null
+++ b/Src/Asp.NetCore2/SqlSugar.QuestDb.RestApi/SqlSugar.QuestDb.RestAPI.csproj
@@ -0,0 +1,16 @@
+
+
+
+ netstandard2.1
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Src/Asp.NetCore2/SqlSugarCore.sln b/Src/Asp.NetCore2/SqlSugarCore.sln
index b68e20c2c..018501a90 100644
--- a/Src/Asp.NetCore2/SqlSugarCore.sln
+++ b/Src/Asp.NetCore2/SqlSugarCore.sln
@@ -68,7 +68,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlSugar.TDengineCore", "Sq
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1TDengineTest", "TDengineTest\1TDengineTest.csproj", "{AFBF6813-DA87-4621-9659-5D123234CDDF}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "2VastbaseTest", "VastbaseTest\2VastbaseTest.csproj", "{FA0F2233-7BD5-49A0-8333-BA471509F2D7}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "2VastbaseTest", "VastbaseTest\2VastbaseTest.csproj", "{FA0F2233-7BD5-49A0-8333-BA471509F2D7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlSugar.QuestDb.RestAPI", "SqlSugar.QuestDb.RestApi\SqlSugar.QuestDb.RestAPI.csproj", "{6DED25D0-660B-468A-AD61-7646345145F5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -440,6 +442,18 @@ Global
{FA0F2233-7BD5-49A0-8333-BA471509F2D7}.Release|ARM32.Build.0 = Release|Any CPU
{FA0F2233-7BD5-49A0-8333-BA471509F2D7}.Release|x86.ActiveCfg = Release|Any CPU
{FA0F2233-7BD5-49A0-8333-BA471509F2D7}.Release|x86.Build.0 = Release|Any CPU
+ {6DED25D0-660B-468A-AD61-7646345145F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6DED25D0-660B-468A-AD61-7646345145F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6DED25D0-660B-468A-AD61-7646345145F5}.Debug|ARM32.ActiveCfg = Debug|Any CPU
+ {6DED25D0-660B-468A-AD61-7646345145F5}.Debug|ARM32.Build.0 = Debug|Any CPU
+ {6DED25D0-660B-468A-AD61-7646345145F5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6DED25D0-660B-468A-AD61-7646345145F5}.Debug|x86.Build.0 = Debug|Any CPU
+ {6DED25D0-660B-468A-AD61-7646345145F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6DED25D0-660B-468A-AD61-7646345145F5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6DED25D0-660B-468A-AD61-7646345145F5}.Release|ARM32.ActiveCfg = Release|Any CPU
+ {6DED25D0-660B-468A-AD61-7646345145F5}.Release|ARM32.Build.0 = Release|Any CPU
+ {6DED25D0-660B-468A-AD61-7646345145F5}.Release|x86.ActiveCfg = Release|Any CPU
+ {6DED25D0-660B-468A-AD61-7646345145F5}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -452,6 +466,7 @@ Global
{517D3B1E-EECB-43C3-83B9-33613F45908A} = {88992AAF-146B-4253-9AD7-493E8F415B57}
{7CEB3DFE-8337-4B83-AE6A-D159D73E3CD3} = {88992AAF-146B-4253-9AD7-493E8F415B57}
{A8FDDB0E-835A-4042-A955-66A2DB98207D} = {88992AAF-146B-4253-9AD7-493E8F415B57}
+ {6DED25D0-660B-468A-AD61-7646345145F5} = {88992AAF-146B-4253-9AD7-493E8F415B57}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {230A85B9-54F1-41B1-B1DA-80086581B2B4}