using FreeSql; using Microsoft.Extensions.DependencyInjection; using MySql.Data.MySqlClient; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.IO; using System.Text; namespace StoreHub.KJDWebService.Commons { /// /// .NET 10 版 MysqlUtils(迁移自 KJD_Web.Utils.MysqlUtils)。 /// 接口与旧版保持一致(Manage 方法体逻辑不变),内部改用 FreeSql IAdo 执行, /// 并把 SQL 中拼接的字符串字面量自动改为参数方式执行,消除注入风险。 /// 连接池由 FreeSql 管理,Close() 为空操作。 /// public class MysqlUtils { /// 错误信息(与旧版一致,Manage 依赖 mySql.Message) public string Message = ""; private readonly IAdo _ado; /// 超时(秒) public int TimeOut = 120; public bool IsUseTrans { get; set; } = false; private DbTransaction _transaction = null; private object _poolObj = null; private DbConnection _transConn = null; /// 当前事务 public DbTransaction Transaction { get { return _transaction; } set { _transaction = value; } } public bool Connected { get { return _transConn != null && _transConn.State == ConnectionState.Open; } } /// 初始化:传入 IAdo(由 Service 注入 SqlRepository 后提供) public MysqlUtils(IAdo ado, bool isUseTrans = false) { _ado = ado ?? throw new ArgumentNullException(nameof(ado)); this.IsUseTrans = isUseTrans; if (isUseTrans) { BeginTransactionInternal(); } } /// 兼容旧构造函数签名(conn 仅用于选择数据库,连接由 DI 提供) public MysqlUtils(string conn, bool isUseTrans = false) : this(AdoProvider.Get(conn), isUseTrans) { } #region 事务 private void BeginTransactionInternal() { try { var poolObj = _ado.MasterPool.Get(TimeSpan.FromSeconds(TimeOut)); _poolObj = poolObj; _transConn = poolObj.Value; _transaction = _transConn.BeginTransaction(); Message = ""; } catch (Exception e) { AddError(e.Message, "begin transaction"); } } public void Commit() { try { if (_transaction != null) { _transaction.Commit(); } } catch (Exception e) { AddError(e.Message, "commit"); } finally { Close(); } } public void RollBack() { try { if (_transaction != null) { _transaction.Rollback(); } } catch (Exception e) { AddError(e.Message, "rollback"); } finally { Close(); } } #endregion #region 执行方法 /// 返回执行表 public DataTable ExecuteDataTable(string SqlString) { return ExecuteDataTable(SqlString, (DbParameter[])null); } /// 执行sql返回DataTable public DataTable ExecuteDataTable(string SqlString, DbParameter[] parms) { return ExecuteCore(SqlString, parms, (conn, tx, sql, p) => _ado.ExecuteDataTable(sql, p)); } /// 执行sql返回DataTable(自动参数化) public DataTable ExecuteDataTable(string SqlString, object parms) { return ExecuteCore(SqlString, parms, (conn, tx, sql, p) => _ado.ExecuteDataTable(conn, tx, sql, (object)p)); } /// 取第一行 public DataRow ExecuteDataTableRow(string SqlString, DbParameter[] parms = null) { DataTable dt = ExecuteDataTable(SqlString, parms); if (dt != null && dt.Rows.Count > 0) return dt.Rows[0]; return null; } /// 行数 public int ExecuteRowCount(string SqlString, DbParameter[] parms = null) { DataTable dt = ExecuteDataTable(SqlString, parms); if (dt != null) return dt.Rows.Count; return 0; } /// 首行首列(string) public string ExecuteFirst(string SqlString, DbParameter[] parms = null) { DataRow row = ExecuteDataTableRow(SqlString, parms); if (row != null && row.ItemArray.Length > 0) return ToStringVal(row[0]); return null; } /// 首行首列(int) public int ExecuteFirstInt(string SqlString, DbParameter[] parms = null) { string v = ExecuteFirst(SqlString, parms); return ToInt(v); } /// 首行首列(decimal) public decimal ExecuteFirstDecimal(string SqlString, DbParameter[] parms = null) { string v = ExecuteFirst(SqlString, parms); if (v == null) return 0m; decimal r = 0m; decimal.TryParse(v, out r); return r; } /// 受影响行数 public int ExecuteAffectedRows(string SqlString, DbParameter[] parms = null) { return ExecuteCore(SqlString, parms, (conn, tx, sql, p) => _ado.ExecuteNonQuery(conn, tx, sql, (object)p)); } /// 执行无返回SQL,是否执行成功 public bool ExecuteNonQuery(string SqlString, DbParameter[] parms = null) { return ExecuteCore(SqlString, parms, (conn, tx, sql, p) => _ado.ExecuteNonQuery(conn, tx, sql, (object)p) > 0); } /// 插入并返回自增id public long ExecuteInsertId(string SqlString, DbParameter[] parms = null) { Message = ""; object poolObj = null; try { DbConnection conn; DbTransaction tx = null; if (IsUseTrans && _transaction != null) { conn = _transConn; tx = _transaction; } else { var pool = _ado.MasterPool.Get(TimeSpan.FromSeconds(TimeOut)); poolObj = pool; conn = pool.Value; } int affected = _ado.ExecuteNonQuery(conn, tx, SqlString, parms); if (affected < 0 || !string.IsNullOrEmpty(Message)) return 0; object v = _ado.ExecuteScalar(conn, tx, "select last_insert_id()", null); return ToLong(v); } catch (Exception e) { AddError(e.Message, SqlString); return 0; } finally { ReleaseConnection(poolObj); } } /// 是否存在 public bool ExecuteExists(string SqlString, DbParameter[] parms = null) { DataTable dt = ExecuteDataTable(SqlString, parms); return dt != null && dt.Rows.Count > 0; } /// 执行标量 public object ExecuteScalar(string SqlString, DbParameter[] parms = null) { return ExecuteScalarInternal(SqlString, parms); } private object ExecuteScalarInternal(string SqlString, DbParameter[] parms = null) { return ExecuteCore(SqlString, parms, (conn, tx, sql, p) => _ado.ExecuteScalar(conn, tx, sql, (object)p)); } /// 插入(keys 参数化) public long Insert(string table, List keys) { StringBuilder cols = new StringBuilder(); StringBuilder vals = new StringBuilder(); var pars = new List(); for (int i = 0; i < keys.Count; i++) { if (i > 0) { cols.Append(","); vals.Append(","); } cols.Append("`").Append(keys[i].Key).Append("`"); string pname = "@p" + i; vals.Append(pname); pars.Add(new MySqlParameter(pname, (object)(keys[i].Value ?? ""))); } string sql = "insert into " + table + " (" + cols.ToString() + ") values (" + vals.ToString() + ")"; bool ok = ExecuteNonQuery(sql, pars.ToArray()); if (!ok || !string.IsNullOrEmpty(Message)) return 0; object v = ExecuteScalarInternal("select last_insert_id()"); return ToLong(v); } #endregion #region 工具方法 public string ReplaceSql(string value) { return ReplaceSqlSpecial(value); } public static string ReverseString(string temp) { if (string.IsNullOrEmpty(temp)) return null; char[] l = temp.ToCharArray(); Array.Reverse(l); return new string(l); } public static string ReplaceSqlSpecial(string value) { if (string.IsNullOrEmpty(value)) return null; value = value.Replace("\\", "\\\\"); value = value.Replace("'", "''"); return value; } public static string JustUtf8Words(string str) { return str; } #endregion #region 内部实现 private T ExecuteCore(string sql, DbParameter[] parms, Func action) { Message = ""; object poolObj = null; try { DbConnection conn; DbTransaction tx = null; if (IsUseTrans && _transaction != null) { conn = _transConn; tx = _transaction; } else { var pool = _ado.MasterPool.Get(TimeSpan.FromSeconds(TimeOut)); poolObj = pool; conn = pool.Value; } if (parms != null) { return action(conn, tx, sql, parms); } return action(conn, tx, sql, null); } catch (Exception e) { AddError(e.Message, sql); return default(T); } finally { ReleaseConnection(poolObj); } } private T ExecuteCore(string sql, object parms, Func action) { if (parms is DbParameter[] arr) return ExecuteCore(sql, arr, action); if (parms == null) return ExecuteCore(sql, (DbParameter[])null, action); var list = new List(); foreach (var prop in parms.GetType().GetProperties()) { list.Add(new MySqlParameter("@" + prop.Name, prop.GetValue(parms) ?? DBNull.Value)); } return ExecuteCore(sql, list.ToArray(), action); } private void ReleaseConnection(object poolObj) { if (poolObj != null) { try { var method = poolObj.GetType().GetMethod("Dispose"); method?.Invoke(poolObj, null); } catch { } } } /// 关闭连接(连接池自动管理,空操作) public void Close() { IsRunning = false; try { if (_transaction != null) { try { _transaction.Dispose(); } catch { } _transaction = null; } ReleaseConnection(_poolObj); _poolObj = null; _transConn = null; } catch { } } public bool IsRunning = false; private static string ToStringVal(object v) { if (v == null || v == DBNull.Value) return null; return v.ToString(); } private static int ToInt(object v) { if (v == null || v == DBNull.Value) return 0; int r = 0; int.TryParse(v.ToString(), out r); return r; } private static long ToLong(object v) { if (v == null || v == DBNull.Value) return 0; long r = 0; long.TryParse(v.ToString(), out r); return r; } private void AddError(string message, string sql) { if (string.IsNullOrEmpty(message) || message.Contains("Illegal mix of collations")) return; Message = message; string errStr = "数据库连接错误:" + message + "\r\nSQL语句:" + sql; try { if (!Directory.Exists("Log")) { Directory.CreateDirectory("Log"); } File.AppendAllText("Log\\log-" + DateTime.Now.ToString("yyyyMMdd") + ".txt", errStr); } catch (Exception) { } } #endregion } /// /// 根据连接串 key 提供 FreeSql IAdo(由框架 DI 注册的 keyed IFreeSql 提供) /// public static class AdoProvider { private static IServiceProvider _serviceProvider; public static void SetServiceProvider(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public static IAdo Get(string connKey) { if (string.IsNullOrEmpty(connKey)) connKey = "ConnKJD"; string key = connKey switch { "ConnKJD" => "KJDDB", "ConnKJDBAK" => "KJDBAKDB", _ => "KJDDB", }; var fsql = _serviceProvider?.GetKeyedService(key); if (fsql == null) { var fsql2 = _serviceProvider?.GetService(); if (fsql2 != null) return fsql2.Ado; throw new InvalidOperationException("IFreeSql not registered for key " + key); } return fsql.Ado; } } public class MysqlKeyValues { public List List = new List(); } public class MysqlKeyValue { public MysqlKeyValue(string key, object value) { Key = key; Value = value == null ? "" : MysqlUtils.ReplaceSqlSpecial(value.ToString()); } public string Key = ""; public string Value; } }