using DocumentFormat.OpenXml.Drawing; using DocumentFormat.OpenXml.Office.Word; using DocumentFormat.OpenXml.Spreadsheet; using DocumentFormat.OpenXml.Wordprocessing; using KssSmaPlaLib.Commons; using Serilog; using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using static KssSmaPlaLib.IO.Database.DbUxer; using Path = System.IO.Path; namespace KssSmaPlaLib.IO.Database { /// /// DBアクセスアシスタント /// public class DbUxer : IDisposable { public enum DbProvider { PostgreSQL, SQLServer, MySQL, Oracle, SQLite, } /// /// プロバイダ名ディクショナリ /// private static readonly Dictionary _providerNameDic = new Dictionary() { { DbProvider.PostgreSQL, "Npgsql" }, { DbProvider.SQLServer, "System.Data.SqlClient" }, { DbProvider.MySQL, "MySql.Data.MySqlClient" }, { DbProvider.Oracle, "Oracle.ManagedDataAccess.Client" }, { DbProvider.SQLite, "System.Data.SQLite" }, }; /// /// プロバイダ名(文字列)からenum値に変更 /// /// プロバイダ名(文字列) /// enum値(該当なし時はnull) public static DbProvider? GetDbProviderEnumFromString(string dbProviderName = "PostgreSQL") { switch(dbProviderName.ToLower().Replace(" ","")) { case "postgresql": case "ポスグレ": return DbProvider.PostgreSQL; case "sqlserver": return DbProvider.SQLServer; case "mysql": return DbProvider.MySQL; case "oracle": case "oracledatabase": return DbProvider.Oracle; case "sqlite": case "sqlite3": return DbProvider.SQLite; default: return null; } } /// /// コミット済みフラグ /// private bool _isCommitted = false; /// /// SQLディクショナリ /// private static readonly Dictionary _sqlDic = new Dictionary(); /// /// 指定フォルダからSQLファイルを読み込む(ディクショナリ保存) /// /// フォルダパス /// 成否 public static bool TryLoadSqlFromText(string folderPath, out string resultMessage) { resultMessage = string.Empty; var fileCount = 0; List files = new List(); try { if (string.IsNullOrEmpty(folderPath)) { resultMessage = "フォルダパスが指定されていません。"; return false; } if (!Directory.Exists(folderPath)) { resultMessage = $"フォルダが存在しません。[フォルダパス:{folderPath}]"; return false; } _sqlDic.Clear(); foreach (var file in Directory.GetFiles(folderPath, "*.sql.txt")) { _sqlDic.Add(Path.GetFileNameWithoutExtension(file).ToLower().Replace(".sql", ""), System.IO.File.ReadAllText(file)); files.Add(Path.GetFileNameWithoutExtension(file).Replace(".sql", "")); fileCount++; } resultMessage = $"SQLファイルをロードしました。[ファイル数:{fileCount},ファイル:{string.Join(",", files)}]"; return true; } catch (Exception ex) { resultMessage = $"SQLファイル読み込み時にエラーが発生しました。[エラー内容:{ex.Message}]"; return false; } } // public static bool TestConnectByDic(Dictionary iniDic, out string resultMessage) // { // const string KEY_DB_PROVIDER_NAME = "DbProviderName"; // const string KEY_DB_CONNECTION_STRING = "DbConnectionString"; // var dbProviderName = DictionaryUxer.GetValueOrDefault(iniDic, KEY_DB_PROVIDER_NAME, string.Empty); // var connectionString = DictionaryUxer.GetValueOrDefault(iniDic, KEY_DB_CONNECTION_STRING, string.Empty); // return TestConnect(dbProviderName, connectionString, out resultMessage); // } /// /// テスト接続 /// /// 成否 // public static bool TestConnect(string dbPrividerName, string dbConnectionString, out string resultMessage) // { // // パラメータチェック&DBプロバイダ取得 // if (!TryConnectParameter(dbPrividerName, dbConnectionString, out DbProvider? _, out resultMessage)) // { // return false; // } // if (!TryCreate(dbPrividerName, dbConnectionString, out var dbUxer, out resultMessage, false)) // { // return false; // } // dbUxer.Dispose(); // return true; // } /// /// DB接続 /// private DbConnection? _con = null; /// /// トランザクション /// private DbTransaction? _tran = null; // public static bool TryCreateByDic(Dictionary iniDic, out DbUxer dbUxer, out string resultMessage, bool useTransaction = false) // { // const string KEY_DB_PROVIDER_NAME = "DbProviderName"; // const string KEY_DB_CONNECTION_STRING = "DbConnectionString"; // var dbProviderName = DictionaryUxer.GetValueOrDefault(iniDic, KEY_DB_PROVIDER_NAME, string.Empty); // var connectionString = DictionaryUxer.GetValueOrDefault(iniDic, KEY_DB_CONNECTION_STRING, string.Empty); // return TryCreate(dbProviderName, connectionString, out dbUxer, out resultMessage, useTransaction); // } /// /// 生成挑戦 /// /// /// /// /// /// /// // public static bool TryCreate(string dbPrividerName, string dbConnectionString, out DbUxer dbUxer, out string resultMessage, bool useTransaction = false) // { // dbUxer = null; // // パラメータチェック&DBプロバイダ取得 // if (!TryConnectParameter(dbPrividerName, dbConnectionString, out DbProvider? dbp, out resultMessage)) // { // return false; // } // DbProvider dbpp = (DbProvider)dbp; // try // { // // 生成 // dbUxer = new DbUxer(dbpp, dbConnectionString, useTransaction); // return true; // } // catch (Exception ex) // { // // 接続NG回答 // resultMessage = $"DB接続に失敗しました。[エラー内容:{ex.Message}]"; // return false; // } // } private static bool TryConnectParameter(string dbPrividerName, string dbConnectionString, out DbProvider? dbp, out string resultMessage) { dbp = DbProvider.PostgreSQL; resultMessage = string.Empty; if (string.IsNullOrEmpty(dbPrividerName)) { // DBプロバイダ指定なしの場合、デフォルトはポスグレとする // Infoメッセージ resultMessage = "DBプロバイダ名が未指定のため、デフォルトのPostgreSQLを使用します。"; } if (string.IsNullOrEmpty(dbConnectionString)) { resultMessage = "接続文字列が設定されていません。"; return false; } if (!string.IsNullOrEmpty(dbPrividerName)) { dbp = GetDbProviderEnumFromString(dbPrividerName); } if (dbp == null) { resultMessage = $"DBプロバイダ名が不正です。[DBプロバイダ名:{dbPrividerName}]"; return false; } return true; } /// /// コンストラクタ(DB接続) /// /// DBプロバイダ(enum値) /// 接続文字列 /// トランザクション有無 private DbUxer(DbProvider dbProvider, string connectionString, bool useTransaction = false) { // try // { // // DBプロバイダファクトリー作成 // DbProviderFactory factory; // if (dbProvider == DbProvider.PostgreSQL) // { // // ポスグレの場合、Npgsqlのインスタンをを直接取得 // factory = Npgsql.NpgsqlFactory.Instance; // } // else // { // factory = DbProviderFactories.GetFactory(_providerNameDic[dbProvider]); // } // // DB接続作成 // _con = factory.CreateConnection(); // // 接続文字列設定 // _con.ConnectionString = connectionString; // // 接続 // _con.Open(); // // トランザクション // if (useTransaction) _tran = _con.BeginTransaction(); // } // catch (Exception ex) // { // LoggerUxer.Error($"DB接続に失敗しました。[プロバイダ:{dbProvider},接続文字列:{connectionString},エラー内容:{ex.Message}]"); // throw ex; // } } // public bool ExecuteNonQuery(string sqlOrName, Dictionary parameters, out int resultCount, out string resultMessage) // { // resultCount = 0; // resultMessage = string.Empty; // if (_con == null || _con.State != ConnectionState.Open) // { // resultMessage = "DBと接続されていません。"; // return false; // } // try // { // using (var cmd = _con.CreateCommand()) // { // // コパンドプロパティを注入 // if (!TryInjectSoulToCmd(cmd, sqlOrName, parameters, out var messageForMe)) // { // resultMessage = messageForMe; // return false; // } // resultCount = cmd.ExecuteNonQuery(); // LoggerUxer.Info($"[Rec Count:{resultCount}]"); // return true; // } // } // catch (Exception ex) // { // resultMessage = $"SQL実行でエラーが発生しました。[SQL名:{sqlOrName},エラー内容:{ex.Message}]"; // return false; // } // } // public bool ExecuteQuery(string sqlOrName, Dictionary parameters, out List> recordSet, out string resultMessage) // { // recordSet = new List>(); // resultMessage = string.Empty; // if (_con == null || _con.State != ConnectionState.Open) // { // resultMessage = "DBと接続されていません。"; // return false; // } // try // { // using (var cmd = _con.CreateCommand()) // { // // コパンドプロパティを注入 // if (!TryInjectSoulToCmd(cmd, sqlOrName, parameters, out var messageForMe)) // { // resultMessage = messageForMe; // return false; // } // using (var reader = cmd.ExecuteReader()) // { // while (reader.Read()) // { // var row = new Dictionary(); // for (int i = 0; i < reader.FieldCount; i++) // { // row[reader.GetName(i)] = reader.GetValue(i); // } // recordSet.Add(row); // } // LoggerUxer.Info($"[Rec Count:{recordSet.Count}]"); // } // } // return true; // } // catch (Exception ex) // { // resultMessage = $"DBからデータ取得時にエラーが発生しました。[SQL名:{sqlOrName},エラー内容:{ex.Message}]"; // return false; // } // } // private bool TryInjectSoulToCmd(DbCommand cmd, string sqlOrName, Dictionary parameters, out string resultMessage) // { // resultMessage = string.Empty; // // SQLの取得 // sqlOrName = sqlOrName.ToLower(); // var sql = _sqlDic.ContainsKey(sqlOrName) ? _sqlDic[sqlOrName] : sqlOrName; // //LoggerUxer.Info($"[SQL:{sql}]"); // // SQL中のコマンドパラメータ名取得 // var paraList = ExtractSqlParams(sql); // //oggerUxer.Info($"[SQL Para names:{string.Join(",", paraList)}]"); // // コマンドパラメータ値の型変換 // if (!TryChangeParamTypeFromSqlParams(ref parameters, ref paraList, out var messageForMe)) // { // resultMessage = $"コマンドパラメータ値の型変換に失敗しました。[エラー内容:{messageForMe}]"; // return false; // } // // SQL文中のコマンドパラメータ型定義をカット // cmd.CommandText = sql = CutParamTypeOfSQL(sql); // //LoggerUxer.Info($"[SQL(Param Type Cut):{sql}]"); // // 再度、SQL中のコマンドパラメータ名取得 // paraList = ExtractSqlParams(sql); // //LoggerUxer.Info($"[SQL Para names(Type cut):{string.Join(",", paraList)}]"); // // SQLに合わせたコマンドパラメータ登録 // foreach (var p in parameters) // { // if (!paraList.Contains(p.Key)) continue; // var param = cmd.CreateParameter(); // param.ParameterName = p.Key; // param.Value = p.Value; // cmd.Parameters.Add(param); // } // //LoggerUxer.Info($"[SQL Params:{GetCommandParametersCsv(cmd)}]"); // if (cmd.Parameters.Count != paraList.Count) // { // resultMessage = $"必要なパラメータが不足しています。[SQL名:{sqlOrName},SQL項目:{string.Join(",", paraList)}, CSV項目:{string.Join(",", parameters.Keys)}]"; // return false; // } // return true; // } public void Commit() { _tran?.Commit(); _isCommitted = true; } /// /// 終了処理 /// /// public void Dispose() { if (_tran != null && !_isCommitted) { Log.Warning("トランザクション未コミット → 自動ロールバックされました"); _tran.Rollback(); // 明示的にロールバック } _con?.Dispose(); _con = null; _tran = null; } public static List ExtractSqlParams(string sql) { //var matches = System.Text.RegularExpressions.Regex.Matches(sql, @":([a-zA-Z_][a-zA-Z0-9_]*)"); //return matches.Cast() // .Select(m => m.Groups[1].Value) // .Distinct() // .ToList(); //var result = new List(); //var lines = sql.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None); //foreach (var line in lines) //{ // // コメント部分を除去 // var codePart = line.Contains("--") ? line.Substring(0, line.IndexOf("--")) : line; // // パラメータ抽出 // var matches = System.Text.RegularExpressions.Regex.Matches( // codePart, // @":(\[[a-zA-Z]\][a-zA-Z_][a-zA-Z0-9_]*)|:([a-zA-Z_][a-zA-Z0-9_]*)" // ); // foreach (System.Text.RegularExpressions.Match match in matches) // { // var paramName = match.Groups[1].Success ? match.Groups[1].Value : match.Groups[2].Value; // if (!result.Contains(paramName)) // result.Add(paramName); // } //} //return result; var result = new List(); var lines = sql.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None); foreach (var line in lines) { // コメント部分を除去 var codePart = line.Contains("--") ? line.Substring(0, line.IndexOf("--")) : line; // パラメータ抽出(::で始まるものは除外) var matches = System.Text.RegularExpressions.Regex.Matches( codePart, @"(?(); // foreach (DbParameter param in command.Parameters) // { // string name = param.ParameterName?.TrimStart('@'); // @を除去(任意) // object value = param.Value; // string formattedValue; // if (value == null || value == DBNull.Value) // { // formattedValue = "NULL"; // } // else if (value is string || value is char) // { // formattedValue = $"'{value}'"; // シングルクォートで括る // } // else if (value is DateTime dt) // { // formattedValue = $"'{dt:yyyy-MM-dd HH:mm:ss}'"; // 日付も文字列扱い // } // else // { // formattedValue = value.ToString(); // } // list.Add($"{name}={formattedValue}"); // } // return string.Join(",", list); // } // private static bool TryChangeParamTypeFromSqlParams(ref Dictionary parameters, ref List paraNameList, out string resultMessage) // { // resultMessage = string.Empty; // foreach (var key in parameters.Keys.ToList()) // { // var name = paraNameList // .FirstOrDefault(m => m == key || m.Substring(0, 1) == "[" && m.Substring(3) == key); // if (name != null && name.Substring(0, 1) == "[") // { // switch (name.Substring(0, 3).ToUpper()) // { // case "[I]": // if (string.IsNullOrEmpty(parameters[key].ToString())) // { // parameters[key] = DBNull.Value; // } // else if (int.TryParse(parameters[key].ToString(), out int intValue)) // { // parameters[key] = intValue; // } // else // { // resultMessage = $"パラメータ値[I]型変換に失敗しました。[キー:{key}値:{parameters[key]}]"; // return false; // } // break; // //case "[IN": // // if (string.IsNullOrEmpty(parameters[key].ToString())) // // { // // parameters[key] = DBNull.Value; // // } // // else if (int.TryParse(parameters[key].ToString(), out intValue)) // // { // // parameters[key] = intValue; // // } // // else // // { // // resultMessage = $"パラメータ値[IN]型変換に失敗しました。[キー:{key}値:{parameters[key]}]"; // // return false; // // } // // break; // case "[F]": // if (double.TryParse(parameters[key].ToString(), out double doubleValue)) // { // parameters[key] = doubleValue; // } // else // { // resultMessage = $"パラメータ値[F]型変換に失敗しました。[キー:{key}値:{parameters[key]}]"; // return false; // } // break; // case "[D]": // if (DateTime.TryParse(parameters[key].ToString(), out DateTime dtValue)) // { // parameters[key] = dtValue; // } // else // { // resultMessage = $"パラメータ値[D]型変換に失敗しました。[キー:{key}値:{parameters[key]}]"; // return false; // } // break; // case "[S]": // parameters[key] = parameters[key].ToString(); // break; // } // } // } // return true; // } // SQL文中のコマンドパラメータ型定義をカット private static string CutParamTypeOfSQL(string sql) { string[] cutItems = new string[] { "[I]", "[F]", "[D]", "[S]" }; foreach (var cutItem in cutItems) { sql = sql.Replace(cutItem.ToUpper(), ""); sql = sql.Replace(cutItem.ToLower(), ""); } return sql; } } }