项目重命名
This commit is contained in:
54
MesETL.App/Helpers/DatabaseHelper.cs
Normal file
54
MesETL.App/Helpers/DatabaseHelper.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Data;
|
||||
using MySqlConnector;
|
||||
|
||||
namespace ConsoleApp2.Helpers;
|
||||
|
||||
public static class DatabaseHelper
|
||||
{
|
||||
public static async Task<DataSet> QueryTableAsync(string connStr, string sql)
|
||||
{
|
||||
await using var conn = new MySqlConnection(connStr);
|
||||
if(conn.State is not ConnectionState.Open)
|
||||
await conn.OpenAsync();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
var ds = new DataSet();
|
||||
new MySqlDataAdapter(cmd).Fill(ds);
|
||||
return ds;
|
||||
}
|
||||
|
||||
public static async Task<object?> QueryScalarAsync(string connStr, string sql)
|
||||
{
|
||||
await using var conn = new MySqlConnection(connStr);
|
||||
if(conn.State is not ConnectionState.Open)
|
||||
await conn.OpenAsync();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
return await cmd.ExecuteScalarAsync();
|
||||
}
|
||||
|
||||
public static async Task<int> NonQueryAsync(string connStr, string sql)
|
||||
{
|
||||
await using var conn = new MySqlConnection(connStr);
|
||||
if(conn.State is not ConnectionState.Open)
|
||||
await conn.OpenAsync();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
return await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
public static async Task<int> TransactionAsync(string connStr, string sql, params MySqlParameter[] parameters)
|
||||
{
|
||||
await using var conn = new MySqlConnection(connStr);
|
||||
if(conn.State is not ConnectionState.Open)
|
||||
await conn.OpenAsync();
|
||||
await using var trans = await conn.BeginTransactionAsync();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = trans;
|
||||
cmd.Parameters.AddRange(parameters);
|
||||
var rows = await cmd.ExecuteNonQueryAsync();
|
||||
await trans.CommitAsync();
|
||||
return rows;
|
||||
}
|
||||
}
|
125
MesETL.App/Helpers/DumpDataHelper.cs
Normal file
125
MesETL.App/Helpers/DumpDataHelper.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using ZstdSharp;
|
||||
|
||||
namespace ConsoleApp2.Helpers;
|
||||
|
||||
public static partial class DumpDataHelper
|
||||
{
|
||||
[GeneratedRegex(@"'.+\.dat'")]
|
||||
private static partial Regex MatchDatFile();
|
||||
[GeneratedRegex(@"\([^)]*\)")]
|
||||
private static partial Regex MatchBrackets();
|
||||
|
||||
/// <summary>
|
||||
/// 从MyDumper导出的SQL文件内容中读取表头
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public static string[] GetCsvHeadersFromSqlFile(string content)
|
||||
{
|
||||
var match = MatchBrackets().Match(content);
|
||||
if (!match.Success)
|
||||
throw new ArgumentException("输入的SQL内容有误,无法提取表头", nameof(content));
|
||||
return ParseHeader(match.ValueSpan);
|
||||
|
||||
string[] ParseHeader(ReadOnlySpan<char> headerStr)
|
||||
{
|
||||
headerStr = headerStr[1..^1];
|
||||
Span<Range> ranges = stackalloc Range[50];
|
||||
var count = headerStr.Split(ranges, ',');
|
||||
var arr = new string[count];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
arr[i] = headerStr[ranges[i]].Trim("@`").ToString(); // 消除列名的反引号,如果是变量则消除@
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从MyDumper导出的Csv文件名解析出表名
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetTableNameFromCsvFileName(ReadOnlySpan<char> filePath)
|
||||
{
|
||||
filePath = filePath[(filePath.LastIndexOf('\\') + 1)..];
|
||||
var firstDotIdx = -1;
|
||||
var secondDotIdx = -1;
|
||||
var times = 0;
|
||||
for (var i = 0; i < filePath.Length; i++)
|
||||
{
|
||||
if (filePath[i] == '.')
|
||||
{
|
||||
++times;
|
||||
if(times == 1)
|
||||
firstDotIdx = i;
|
||||
if (times == 2)
|
||||
{
|
||||
secondDotIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filePath[(firstDotIdx+1)..secondDotIdx].ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从MyDumper导出的SQL文件内容中读取CSV文件名
|
||||
/// </summary>
|
||||
/// <param name="txt"></param>
|
||||
/// <param name="regex"></param>
|
||||
/// <returns></returns>
|
||||
public static Task<string[]> GetCsvFileNamesFromSqlFileAsync(string txt, Regex regex)
|
||||
{
|
||||
//var txt = await File.ReadAllTextAsync(filePath);
|
||||
var matches = regex.Matches(txt);
|
||||
return Task.FromResult(matches.Select(match => match.ValueSpan[1..^1].ToString()).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查字符串是否为16进制
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static bool CheckHexField(string? str)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(str))
|
||||
return false;
|
||||
|
||||
if (str.StartsWith('\"'))
|
||||
return false;
|
||||
|
||||
var isAllDigit = true;
|
||||
|
||||
foreach (var c in str)
|
||||
{
|
||||
if (!char.IsAsciiHexDigit(c))
|
||||
return false;
|
||||
if (!char.IsNumber(c))
|
||||
isAllDigit = false;
|
||||
}
|
||||
|
||||
if (isAllDigit) //避免全数字
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将输入流以ZSTD标准解压为字符串
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> DecompressZstAsStringAsync(Stream stream)
|
||||
{
|
||||
await using var ds = new DecompressionStream(stream);
|
||||
var reader = new StreamReader(ds);
|
||||
return await reader.ReadToEndAsync();
|
||||
}
|
||||
|
||||
}
|
30
MesETL.App/Helpers/Extensions.Dictionary.cs
Normal file
30
MesETL.App/Helpers/Extensions.Dictionary.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace ConsoleApp2.Helpers;
|
||||
|
||||
public static class DictionaryExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的键是否存在来添加或是更新字典
|
||||
/// </summary>
|
||||
/// <param name="this"></param>
|
||||
/// <param name="key">指定的键</param>
|
||||
/// <param name="addValue">如果指定的键不存在,则向字典添加该值</param>
|
||||
/// <param name="updateFactory">如果指定的键存在,则根据该委托的返回值修改字典中对应的值</param>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <returns>添加或是修改后的值</returns>
|
||||
public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, TValue addValue,
|
||||
Func<TKey, TValue, TValue> updateFactory)
|
||||
{
|
||||
if (!@this.TryGetValue(key, out var value))
|
||||
{
|
||||
@this.Add(key, addValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
@this[key] = updateFactory(key, value);
|
||||
}
|
||||
|
||||
return @this[key];
|
||||
}
|
||||
}
|
67
MesETL.App/Helpers/Extensions.IEnumerable.cs
Normal file
67
MesETL.App/Helpers/Extensions.IEnumerable.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ConsoleApp2.Helpers;
|
||||
#nullable disable
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static string ToMarkdownTable<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]T>(this IEnumerable<T> source)
|
||||
{
|
||||
var properties = typeof(T).GetRuntimeProperties();
|
||||
var fields = typeof(T)
|
||||
.GetRuntimeFields()
|
||||
.Where(f => f.IsPublic);
|
||||
|
||||
var gettables = Enumerable.Union(
|
||||
properties.Select(p => new { p.Name, GetValue = (Func<object, object>)p.GetValue, Type = p.PropertyType }),
|
||||
fields.Select(p => new { p.Name, GetValue = (Func<object, object>)p.GetValue, Type = p.FieldType }));
|
||||
|
||||
var maxColumnValues = source
|
||||
.Select(x => gettables.Select(p => p.GetValue(x)?.ToString()?.Length ?? 0))
|
||||
.Union(new[] { gettables.Select(p => p.Name.Length) }) // Include header in column sizes
|
||||
.Aggregate(
|
||||
new int[gettables.Count()].AsEnumerable(),
|
||||
(accumulate, x) => accumulate.Zip(x, Math.Max))
|
||||
.ToArray();
|
||||
|
||||
var columnNames = gettables.Select(p => p.Name);
|
||||
|
||||
var headerLine = "| " + string.Join(" | ", columnNames.Select((n, i) => n.PadRight(maxColumnValues[i]))) + " |";
|
||||
|
||||
var isNumeric = new Func<Type, bool>(type =>
|
||||
type == typeof(Byte) ||
|
||||
type == typeof(SByte) ||
|
||||
type == typeof(UInt16) ||
|
||||
type == typeof(UInt32) ||
|
||||
type == typeof(UInt64) ||
|
||||
type == typeof(Int16) ||
|
||||
type == typeof(Int32) ||
|
||||
type == typeof(Int64) ||
|
||||
type == typeof(Decimal) ||
|
||||
type == typeof(Double) ||
|
||||
type == typeof(Single));
|
||||
|
||||
var rightAlign = new Func<Type, char>(type => isNumeric(type) ? ':' : ' ');
|
||||
|
||||
var headerDataDividerLine =
|
||||
"| " +
|
||||
string.Join(
|
||||
"| ",
|
||||
gettables.Select((g, i) => new string('-', maxColumnValues[i]) + rightAlign(g.Type))) +
|
||||
"|";
|
||||
|
||||
var lines = new[]
|
||||
{
|
||||
headerLine,
|
||||
headerDataDividerLine,
|
||||
}.Union(
|
||||
source
|
||||
.Select(s =>
|
||||
"| " + string.Join(" | ",
|
||||
gettables.Select((n, i) => (n.GetValue(s)?.ToString() ?? "").PadRight(maxColumnValues[i]))) +
|
||||
" |"));
|
||||
|
||||
return lines
|
||||
.Aggregate((p, c) => p + Environment.NewLine + c);
|
||||
}
|
||||
}
|
70
MesETL.App/Helpers/Extensions.String.cs
Normal file
70
MesETL.App/Helpers/Extensions.String.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace ConsoleApp2.Helpers;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 截断字符串
|
||||
/// </summary>
|
||||
/// <param name="this"></param>
|
||||
/// <param name="maxLength">截断长度</param>
|
||||
/// <returns></returns>
|
||||
public static string Omit(this ReadOnlySpan<char> @this, int maxLength)
|
||||
{
|
||||
if (@this.Length > maxLength)
|
||||
return @this[..maxLength].ToString() + "...";
|
||||
return @this.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 截断字符串
|
||||
/// </summary>
|
||||
/// <param name="this"></param>
|
||||
/// <param name="maxLength">截断长度</param>
|
||||
/// <returns></returns>
|
||||
public static string Omit(this string @this, int maxLength) => Omit(@this.AsSpan(), maxLength);
|
||||
|
||||
/// <summary>
|
||||
/// 将16进制字符串转换为字符串
|
||||
/// </summary>
|
||||
/// <param name="hexString"></param>
|
||||
/// <param name="encoding"></param>
|
||||
/// <returns></returns>
|
||||
public static string FromHex(ReadOnlySpan<char> hexString, Encoding? encoding = null)
|
||||
{
|
||||
encoding ??= Encoding.UTF8;
|
||||
|
||||
var realLength = 0;
|
||||
for (var i = hexString.Length - 2; i >= 0; i -= 2)
|
||||
{
|
||||
var b = byte.Parse(hexString.Slice(i, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
if (b != 0) //not NULL character
|
||||
{
|
||||
realLength = i + 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var bytes = new byte[realLength / 2];
|
||||
for (var i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
bytes[i] = byte.Parse(hexString.Slice(i * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
return encoding.GetString(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否为JSON字符串
|
||||
/// </summary>
|
||||
/// <param name="hexStr"></param>
|
||||
/// <returns></returns>
|
||||
public static bool CheckJsonHex(ReadOnlySpan<char> hexStr)
|
||||
{
|
||||
if (hexStr.Length < 2)
|
||||
return false;
|
||||
return FromHex(hexStr[..2]) is ['{'] or ['['];
|
||||
}
|
||||
}
|
19
MesETL.App/Helpers/Extensions.Task.cs
Normal file
19
MesETL.App/Helpers/Extensions.Task.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace ConsoleApp2.Helpers;
|
||||
|
||||
public static class TaskExtensions
|
||||
{
|
||||
public static async Task WaitUntil(Func<bool> condition, int pollDelay = 25, CancellationToken ct = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!condition())
|
||||
{
|
||||
await Task.Delay(pollDelay, ct);
|
||||
}
|
||||
}
|
||||
catch(TaskCanceledException)
|
||||
{
|
||||
// CancellationToken激活时Task.Delay通过抛异常来结束停止等待,不用管它
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user