Files
BeWoPlaner/Tools/BeWoDatabaseUpdater/BeWoDatabaseUpdater/Program.cs

283 lines
9.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BeWoDatabaseUpdater.Extensions;
using BeWoDatabaseUpdater.Utils;
namespace BeWoDatabaseUpdater
{
// ToDo: bestimmte Fehlermeldungen ignorieren oder prüfen, ob es die Spalten bereits gibt etc.
internal class Program
{
private const int CutoffYear = 2001;
private static string _ConnectionString;
private static string _ModelPath;
private static DateTime? _LastAccessTime;
private const string UpdaterSettingsFilePath = "bewo-database-updater-settings.txt";
public const string FailedSqlCommandsFilePath = "bewo-database-updater-failed-sql-commands.txt";
private static readonly List<FilePath2MySqlCommands> _FilePaths2Sql = [];
private static List<string> _FilePaths = [];
private static List<string> _BeWoSchemaNames = [];
private static readonly List<DateTime> _LastAccessTimes = [];
private static readonly Stopwatch _Stopwatch = new();
private static int _OpenDbConnections = 0;
private static async Task Main(string[] args)
{
var settingsFilePath = Path.Combine(AppContext.BaseDirectory, UpdaterSettingsFilePath);
// Falls die Settings-Datei noch nicht existiert:
if(false == File.Exists(settingsFilePath))
{
using var fileStream = File.Create(settingsFilePath);
}
// Falls es noch keine Datei mit Sql-Fehler gibt, wird eine erstellt, in die die Fehler geschrieben werden:
if(false == File.Exists(FailedSqlCommandsFilePath))
{
using var fileStream = File.Create(FailedSqlCommandsFilePath);
}
// Die Settingsdatei mit Pfad zum "Model"-Verzeichnis, MySql-Connection-String und dem letzten Ausführdatum auslesen
ReadSettingsFile(settingsFilePath);
if(string.IsNullOrWhiteSpace(_ConnectionString))
{
Console.WriteLine();
Console.Write("Connection String (Server, User Id, Passwort): ");
_ConnectionString = Console.ReadLine();
}
// Den Pfad zum Model-Verzeichnis im BeWoPlaner-Projekt ermitteln
DetermineModelPath();
// Die Dateien mit den Sql-Skripten auflisten
_Stopwatch.Start();
GetTextAndSqlFiles();
Console.WriteLine($"{DateTime.Now:dd.MM.yyyy HH:mm:ss.fff}: Führe Updates durch ...{Environment.NewLine}");
_BeWoSchemaNames = ["3522103738"]; //await MySqlBeWoHelper.LoadBeWoDatabaseNames(_ConnectionString) ?? [];
LoadSqlScriptsFromFilesToMemory(() =>
{
var commandCount = 0;
var fileCount = 0;
_FilePaths2Sql.DoForEach(path2Commands =>
{
if(path2Commands is null)
{
return;
}
commandCount += path2Commands.Commands.Count;
fileCount++;
});
var message = $"{DateTime.Now:dd.MM.yyyy HH:mm:ss.fff}: {commandCount} MySQL-Abfrag{(commandCount != 1 ? "en" : string.Empty)} in {fileCount} Datei{(fileCount != 1 ? "en" : string.Empty)} und {_BeWoSchemaNames.Count} BeWoPlaner-Datenbanken ({_BeWoSchemaNames.Count * commandCount} Transaktionen gesamt):{Environment.NewLine}";
var sb = new StringBuilder();
for(var i = 0; i < message.Length; i++)
{
sb.Append('-');
}
Console.WriteLine($"{message}{sb}");
if(commandCount == 0)
{
OnAllThreadsCompleted();
return;
}
// Ein Thread pro Sql-Kommando
// ToDo: Threads beachten!
var threads = new List<Thread>();
foreach(var schemaName in _BeWoSchemaNames)
{
foreach(var path2Commands in _FilePaths2Sql)
{
_LastAccessTimes.Add(new FileInfo(path2Commands.FilePath).LastAccessTime);
foreach(var command in path2Commands.Commands)
{
var thread = new Thread(() =>
{
var sql = $"USE `{schemaName}`; {command}";
MySqlBeWoHelper.ExecuteUpdateOrCreateAsync(sql, path2Commands.FilePath, _ConnectionString);
});
thread.Start();
threads.Add(thread);
}
}
}
threads.DoForEach(thread =>
{
thread.Join();
OnAllThreadsCompleted();
});
});
#if DEBUG
Console.Read();
#endif
if(MySqlBeWoHelper.DistinctErrors.Any())
{
Console.Read();
}
}
private static void OnAllThreadsCompleted()
{
_LastAccessTime = _LastAccessTimes.Any() ? _LastAccessTimes.Max() : DateTime.Now;
_Stopwatch.Stop();
File.WriteAllText(UpdaterSettingsFilePath,
$"ConnectionString={_ConnectionString}{Environment.NewLine}" +
$"LastScriptAccessTime={_LastAccessTime}{Environment.NewLine}" +
$"ModelPath={_ModelPath}"
);
var timeSpan = TimeSpan.FromTicks(_Stopwatch.ElapsedTicks);
Console.WriteLine($"{DateTime.Now:dd.MM.yyyy HH:mm:ss.fff}: Dauer: {timeSpan.ToShortString()}{Environment.NewLine}");
Console.WriteLine($"{MySqlBeWoHelper.DistinctErrors.Count} Fehler{(MySqlBeWoHelper.DistinctErrors.Any() ? ":" : "")}");
//MySqlBeWoHelper.WriteSqlErrorsToFile();
MySqlBeWoHelper.DistinctErrors.DoForEach(number2Message => Console.WriteLine($"{number2Message.Key}: {number2Message.Value}"));
Console.WriteLine("Alle Updates durchgeführt!");
}
private static void GetTextAndSqlFiles()
{
_FilePaths = Directory.GetFiles(_ModelPath).Where(file =>
{
var extension = Path.GetExtension(file);
var fileInfo = new FileInfo(file);
var name = fileInfo.Name.ToLower();
if(false == name.StartsWith("changes_"))
{
return false;
}
int.TryParse(name.Split('_')[1], out var year);
if(year == 0)
{
year = DateTime.Today.Year;
}
return (extension.EndsWith("txt") || extension.EndsWith("sql")) && year >= CutoffYear && fileInfo.LastAccessTime > _LastAccessTime;
}).ToList();
}
private static void ReadSettingsFile(string settingsFilePath)
{
var allLines = File.ReadAllLines(settingsFilePath);
foreach(var line in allLines)
{
var settingsName2Value = line.Split('=');
if(settingsName2Value.Length < 2)
{
continue;
}
var settingsName = settingsName2Value[0];
var indexOfFirstEquals = line.IndexOf('=');
var settingsValue = line.Substring(indexOfFirstEquals + 1);
switch(settingsName)
{
case "ConnectionString":
_ConnectionString = settingsValue;
break;
case "LastScriptAccessTime":
if(DateTime.TryParse(settingsValue, out var parsedNewestScriptCreationTime))
{
_LastAccessTime = parsedNewestScriptCreationTime;
}
break;
case "ModelPath":
_ModelPath = settingsValue;
break;
}
}
}
private static void DetermineModelPath()
{
while(string.IsNullOrWhiteSpace(_ModelPath) || false == Directory.Exists(_ModelPath))
{
Console.WriteLine();
if(false == string.IsNullOrWhiteSpace(_ModelPath) && false == Directory.Exists(_ModelPath))
{
Console.WriteLine($"Der Pfad '{_ModelPath}' ist ungültig!");
}
Console.Write("Pfad zum Model-Verzeichnis: ");
_ModelPath = Console.ReadLine();
}
}
private static void LoadSqlScriptsFromFilesToMemory(Action callback)
{
var threads = new List<Thread>();
foreach(var filePath in _FilePaths)
{
var thread = new Thread(() =>
{
var fileInfo = new FileInfo(filePath);
if(_LastAccessTime.HasValue && _LastAccessTime >= fileInfo.CreationTime)
{
return;
}
_FilePaths2Sql.Add(new FilePath2MySqlCommands(filePath, File.ReadAllText(filePath).Split(';').ToList()));
});
thread.Start();
threads.Add(thread);
}
Task.Run(() =>
{
threads.DoForEach(thread => thread.Join());
callback?.Invoke();
});
}
}
}