diff --git a/Apewer.Source/Source/Access.cs b/Apewer.Source/Source/Access.cs
index b5bd605..8857cd5 100644
--- a/Apewer.Source/Source/Access.cs
+++ b/Apewer.Source/Source/Access.cs
@@ -24,7 +24,7 @@ namespace Apewer.Source
#if NETFRAMEWORK
- public partial class Access : IDbClientBase, IDbClientAdo, IDisposable
+ public partial class Access : IDbAdo
{
#region 连接
diff --git a/Apewer.Source/Source/DbClient.cs b/Apewer.Source/Source/DbClient.cs
new file mode 100644
index 0000000..a0541db
--- /dev/null
+++ b/Apewer.Source/Source/DbClient.cs
@@ -0,0 +1,480 @@
+#if DEBUG
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Text;
+
+namespace Apewer.Source
+{
+
+ /// 数据库客户端基类。
+
+
+ public abstract class DbClient : IDbClient
+ {
+
+ ///
+ public virtual Logger Logger { get; set; }
+
+ ///
+ public DbClient(Timeout timeout) { _timeout = timeout ?? Timeout.Default; }
+
+ #region connection
+
+ Timeout _timeout = null;
+ IDbConnection _conn = null;
+ string _str = null;
+
+ ///
+ public Timeout Timeout { get => _timeout; set => _timeout = value ?? Timeout.Default; }
+
+ /// 获取当前的 SqlConnection 对象。
+ public IDbConnection Connection { get => _conn; }
+
+ ///
+ public bool Online { get => _conn == null ? false : (_conn.State == ConnectionState.Open); }
+
+ /// 连接字符串。
+ public string ConnectionString { get => _str; }
+
+ /// 连接数据库,若未连接则尝试连接,获取连接成功的状态。
+ public string Connect()
+ {
+ if (_conn == null)
+ {
+ _str = NewConnectionString();
+ _conn = NewConnection();
+ _conn.ConnectionString = _str;
+ }
+ else
+ {
+ if (_conn.State == ConnectionState.Open) return null;
+ }
+
+ try
+ {
+ _conn.Open();
+ if (_conn.State == ConnectionState.Open) return null;
+ var message = $"连接后状态为 {_conn.State},无法验证打开状态。";
+ Logger.Error(this, "Connect", message, _str);
+ return "连接失败," + message;
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(this, "Connect", ex.GetType().Name, ex.Message, _str);
+ 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 IDbTransaction _transaction = null;
+ private bool _autocommit = false;
+
+ ///
+ /// 启动事务,可指定事务锁定行为。
+ /// Chaos
无法覆盖隔离级别更高的事务中的挂起的更改。
+ /// ReadCommitted
在正在读取数据时保持共享锁,以避免脏读,但是在事务结束之前可以更改数据,从而导致不可重复的读取或幻像数据。
+ /// ReadUncommitted
可以进行脏读,意思是说,不发布共享锁,也不接受独占锁。
+ /// RepeatableRead
在查询中使用的所有数据上放置锁,以防止其他用户更新这些数据。 防止不可重复的读取,但是仍可以有幻像行。
+ /// Serializable
在 System.Data.DataSet 上放置范围锁,以防止在事务完成之前由其他用户更新行或向数据集中插入行。
+ /// Snapshot
通过在一个应用程序正在修改数据时存储另一个应用程序可以读取的相同数据版本来减少阻止。 表示您无法从一个事务中看到在其他事务中进行的更改,即便重新查询也是如此。
+ /// Unspecified = -1
正在使用与指定隔离级别不同的隔离级别,但是无法确定该级别。
+ ///
+ /// 在连接的生命周期结束时未结束事务,指定 TRUE 将自动提交,指定 FALSE 将自动回滚。
+ /// 指定事务锁定行为,不指定时将使用默认值。
+ public string Begin(bool commit = false, Class isolation = null)
+ {
+ if (_transaction != null) return "已存在未完成的事务,无法再次启动。";
+ var connect = Connect();
+ if (connect.NotEmpty()) return $"无法启动事务:连接失败。(${connect})";
+ try
+ {
+ _transaction = isolation ? _conn.BeginTransaction(isolation.Value) : _conn.BeginTransaction();
+ _autocommit = commit;
+ return null;
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(this, "Begin", 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.IsEmpty(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);
+ }
+ }
+
+ var ex = null as Exception;
+ var da = null as IDataAdapter;
+ try { da = NewDataAdapter(command); }
+ catch (Exception adapterEx) { ex = adapterEx; }
+ if (ex == null)
+ {
+ using (var ds = new DataSet())
+ {
+ da.Fill(ds);
+ if (ds.Tables.Count > 0)
+ {
+ var tables = new DataTable[ds.Tables.Count];
+ ds.Tables.CopyTo(tables, 0);
+ return new Query(tables, true);
+ }
+ else
+ {
+ Logger.Error(this, "Query", "查询结果不包含任何数据表。", sql);
+ return new Query(false, "查询结果不包含任何数据表。");
+ }
+ }
+ }
+ else
+ {
+ Logger.Error(this, "Query", ex.GetType().FullName, ex.Message, sql);
+ return new Query(ex);
+ }
+ }
+ }
+ catch (Exception exception)
+ {
+ Logger.Error(this, "Query", exception.GetType().FullName, exception.Message, sql);
+ return new Query(exception);
+ }
+ }
+
+ /// 执行。
+ public IExecute Execute(string sql) => Execute(sql, null);
+
+ /// 执行单条 Transact-SQL 语句,并加入参数。
+ public IExecute Execute(string sql, IEnumerable parameters)
+ {
+ if (TextUtility.IsEmpty(sql)) return new Execute(false, "语句无效。");
+
+ 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);
+ }
+ }
+
+ /// 输出查询结果的首列数据。
+ protected string[] TextColumn(string sql, string[] excluded = null)
+ {
+ if (Connect().NotEmpty()) return new string[0];
+ using (var query = Query(sql))
+ {
+ var rows = query.Rows;
+ var list = new List(rows);
+ for (int r = 0; r < query.Rows; r++)
+ {
+ var cell = query.Text(r, 0);
+ if (TextUtility.IsEmpty(cell)) continue;
+ if (excluded != null && excluded.Contains(cell)) continue;
+ list.Add(cell);
+ }
+ return list.ToArray();
+ }
+ }
+
+ #endregion
+
+ #region orm
+
+ /// 使用指定语句查询,获取查询结果。
+ /// 要执行的 SQL 语句。
+ /// 为 SQL 语句提供的参数。
+ public Result Query(string sql, IEnumerable parameters = null) where T : class, new() => OrmHelper.As