using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Text; namespace Apewer.Source { /// abstract class DbClient { /// public virtual Logger Logger { get; set; } #region Connection DbConnection _conn = null; string _str = null; /// public Timeout Timeout { get; set; } /// public DbConnection Connection { get => _conn; } /// public bool Online { get => _conn == null ? false : (_conn.State == ConnectionState.Open); } /// 连接字符串。 public string ConnectionString { get => _str; } /// public virtual string Connect() { if (_conn == null) { _str = GetConnectionString(); _conn = NewConnection(); _conn.ConnectionString = _str; } else { if (_conn.State == ConnectionState.Open) return null; } try { _conn.Open(); switch (_conn.State) { case ConnectionState.Open: return null; default: return $"连接失败,当前处于 {_conn.State} 状态。"; } } catch (Exception ex) { Logger.Error(this, "Connect", ex, _conn.ConnectionString); Close(); return ex.Message; } } /// public void Close() { if (_conn != null) { if (_transaction != null) { if (_autocommit) Commit(); else Rollback(); } _conn.Close(); _conn.Dispose(); _conn = null; } } /// public void Dispose() { Close(); } #endregion #region Transaction private DbTransaction _transaction = null; private bool _autocommit = false; /// 启动事务。 public string Begin(bool commit = true) => Begin(commit, null); /// 启动事务。 public string Begin(bool commit, Class isolation) { if (Connect() != null) return "未连接。"; if (_transaction != null) return "存在已启动的事务,无法再次启动。"; try { _transaction = isolation ? _conn.BeginTransaction(isolation.Value) : _conn.BeginTransaction(); _autocommit = commit; return null; } catch (Exception ex) { Logger.Error(this, "Commit", ex.Message()); return ex.Message(); } } /// 提交事务。 public string Commit() { if (_transaction == null) return "事务不存在。"; try { _transaction.Commit(); RuntimeUtility.Dispose(_transaction); _transaction = null; return null; } catch (Exception ex) { RuntimeUtility.Dispose(_transaction); _transaction = null; Logger.Error(this, "Commit", ex.Message()); return ex.Message(); } } /// 从挂起状态回滚事务。 public string Rollback() { if (_transaction == null) return "事务不存在。"; try { _transaction.Rollback(); RuntimeUtility.Dispose(_transaction); _transaction = null; return null; } catch (Exception ex) { RuntimeUtility.Dispose(_transaction); _transaction = null; Logger.Error(this, "Rollback", ex.Message()); return ex.Message(); } } #endregion #region ADO /// 查询。 public IQuery Query(string sql) => Query(sql, null); /// 查询。 public IQuery Query(string sql, IEnumerable parameters) { if (TextUtility.IsBlank(sql)) return new Query(false, "语句无效。"); var connected = Connect(); if (connected.NotEmpty()) return new Query(false, connected); try { using (var command = NewCommand()) { command.Connection = _conn; if (Timeout != null) command.CommandTimeout = Timeout.Query; command.CommandText = sql; if (parameters != null) { foreach (var parameter in parameters) { if (parameter != null) command.Parameters.Add(parameter); } } using (var ds = new DataSet()) { using (var da = NewDataAdapter(sql)) { var name = "table_" + Guid.NewGuid().ToString("n"); da.Fill(ds, name); var table = ds.Tables[name]; return new Query(table, true); } } } } catch (Exception exception) { Logger.Error(this, "Query", exception, sql); return new Query(exception); } } /// 执行。 public IExecute Execute(string sql) => Execute(sql, null); /// 执行单条 Transact-SQL 语句,并加入参数。 public IExecute Execute(string sql, IEnumerable parameters) { if (TextUtility.IsBlank(sql)) return Example.InvalidExecuteStatement; var connected = Connect(); if (connected.NotEmpty()) return new Execute(false, connected); var inTransaction = _transaction != null; if (!inTransaction) Begin(); try { using (var command = NewCommand()) { command.Connection = _conn; command.Transaction = (DbTransaction)_transaction; if (Timeout != null) command.CommandTimeout = Timeout.Execute; command.CommandText = sql; if (parameters != null) { foreach (var parameter in parameters) { if (parameter != null) command.Parameters.Add(parameter); } } var rows = command.ExecuteNonQuery(); if (!inTransaction) Commit(); // todo 此处应该检查事务提交产生的错误。 return new Execute(true, rows); } } catch (Exception exception) { Logger.Error(this, "Execute", exception, sql); if (!inTransaction) Rollback(); return new Execute(exception); } } #endregion #region ORM - Query /// 查询记录。 /// 记录模型。 /// SQL 语句。 public Result Query(Type model, string sql) { if (_conn == null) return new Result("连接无效。"); if (model == null) return new Result("数据模型类型无效。"); if (string.IsNullOrEmpty(sql)) return new Result("SQL 语句无效。"); var query = Query(sql); var result = null as Result; if (query.Success) { try { var array = OrmHelper.Fill(query, model); result = new Result(array); } catch (Exception ex) { result = new Result(ex); } } else { result = new Result(query.Message); } query.Dispose(); return result; } /// public Result Query(string sql) where T : class, new() { var query = Query(sql); if (!query.Success) return new Result(query.Message); var records = query.Fill(); query.Dispose(); var result = new Result(records); return result; } #endregion #region Static /// 对文本转义,符合 SQL 安全性。可根据字段类型限制 UTF-8 字节数,默认为 0 时不限制字节数。 protected static string Escape(string text, int bytes = 0) { if (text.IsEmpty()) return ""; var t = text ?? ""; t = t.Replace("\\", "\\\\"); t = t.Replace("'", "\\'"); t = t.Replace("\n", "\\n"); t = t.Replace("\r", "\\r"); t = t.Replace("\b", "\\b"); t = t.Replace("\t", "\\t"); t = t.Replace("\f", "\\f"); if (bytes > 5) { if (t.Bytes(Encoding.UTF8).Length > bytes) { while (true) { t = t.Substring(0, t.Length - 1); if (t.Bytes(Encoding.UTF8).Length <= (bytes - 4)) break; } t = t + " ..."; } } return t; } #endregion /// protected abstract string GetConnectionString(); /// protected abstract DbConnection NewConnection(); /// protected abstract DbDataAdapter NewDataAdapter(string sql); /// protected abstract DbCommand NewCommand(); } }