2024-03-21 02:21:52 +08:00
|
|
|
|
using CsvHelper;
|
2024-03-31 20:03:40 +08:00
|
|
|
|
using CsvHelper.Configuration;
|
2024-03-25 10:56:22 +08:00
|
|
|
|
using CsvHelper.TypeConversion;
|
2024-03-21 02:21:52 +08:00
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
2024-03-21 02:43:44 +08:00
|
|
|
|
using System.Data.Common;
|
2024-03-21 02:21:52 +08:00
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Net.Http;
|
|
|
|
|
using System.Text;
|
2024-03-21 04:19:42 +08:00
|
|
|
|
using System.Threading;
|
2024-03-21 02:21:52 +08:00
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Web;
|
2024-03-21 02:43:44 +08:00
|
|
|
|
using System.Xml.Linq;
|
2024-03-31 20:03:40 +08:00
|
|
|
|
using System.Linq;
|
2024-03-21 02:21:52 +08:00
|
|
|
|
namespace SqlSugar
|
2024-03-21 05:13:39 +08:00
|
|
|
|
{
|
2024-03-21 02:43:44 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// QuestDb RestAPI
|
|
|
|
|
/// </summary>
|
2024-03-21 02:21:52 +08:00
|
|
|
|
public class QuestDbRestAPI
|
|
|
|
|
{
|
2024-03-21 05:13:39 +08:00
|
|
|
|
internal string url = string.Empty;
|
|
|
|
|
internal string authorization = string.Empty;
|
2024-03-21 04:19:42 +08:00
|
|
|
|
internal static Random random = new Random();
|
2024-03-25 15:37:01 +08:00
|
|
|
|
//can be modified
|
2024-04-13 11:40:43 +08:00
|
|
|
|
public static int HttpPort { get; set; }= 9000;
|
|
|
|
|
public static string UserName { get; set; } = null;
|
|
|
|
|
public static string Password { get; set; } = null;
|
2024-03-21 02:21:52 +08:00
|
|
|
|
ISqlSugarClient db;
|
|
|
|
|
public QuestDbRestAPI(ISqlSugarClient db)
|
|
|
|
|
{
|
|
|
|
|
|
2024-03-21 02:43:44 +08:00
|
|
|
|
var builder = new DbConnectionStringBuilder();
|
|
|
|
|
builder.ConnectionString = db.CurrentConnectionConfig.ConnectionString;
|
|
|
|
|
this.db = db;
|
|
|
|
|
string host = String.Empty;
|
|
|
|
|
string username = String.Empty;
|
|
|
|
|
string password = String.Empty;
|
|
|
|
|
QuestDbRestAPHelper.SetRestApiInfo(builder, ref host, ref username, ref password);
|
2024-04-13 11:40:43 +08:00
|
|
|
|
if (!string.IsNullOrEmpty(QuestDbRestAPI.UserName))
|
|
|
|
|
{
|
|
|
|
|
username = QuestDbRestAPI.UserName;
|
|
|
|
|
}
|
|
|
|
|
if (!string.IsNullOrEmpty(QuestDbRestAPI.Password))
|
|
|
|
|
{
|
|
|
|
|
password = QuestDbRestAPI.Password;
|
|
|
|
|
}
|
2024-03-21 02:43:44 +08:00
|
|
|
|
BindHost(host, username, password);
|
2024-03-21 02:21:52 +08:00
|
|
|
|
}
|
2024-03-21 02:43:44 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 执行SQL异步
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="sql"></param>
|
|
|
|
|
/// <returns></returns>
|
2024-03-21 05:13:39 +08:00
|
|
|
|
public async Task<string> ExecuteCommandAsync(string sql)
|
2024-03-21 02:21:52 +08:00
|
|
|
|
{
|
|
|
|
|
//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;
|
|
|
|
|
}
|
2024-03-21 02:43:44 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 执行SQL
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="sql"></param>
|
|
|
|
|
/// <returns></returns>
|
2024-03-21 05:13:39 +08:00
|
|
|
|
public string ExecuteCommand(string sql)
|
2024-03-21 02:21:52 +08:00
|
|
|
|
{
|
|
|
|
|
return ExecuteCommandAsync(sql).GetAwaiter().GetResult();
|
|
|
|
|
}
|
2024-03-21 05:13:39 +08:00
|
|
|
|
|
2024-03-21 05:53:09 +08:00
|
|
|
|
public async Task<int> BulkCopyAsync<T>(T insertData, string dateFormat = "yyyy/M/d H:mm:ss") where T:class,new()
|
2024-03-21 05:13:39 +08:00
|
|
|
|
{
|
|
|
|
|
if (db.CurrentConnectionConfig.MoreSettings == null)
|
|
|
|
|
db.CurrentConnectionConfig.MoreSettings = new ConnMoreSettings();
|
|
|
|
|
db.CurrentConnectionConfig.MoreSettings.DisableNvarchar = true;
|
|
|
|
|
var sql= db.Insertable(insertData).ToSqlString();
|
2024-03-21 05:47:18 +08:00
|
|
|
|
var result = await ExecuteCommandAsync(sql);
|
|
|
|
|
return result.ToUpper().Contains("OK")?1:0;
|
2024-03-21 05:13:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-21 05:53:09 +08:00
|
|
|
|
public int BulkCopy<T>(T insertData, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
2024-03-21 05:13:39 +08:00
|
|
|
|
{
|
2024-03-21 12:37:37 +08:00
|
|
|
|
return BulkCopyAsync(insertData, dateFormat).GetAwaiter().GetResult();
|
2024-03-21 05:13:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-30 11:21:46 +08:00
|
|
|
|
public QuestDbPageSizeBulkCopy PageSize(int pageSize)
|
|
|
|
|
{
|
|
|
|
|
QuestDbPageSizeBulkCopy result = new QuestDbPageSizeBulkCopy(this,pageSize,db);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2024-03-21 05:13:39 +08:00
|
|
|
|
|
2024-03-21 02:21:52 +08:00
|
|
|
|
/// <summary>
|
2024-03-21 02:43:44 +08:00
|
|
|
|
/// 批量快速插入异步
|
2024-03-21 02:21:52 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
|
|
/// <param name="that"></param>
|
|
|
|
|
/// <param name="dateFormat">导入时,时间格式 默认:yyyy/M/d H:mm:ss</param>
|
|
|
|
|
/// <returns></returns>
|
2024-03-21 05:53:09 +08:00
|
|
|
|
public async Task<int> BulkCopyAsync<T>(List<T> insertList, string dateFormat = "yyyy/M/d H:mm:ss") where T : class,new()
|
2024-03-21 02:21:52 +08:00
|
|
|
|
{
|
2024-03-21 04:19:42 +08:00
|
|
|
|
|
2024-03-21 02:21:52 +08:00
|
|
|
|
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<Hashtable>();
|
2024-03-21 04:19:42 +08:00
|
|
|
|
var name = db.EntityMaintenance.GetEntityInfo<T>().DbTableName;
|
2024-03-21 04:41:17 +08:00
|
|
|
|
|
|
|
|
|
var key = "QuestDbBulkCopy" + typeof(T).FullName + typeof(T).GetHashCode();
|
2024-03-21 04:19:42 +08:00
|
|
|
|
var columns = new ReflectionInoCacheService().GetOrCreate(key, () =>
|
|
|
|
|
db.CopyNew().DbMaintenance.GetColumnInfosByTableName(name));
|
|
|
|
|
columns.ForEach(d =>
|
2024-03-21 02:21:52 +08:00
|
|
|
|
{
|
|
|
|
|
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))
|
|
|
|
|
{
|
2024-03-25 10:56:22 +08:00
|
|
|
|
var options = new TypeConverterOptions { Formats = new[] { GetDefaultFormat() } };
|
|
|
|
|
csv.Context.TypeConverterOptionsCache.AddOptions<DateTime>(options);
|
2024-03-31 20:03:40 +08:00
|
|
|
|
CsvCreating<T>(csv);
|
2024-03-21 02:21:52 +08:00
|
|
|
|
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 =
|
2024-03-21 04:41:17 +08:00
|
|
|
|
await Post(client, name, httpContent);
|
2024-03-21 02:21:52 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-31 20:03:40 +08:00
|
|
|
|
private void CsvCreating<T>(CsvWriter csv) where T : class, new()
|
|
|
|
|
{
|
|
|
|
|
var entityColumns = db.EntityMaintenance.GetEntityInfo<T>().Columns;
|
2024-04-20 16:43:35 +08:00
|
|
|
|
if (entityColumns.Any(it => it.IsIgnore||it.UnderType?.IsEnum==true))
|
2024-03-31 20:03:40 +08:00
|
|
|
|
{
|
|
|
|
|
var customMap = new DefaultClassMap<T>();
|
|
|
|
|
foreach (var item in entityColumns.Where(it => !it.IsIgnore))
|
|
|
|
|
{
|
2024-04-20 16:43:35 +08:00
|
|
|
|
var memberMap = customMap.Map(typeof(T), item.PropertyInfo).Name(item.PropertyName);
|
|
|
|
|
if (item.UnderType?.IsEnum==true
|
|
|
|
|
&&item.SqlParameterDbType==null
|
|
|
|
|
&&db.CurrentConnectionConfig?.MoreSettings?.TableEnumIsString!=true)
|
|
|
|
|
{
|
|
|
|
|
memberMap.TypeConverter<CsvHelperEnumToIntConverter>();
|
|
|
|
|
}
|
2024-03-31 20:03:40 +08:00
|
|
|
|
}
|
|
|
|
|
csv.Context.RegisterClassMap(customMap);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-25 10:56:22 +08:00
|
|
|
|
private static string GetDefaultFormat()
|
|
|
|
|
{
|
|
|
|
|
return "yyyy-MM-ddTHH:mm:ss.fffffff";
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-21 04:41:17 +08:00
|
|
|
|
private Task<HttpResponseMessage> Post(HttpClient client, string name, MultipartFormDataContent httpContent)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return client.PostAsync($"{this.url}/imp?name={name}", httpContent);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-21 02:21:52 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 批量快速插入
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
|
|
/// <param name="that"></param>
|
|
|
|
|
/// <param name="dateFormat">导入时,时间格式 默认:yyyy/M/d H:mm:ss</param>
|
|
|
|
|
/// <returns></returns>
|
2024-03-21 05:53:09 +08:00
|
|
|
|
public int BulkCopy<T>(List<T> insertList, string dateFormat = "yyyy/M/d H:mm:ss") where T : class,new()
|
2024-03-21 02:21:52 +08:00
|
|
|
|
{
|
|
|
|
|
return BulkCopyAsync(insertList, dateFormat).GetAwaiter().GetResult();
|
2024-03-21 02:43:44 +08:00
|
|
|
|
}
|
|
|
|
|
private void BindHost(string host, string username, string password)
|
|
|
|
|
{
|
2024-03-25 15:37:01 +08:00
|
|
|
|
url = host + ":"+ HttpPort;
|
2024-03-21 02:43:44 +08:00
|
|
|
|
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}";
|
|
|
|
|
}
|
2024-03-21 02:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|