using Microsoft.Data.Sqlite; using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SunlightAggregationTerminal.Class { public class ClsLock : IDisposable { private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); public IDisposable Read() { _lock.EnterReadLock(); return new DisposableAction(() => _lock.ExitReadLock()); } public void Dispose() => _lock?.Dispose(); private class DisposableAction : IDisposable { private readonly Action _action; public DisposableAction(Action action) => _action = action; public void Dispose() => _action?.Invoke(); } } public class SqliteHelper { #region 字段 private SqliteTransaction? dbTrans = null!; private static readonly Dictionary RWL = new Dictionary(); private readonly string mDataFile; private readonly string mPassWord = ""; private readonly string lockName = ""; private SqliteConnection mConn = null!; #endregion #region 构造函数 public SqliteHelper(string dataFile) { this.mDataFile = dataFile ?? throw new ArgumentNullException(nameof(dataFile)); this.mDataFile = dataFile; // 注意:原代码有两次赋值,保留逻辑 if (!RWL.ContainsKey(dataFile)) { lockName = dataFile; RWL.Add(dataFile, new ClsLock()); } } public SqliteHelper(string dataFile, string PassWord) { this.mDataFile = dataFile ?? throw new ArgumentNullException(nameof(dataFile)); this.mPassWord = PassWord ?? throw new ArgumentNullException(nameof(PassWord)); this.mDataFile = dataFile; if (!RWL.ContainsKey(dataFile)) { lockName = dataFile; RWL.Add(dataFile, new ClsLock()); } } #endregion #region 打开/关闭 数据库 // 注意:Microsoft.Data.Sqlite.Core 不支持连接字符串构建器,需手动拼接 // 加密支持通常需要 Microsoft.Data.Sqlite.Core + SQLitePCLRaw.bundle_green (且加密功能可能受限) public void Open() { // 构建连接字符串 var connectionString = $"Data Source={mDataFile}"; if (!string.IsNullOrWhiteSpace(mPassWord)) { connectionString += $";Password={mPassWord}"; // 注意:原生 Microsoft.Data.Sqlite 不支持加密 // 如果需要加密,通常需要使用 SQLitePCLRaw 的特定提供程序或第三方库 } mConn = new SqliteConnection(connectionString); // 如果文件不存在,Open() 会自动创建数据库文件 // 但表结构需要你自己执行 CREATE TABLE mConn.Open(); Console.WriteLine("The database was opened successfully"); } public void Close() { if (this.mConn != null) { try { this.mConn.Close(); // 注意:不要在这里移除 RWL,因为这是静态的,可能影响其他实例 // if (RWL.ContainsKey(LockName)) { RWL.Remove(LockName); } } catch { Console.WriteLine("Shutdown failed"); } } Console.WriteLine("The database was shut down successfully"); } #endregion #region 事务 public void BeginTrain() { EnsureConnection(); dbTrans = mConn.BeginTransaction(); } public void DBCommit() { try { dbTrans?.Commit(); dbTrans?.Dispose(); dbTrans = null; // 提交后置空 } catch (Exception) { dbTrans?.Rollback(); dbTrans?.Dispose(); dbTrans = null; } } #endregion #region 工具 // OpenConnection 逻辑已合并到 Open() 方法中,因为 SqliteConnection 构造函数只接受字符串 public SqliteConnection Connection { get { return mConn; } private set { mConn = value ?? throw new ArgumentNullException(); } } protected void EnsureConnection() { if (this.mConn == null) { throw new Exception("SQLiteManager.Connection=null"); } if (mConn.State != ConnectionState.Open) { mConn.Open(); } } public string GetDataFile() => this.mDataFile; public bool TableExists(string table) { if (string.IsNullOrEmpty(table)) return false; EnsureConnection(); // 注意:参数化查询在表名/列名上无效,这里使用字符串拼接(需确保table变量安全) // 或者使用 PRAGMA table_info(table_name) var sql = $"SELECT count(*) FROM sqlite_master WHERE type='table' AND name='{table}'"; using (var cmd = new SqliteCommand(sql, Connection)) { var result = cmd.ExecuteScalar(); return Convert.ToInt32(result) > 0; } } public bool Vacuum() { try { using (var Command = new SqliteCommand("VACUUM", Connection)) { Command.ExecuteNonQuery(); } return true; } catch (Exception) // SqliteException { return false; } } /// /// 将 IDataReader 转换为 DataSet /// /// 已经执行了查询的 DataReader /// 包含数据的 DataSet public DataSet ReaderToDataSet(IDataReader reader) { DataSet ds = new DataSet(); do { DataTable schemaTable = reader.GetSchemaTable()!; DataTable dataTable = new DataTable(); if (schemaTable != null) { // 创建列 foreach (DataRow schemaRow in schemaTable.Rows) { string colName = schemaRow["ColumnName"].ToString()!; Type dataType = (Type)schemaRow["DataType"]; // 防止列名重复 string uniqueColName = colName; int i = 1; while (dataTable.Columns.Contains(uniqueColName)) { uniqueColName = colName + i++; } DataColumn column = new DataColumn(uniqueColName, dataType); dataTable.Columns.Add(column); } // 读取行 while (reader.Read()) { object[] values = new object[dataTable.Columns.Count]; for (int i = 0; i < dataTable.Columns.Count; i++) { values[i] = reader.IsDBNull(i) ? DBNull.Value : reader.GetValue(i); } dataTable.Rows.Add(values); } } else { // 如果没有 Schema (例如 "SELECT 1" 这种标量查询) // 需要手动创建列 for (int i = 0; i < reader.FieldCount; i++) { dataTable.Columns.Add("Column" + i); } while (reader.Read()) { object[] values = new object[reader.FieldCount]; reader.GetValues(values); dataTable.Rows.Add(values); } } ds.Tables.Add(dataTable); } while (reader.NextResult()); // 处理多个结果集 return ds; } #endregion #region 执行SQL (Reader & Scalar) public SqliteDataReader? ExecuteReader(string sql, SqliteParameter[]? paramArr) { if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql)); EnsureConnection(); // 注意:using 语句会立即释放 Reader,导致无法读取 // 这里的 RWL 锁在 using 外部处理,或者调用方自己处理锁 // 这里简化逻辑,直接返回 Reader,调用方需自行管理连接状态 var cmd = new SqliteCommand(sql, Connection); if (paramArr != null) { cmd.Parameters.AddRange(paramArr); } // CommandBehavior.CloseConnection 确保 Reader 关闭时连接也关闭 // 但这里我们管理连接,所以不加 try { // 注意:不要在这里 Dispose Command,否则 Reader 会失效 // 返回 Reader,由调用方在读取完毕后关闭 return cmd.ExecuteReader(); } catch(Exception ex) { Console.WriteLine("[SQLITE_ExecuteReader:err]" + ex.ToString()); cmd?.Dispose(); return null; } } public DataSet? ExecuteDataSet(string sql, SqliteParameter[]? paramArr) { var dat = ExecuteReader(sql, paramArr); if (dat == null) { return null; } return ReaderToDataSet(dat); } // ExecuteScalar public object? ExecuteScalar(string sql, SqliteParameter[]? paramArr) { if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql)); EnsureConnection(); using (RWL[lockName].Read()) { using (var cmd = new SqliteCommand(sql, Connection)) { if (paramArr != null) { cmd.Parameters.AddRange(paramArr); } try { return cmd.ExecuteScalar(); } catch (Exception ex) { Console.WriteLine("[SQLITE_ExecuteScalar:err]" + ex.ToString()); return null; } } } } // QueryOne (返回字典或对象) public Dictionary? QueryOne(string table, string conditionCol, object conditionVal) { if (string.IsNullOrEmpty(table)) return null; EnsureConnection(); string sql = $"SELECT * FROM [{table}]"; // 使用 [] 防止关键字冲突 var parameters = new List(); if (!string.IsNullOrEmpty(conditionCol)) { sql += $" WHERE [{conditionCol}] = @{conditionCol}"; parameters.Add(new SqliteParameter($"@{conditionCol}", conditionVal)); } using (var cmd = new SqliteCommand(sql, Connection)) { cmd.Parameters.AddRange(parameters.ToArray()); using (var reader = cmd.ExecuteReader()) { if (reader.Read()) { var dict = new Dictionary(); for (int i = 0; i < reader.FieldCount; i++) { var value = reader.GetValue(i); if (value == DBNull.Value) value = null; dict[reader.GetName(i)] = value; } return dict; } } } return null; } #endregion #region 增 删 改 // 注意:参数前缀统一为 @ public int InsertData(string table, Dictionary entity) { if (string.IsNullOrEmpty(table)) throw new ArgumentNullException(nameof(table)); EnsureConnection(); string sql = BuildInsert(table, entity); var parameters = BuildParamArray(entity); return ExecuteNonQuery(sql, parameters); } public int Update(string table, Dictionary entity, string where, SqliteParameter[]? whereParams) { if (string.IsNullOrEmpty(table)) throw new ArgumentNullException(nameof(table)); EnsureConnection(); string sql = BuildUpdate(table, entity); var parameters = BuildParamArray(entity); if (!string.IsNullOrEmpty(where)) { sql += " WHERE " + where; if (whereParams != null) { var combined = new SqliteParameter[parameters.Length + whereParams.Length]; parameters.CopyTo(combined, 0); whereParams.CopyTo(combined, parameters.Length); parameters = combined; } } return ExecuteNonQuery(sql, parameters); } public int Delete(string table, string where, SqliteParameter[]? whereParams) { if (string.IsNullOrEmpty(table)) return 0; EnsureConnection(); string sql = $"DELETE FROM [{table}]"; if (!string.IsNullOrEmpty(where)) { sql += " WHERE " + where; } return ExecuteNonQuery(sql, whereParams); } public int ExecuteNonQuery(string sql, SqliteParameter[]? paramArr) { if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql)); EnsureConnection(); using (RWL[lockName].Read()) // 注意:写操作通常建议用 Write 锁 { using (var cmd = new SqliteCommand(sql, Connection)) { if (paramArr != null) { cmd.Parameters.AddRange(paramArr); } try { return cmd.ExecuteNonQuery(); } catch (Exception ex) { Console.WriteLine("[SQLITE_ExecuteScalar:err]" + ex.ToString()); return 0; } } } } // 构建 SQL 和 参数的方法保持不变,仅泛型类型变更 private SqliteParameter[] BuildParamArray(Dictionary entity) { var list = new List(); foreach (string key in entity.Keys) { // 注意:参数名必须包含 @ 前缀 list.Add(new SqliteParameter($"@{key}", entity[key])); } return list.ToArray(); } private string BuildInsert(string table, Dictionary entity) { var buf = new StringBuilder(); buf.Append("INSERT INTO ").Append(table).Append(" ("); var cols = new List(); var vals = new List(); foreach (string key in entity.Keys) { cols.Add(key); vals.Add($"@{key}"); } buf.Append(string.Join(",", cols)).Append(") VALUES ("); buf.Append(string.Join(",", vals)).Append(")"); return buf.ToString(); } private string BuildUpdate(string table, Dictionary entity) { var buf = new StringBuilder(); buf.Append("UPDATE ").Append(table).Append(" SET "); var sets = new List(); foreach (string key in entity.Keys) { sets.Add($"{key} = @{key}"); } buf.Append(string.Join(",", sets)); return buf.ToString(); } // 辅助方法:DataRow 转 Dictionary (由于没有 DataTable,通常直接从 Reader 转) // 这里保留原方法签名,但实际使用中可能需要调整 public Dictionary DataTableToDictionary(DataTable dataTable) { var result = new Dictionary(); if (dataTable.Rows.Count > 0) { var row = dataTable.Rows[0]; foreach (DataColumn column in dataTable.Columns) { var value = row[column]; if (value == DBNull.Value) value = null; result[column.ColumnName] = value; } } return result; } #endregion } }