MES-ETL/ConsoleApp2/Services/MySqlDestination.cs

182 lines
5.8 KiB
C#
Raw Normal View History

2024-01-16 15:35:54 +08:00
using System.Text;
2023-12-29 16:16:05 +08:00
using ConsoleApp2.Helpers;
2024-01-16 18:00:23 +08:00
using ConsoleApp2.Options;
2023-12-29 16:16:05 +08:00
using Microsoft.Extensions.Logging;
2024-01-16 18:00:23 +08:00
using Microsoft.Extensions.Options;
2023-12-29 16:16:05 +08:00
using MySqlConnector;
2024-01-12 16:50:37 +08:00
using ServiceStack;
2023-12-29 16:16:05 +08:00
namespace ConsoleApp2.Services;
2024-01-04 09:00:44 +08:00
/// <summary>
/// Mysql导出
/// </summary>
2023-12-29 16:16:05 +08:00
public class MySqlDestination : IDisposable, IAsyncDisposable
{
private readonly Dictionary<string, IList<DataRecord>> _recordCache;
private readonly MySqlConnection _conn;
private readonly ILogger _logger;
2024-01-12 16:50:37 +08:00
private readonly ProcessContext _context;
2024-01-16 15:35:54 +08:00
private readonly IOptions<DataTransformOptions> _transformOptions;
public MySqlDestination(string connStr, ILogger logger, ProcessContext context, IOptions<DataTransformOptions> transformOptions)
2023-12-29 16:16:05 +08:00
{
_conn = new MySqlConnection(connStr);
_conn.Open();
_recordCache = new Dictionary<string, IList<DataRecord>>();
_logger = logger;
2024-01-12 16:50:37 +08:00
_context = context;
2024-01-16 15:35:54 +08:00
_transformOptions = transformOptions;
2024-01-12 16:50:37 +08:00
}
2024-01-16 15:35:54 +08:00
2023-12-29 16:16:05 +08:00
public Task WriteRecordAsync(DataRecord record)
{
_recordCache.AddOrUpdate(record.TableName, [record], (key, value) =>
{
value.Add(record);
return value;
});
return Task.CompletedTask;
}
public async Task WriteRecordsAsync(IEnumerable<DataRecord> records)
{
foreach (var record in records)
{
await WriteRecordAsync(record);
}
}
2024-01-16 15:35:54 +08:00
public async Task FlushAsync(int maxAllowPacket)
2023-12-29 16:16:05 +08:00
{
if (_recordCache.Count == 0)
return;
2024-01-12 16:50:37 +08:00
2024-01-15 17:26:44 +08:00
var cmd = _conn.CreateCommand();
cmd.CommandTimeout = 3 * 60;
2024-01-16 15:35:54 +08:00
2023-12-29 16:16:05 +08:00
try
{
2024-01-16 15:35:54 +08:00
var excuseList = GetExcuseList(_recordCache, maxAllowPacket).ToList();
2024-01-15 17:26:44 +08:00
foreach (var insertSql in excuseList)
{
cmd.CommandText = insertSql;
await cmd.ExecuteNonQueryAsync();
2024-01-17 11:53:44 +08:00
2024-01-15 17:26:44 +08:00
}
2023-12-29 16:16:05 +08:00
_recordCache.Clear();
}
catch (Exception e)
{
2024-01-15 17:26:44 +08:00
_logger.LogCritical(e, "Error when flushing records, sql: {Sql}", cmd.CommandText.Omit(1000));
2024-01-12 16:50:37 +08:00
_context.AddException(e);
2023-12-29 16:16:05 +08:00
throw;
}
2024-01-04 09:00:44 +08:00
finally
{
2024-01-15 17:26:44 +08:00
await cmd.DisposeAsync();
2024-01-04 09:00:44 +08:00
}
2023-12-29 16:16:05 +08:00
}
2024-01-16 15:35:54 +08:00
public IEnumerable<string> GetExcuseList(IDictionary<string, IList<DataRecord>> tableRecords,int maxAllowPacket)
2023-12-29 16:16:05 +08:00
{
2024-01-16 15:35:54 +08:00
var sb = new StringBuilder();
2023-12-29 16:16:05 +08:00
foreach (var (tableName, records) in tableRecords)
{
if (records.Count == 0)
continue;
2024-01-16 15:35:54 +08:00
var recordIdx = 0;
StartBuild:
var noCommas = true;
// INSERT INTO ... VALUES >>>
sb.Append($"INSERT INTO `{tableName}`(");
2023-12-29 16:16:05 +08:00
for (var i = 0; i < records[0].Headers.Length; i++)
{
var header = records[0].Headers[i];
2024-01-16 15:35:54 +08:00
sb.Append($"`{header}`");
2023-12-29 16:16:05 +08:00
if (i != records[0].Headers.Length - 1)
2024-01-16 15:35:54 +08:00
sb.Append(',');
2023-12-29 16:16:05 +08:00
}
2024-01-16 15:35:54 +08:00
sb.Append(") VALUES ");
// ([FIELDS]), >>>
for (;recordIdx < records.Count; recordIdx++)
2023-12-29 16:16:05 +08:00
{
2024-01-16 15:35:54 +08:00
var record = records[recordIdx];
var recordSb = new StringBuilder();
2024-01-12 16:50:37 +08:00
recordSb.Append('(');
2024-01-16 15:35:54 +08:00
for (var fieldIdx = 0; fieldIdx < record.Fields.Length; fieldIdx++)
2023-12-29 16:16:05 +08:00
{
2024-01-16 15:35:54 +08:00
var field = record.Fields[fieldIdx];
// 在这里处理特殊列
#region HandleFields
if (field == "\\N")
{
recordSb.Append("NULL");
goto Escape;
}
switch (_transformOptions.Value.GetColumnType(record.TableName, record.Headers[fieldIdx]))
2024-01-15 17:26:44 +08:00
{
2024-01-16 15:35:54 +08:00
case ColumnType.Text:
recordSb.Append(string.IsNullOrEmpty(field)
? "''"
: _transformOptions.Value.TransformBinary?.Invoke(field) ?? field);
break;
case ColumnType.Blob:
if (string.IsNullOrEmpty(field))
recordSb.Append("''");
2024-01-16 15:35:54 +08:00
else recordSb.Append($"0x{field}");
break;
case ColumnType.UnDefine:
default:
recordSb.Append(field);
break;
2024-01-15 17:26:44 +08:00
}
2024-01-16 15:35:54 +08:00
Escape:
#endregion
if (fieldIdx != record.Fields.Length - 1)
2024-01-12 16:50:37 +08:00
recordSb.Append(',');
2023-12-29 16:16:05 +08:00
}
2024-01-12 16:50:37 +08:00
recordSb.Append(')');
2023-12-29 16:16:05 +08:00
2024-01-16 15:35:54 +08:00
// 若字符数量即将大于限制则返回SQL清空StringBuilder保留当前记录的索引值然后转到StartBuild标签重新开始一轮INSERT
if (sb.Length + recordSb.Length + 1 > maxAllowPacket)
2024-01-12 16:50:37 +08:00
{
2024-01-16 15:35:54 +08:00
sb.Append(';');
yield return sb.ToString();
sb.Clear();
goto StartBuild;
2024-01-12 16:50:37 +08:00
}
2024-01-16 15:35:54 +08:00
if (!noCommas)
sb.Append(',').AppendLine();
noCommas = false;
sb.Append(recordSb); // StringBuilder.Append(StringBuilder)不会分配多余的内存
2024-01-12 16:50:37 +08:00
}
2024-01-16 15:35:54 +08:00
sb.Append(';');
yield return sb.ToString();
sb.Clear();
2024-01-15 17:26:44 +08:00
}
2023-12-29 16:16:05 +08:00
}
public void Dispose()
{
2024-01-04 09:00:44 +08:00
_conn.Close();
2023-12-29 16:16:05 +08:00
_conn.Dispose();
}
public async ValueTask DisposeAsync()
{
2024-01-04 09:00:44 +08:00
await _conn.CloseAsync();
2023-12-29 16:16:05 +08:00
await _conn.DisposeAsync();
}
}