diff --git a/Apewer.Source/Source/DbClient.cs b/Apewer.Source/Source/DbClient.cs
index c9c7031..63674a7 100644
--- a/Apewer.Source/Source/DbClient.cs
+++ b/Apewer.Source/Source/DbClient.cs
@@ -20,49 +20,80 @@ namespace Apewer.Source
#region Connection
+ string _key = TextUtility.Key();
Timeout _timeout = null;
IDbConnection _conn = null;
+ bool _disposed = false;
///
internal protected object Locker = new object();
+ /// 此连接的唯一标识。
+ public virtual string Key { get => _key; }
+
///
- public Timeout Timeout { get => _timeout; set => _timeout = value ?? Timeout.Default; }
+ public virtual 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 virtual bool Disposed { get => _disposed; }
+
+ /// 连接处于打开状态。
+ public virtual bool Online { get => _conn == null ? false : (_conn.State == ConnectionState.Open); }
/// 连接字符串。
public abstract string ConnectionString { get; }
+ /// 关闭连接后,执行的方法。
+ public virtual Action OnClosed { get; set; }
+
/// 连接数据库,若未连接则尝试连接,获取连接成功的状态。
public string Connect()
{
- if (_conn == null) _conn = GetConnection();
+ if (_disposed)
+ {
+ if (ThrowAdoException) throw new ObjectDisposedException(GetType().Name);
+ return "当前连接已释放,无法再次连接。";
+ }
+ if (_conn == null) _conn = GetConnection();
if (_conn.State == ConnectionState.Open) return null;
+
+ if (ThrowAdoException)
+ {
+ _conn.Open();
+ if (_conn.State != ConnectionState.Open)
+ {
+ var message = $"连接后状态为 {_conn.State},无法验证打开状态。";
+ Logger.Error(this, "Connect", message);
+ throw new SqlException(message);
+ }
+ return null;
+ }
+
try
{
_conn.Open();
- if (_conn.State == ConnectionState.Open) return null;
- var message = $"连接后状态为 {_conn.State},无法验证打开状态。";
- Logger.Error(this, "Connect", message);
- return "连接失败," + message;
+ if (_conn.State != ConnectionState.Open)
+ {
+ var message = $"连接后状态为 {_conn.State},无法验证打开状态。";
+ Logger.Error(this, "Connect", message);
+ return "连接失败," + message;
+ }
+ return null;
}
catch (Exception ex)
{
Logger.Error(this, "Connect", ex.Message);
- Close();
if (ThrowAdoException) throw;
return ex.Message;
}
}
/// 更改已打开的数据库。
- public string Change(string store)
+ public virtual string Change(string store)
{
if (store.IsEmpty())
{
@@ -89,6 +120,9 @@ namespace Apewer.Source
/// 关闭连接,并释放对象所占用的系统资源。
public virtual void Close()
{
+ if (_disposed) return;
+ _disposed = true;
+
if (_conn != null)
{
if (_transaction != null)
@@ -97,8 +131,10 @@ namespace Apewer.Source
else Rollback();
_transaction = null;
}
- try { ClearPool(); } catch { }
try { _conn.Close(); } catch { }
+
+ var onClosed = OnClosed;
+ if (onClosed != null) onClosed.Invoke(this);
}
}
@@ -108,9 +144,6 @@ namespace Apewer.Source
Close();
}
- /// 清理连接池,释放当前连接。
- protected virtual void ClearPool(bool all = false) { }
-
#endregion
#region Transaction
@@ -121,7 +154,7 @@ namespace Apewer.Source
private bool _autocommit = false;
/// 获取已启动的事务。
- public IDbTransaction Transaction { get => _transaction; }
+ public virtual IDbTransaction Transaction { get => _transaction; }
string Begin(bool commit, Class isolation)
{
@@ -160,7 +193,7 @@ namespace Apewer.Source
/// Unspecified = -1
正在使用与指定隔离级别不同的隔离级别,但是无法确定该级别。
///
/// 在连接的生命周期结束时未结束事务,指定 TRUE 将自动提交,指定 FALSE 将自动回滚。
- public string Begin(bool commit = false) => Begin(commit, null);
+ public virtual string Begin(bool commit = false) => Begin(commit, null);
///
/// 使用指定的事务锁定行为启动事务。
@@ -174,10 +207,10 @@ namespace Apewer.Source
///
/// 在连接的生命周期结束时未结束事务,指定 TRUE 将自动提交,指定 FALSE 将自动回滚。
/// 指定事务锁定行为,不指定时将使用默认值。
- public string Begin(bool commit, IsolationLevel isolation) => Begin(commit, new Class(isolation));
+ public virtual string Begin(bool commit, IsolationLevel isolation) => Begin(commit, new Class(isolation));
/// 提交事务。
- public string Commit()
+ public virtual string Commit()
{
if (_transaction == null)
{
@@ -203,7 +236,7 @@ namespace Apewer.Source
}
/// 从挂起状态回滚事务。
- public string Rollback()
+ public virtual string Rollback()
{
if (_transaction == null)
{
diff --git a/Apewer.Source/Source/MySql.cs b/Apewer.Source/Source/MySql.cs
index 323ba21..37946ad 100644
--- a/Apewer.Source/Source/MySql.cs
+++ b/Apewer.Source/Source/MySql.cs
@@ -63,14 +63,6 @@ namespace Apewer.Source
_connstr = cs;
}
- ///
- protected override void ClearPool(bool all = false)
- {
- var conn = Connection as MySqlConnection;
- if (conn != null) MySqlConnection.ClearPool(conn);
- if (all) MySqlConnection.ClearAllPools();
- }
-
#endregion
#region override
@@ -425,6 +417,37 @@ namespace Apewer.Source
#region static
+ /// 清空与指定连接关联的连接池。
+ public static void ClearPool(IDbConnection connection)
+ {
+ var special = connection as MySqlConnection;
+ if (special != null)
+ {
+ try
+ {
+ MySqlConnection.ClearPool(special);
+ }
+ catch { }
+ }
+ }
+
+ /// 清空与指定连接关联的连接池。
+ public static void ClearPool(IDbAdo instance)
+ {
+ var connection = instance?.Connection;
+ if (connection != null) ClearPool(connection);
+ }
+
+ /// 清空连接池。
+ public static void ClearAllPools()
+ {
+ try
+ {
+ MySqlConnection.ClearAllPools();
+ }
+ catch { }
+ }
+
///
///
///
diff --git a/Apewer.Source/Source/SqlClient.cs b/Apewer.Source/Source/SqlClient.cs
index d2a2299..d10edea 100644
--- a/Apewer.Source/Source/SqlClient.cs
+++ b/Apewer.Source/Source/SqlClient.cs
@@ -62,14 +62,6 @@ namespace Apewer.Source
_connstr = cs;
}
- ///
- protected override void ClearPool(bool all = false)
- {
- var conn = Connection as SqlConnection;
- if (conn != null) SqlConnection.ClearPool(conn);
- if (all) SqlConnection.ClearAllPools();
- }
-
#endregion
#region override
@@ -292,6 +284,37 @@ namespace Apewer.Source
#region special
+ /// 清空与指定连接关联的连接池。
+ public static void ClearPool(IDbConnection connection)
+ {
+ var special = connection as SqlConnection;
+ if (special != null)
+ {
+ try
+ {
+ SqlConnection.ClearPool(special);
+ }
+ catch { }
+ }
+ }
+
+ /// 清空与指定连接关联的连接池。
+ public static void ClearPool(IDbAdo instance)
+ {
+ var connection = instance?.Connection;
+ if (connection != null) ClearPool(connection);
+ }
+
+ /// 清空连接池。
+ public static void ClearAllPools()
+ {
+ try
+ {
+ SqlConnection.ClearAllPools();
+ }
+ catch { }
+ }
+
/// 获取列信息。
public ColumnInfo[] ColumnsInfo(string tableName)
{
diff --git a/Apewer.Windows/Internals/Interop/Kernel32.cs b/Apewer.Windows/Internals/Interop/Kernel32.cs
index a628f24..7cb39ed 100644
--- a/Apewer.Windows/Internals/Interop/Kernel32.cs
+++ b/Apewer.Windows/Internals/Interop/Kernel32.cs
@@ -34,6 +34,9 @@ namespace Apewer.Internals.Interop
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint GetFileSize(IntPtr hFile, out uint highSize);
+ [DllImport("kernel32.dll")]
+ private static extern uint GetLastError();
+
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
diff --git a/Apewer.Windows/Internals/Interop/LASTINPUTINFO.cs b/Apewer.Windows/Internals/Interop/LASTINPUTINFO.cs
new file mode 100644
index 0000000..82557fe
--- /dev/null
+++ b/Apewer.Windows/Internals/Interop/LASTINPUTINFO.cs
@@ -0,0 +1,10 @@
+namespace Apewer.Internals.Interop
+{
+
+ internal struct LASTINPUTINFO
+ {
+ public uint cbSize;
+ public uint dwTime;
+ }
+
+}
diff --git a/Apewer.Windows/Internals/Interop/User32.cs b/Apewer.Windows/Internals/Interop/User32.cs
index 3423590..8a67a21 100644
--- a/Apewer.Windows/Internals/Interop/User32.cs
+++ b/Apewer.Windows/Internals/Interop/User32.cs
@@ -100,6 +100,9 @@ namespace Apewer.Internals.Interop
[DllImport("User32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern short GetKeyState(int KeyCode);
+ [DllImport("User32.dll")]
+ public static extern bool GetLastInputInfo(ref LASTINPUTINFO Dummy);
+
[DllImport("user32.dll", EntryPoint = "GetSystemMenu")]
public static extern IntPtr GetSystemMenu(IntPtr hWnd, IntPtr bRevert);
@@ -144,6 +147,9 @@ namespace Apewer.Internals.Interop
[DllImport("user32.dll")]
public static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);
+ [DllImport("User32.dll")]
+ public static extern bool LockWorkStation();
+
///
///
///
diff --git a/Apewer.Windows/WindowsUtility.cs b/Apewer.Windows/WindowsUtility.cs
index 7ef5934..52833e2 100644
--- a/Apewer.Windows/WindowsUtility.cs
+++ b/Apewer.Windows/WindowsUtility.cs
@@ -550,6 +550,15 @@ namespace Apewer
User32.ShowWindow(i, 1);
}
+ /// 获取系统空闲的毫秒数。
+ public static uint GetIdleTime()
+ {
+ LASTINPUTINFO LastUserAction = new LASTINPUTINFO();
+ LastUserAction.cbSize = (uint)Marshal.SizeOf(LastUserAction);
+ User32.GetLastInputInfo(ref LastUserAction);
+ return ((uint)Environment.TickCount - LastUserAction.dwTime);
+ }
+
#endregion
#region 鼠标。
diff --git a/Apewer/Apewer.props b/Apewer/Apewer.props
index edd434c..1b888ef 100644
--- a/Apewer/Apewer.props
+++ b/Apewer/Apewer.props
@@ -9,7 +9,7 @@
Apewer
Apewer Libraries
- 6.7.3
+ 6.7.4
diff --git a/Apewer/Source/IDbAdo.cs b/Apewer/Source/IDbAdo.cs
index ebe8213..00ae508 100644
--- a/Apewer/Source/IDbAdo.cs
+++ b/Apewer/Source/IDbAdo.cs
@@ -25,6 +25,9 @@ namespace Apewer.Source
/// 数据库当前在线,表示连接可用。
bool Online { get; }
+ /// 关闭连接后,执行的方法。
+ Action OnClosed { get; set; }
+
/// 连接数据库,若未连接则尝试连接,返回错误信息。
string Connect();
diff --git a/Apewer/Source/SourceUtility.cs b/Apewer/Source/SourceUtility.cs
index bacc1fc..cc49b54 100644
--- a/Apewer/Source/SourceUtility.cs
+++ b/Apewer/Source/SourceUtility.cs
@@ -453,7 +453,7 @@ namespace Apewer.Source
/// SQL 语句。
/// SQL 参数。
///
- public static IQuery Query(this IDbClient dbClient, string sql, IEnumerable> parameters)
+ public static IQuery Query(this IDbAdo dbClient, string sql, IEnumerable> parameters)
{
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
@@ -467,7 +467,7 @@ namespace Apewer.Source
/// SQL 语句。
/// 参数容器,每个属性表示一个 SQL 参数。此方法将会自动补足参数名称的 @ 前缀。
///
- public static IQuery Query(this IDbClient dbClient, string sql, object parameters = null)
+ public static IQuery Query(this IDbAdo dbClient, string sql, object parameters = null)
{
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
@@ -490,7 +490,7 @@ namespace Apewer.Source
/// 执行 SQL 语句,并加入参数。
///
- public static IExecute Execute(this IDbClient dbClient, string sql, IEnumerable> parameters, bool autoTransaction = false)
+ public static IExecute Execute(this IDbAdo dbClient, string sql, IEnumerable> parameters, bool autoTransaction = false)
{
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
@@ -505,7 +505,7 @@ namespace Apewer.Source
/// 参数容器,每个属性表示一个 SQL 参数。此方法将会自动补足参数名称的 @ 前缀。
/// 自动使用事务。
///
- public static IExecute Execute(this IDbClient dbClient, string sql, object parameters = null, bool autoTransaction = false)
+ public static IExecute Execute(this IDbAdo dbClient, string sql, object parameters = null, bool autoTransaction = false)
{
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
@@ -524,10 +524,45 @@ namespace Apewer.Source
#endregion
+ #region Transaction
+
+ /// 启动事务,执行指定的过程并在完成后提交事务。若过程被异常打断,则回滚事务。
+ ///
+ ///
+ public static void InTransaction(this IDbAdo source, Action action)
+ {
+ // 检查参数。
+ if (source == null) throw new ArgumentNullException(nameof(source), "数据源无效。");
+ if (action == null) throw new ArgumentNullException(nameof(action), "没有指定要执行的过程。");
+
+ // 启动事务。
+ var begin = source.Begin();
+ if (begin.NotEmpty()) throw new SqlException("无法启动事务:" + begin);
+
+ try
+ {
+ // 在事务内运行。
+ action.Invoke();
+
+ // 执行成功,提交事务。
+ var commit = source.Commit();
+ if (!string.IsNullOrEmpty(commit)) throw new SqlException(commit);
+ }
+ catch (Exception ex)
+ {
+ // 执行失败,回滚事务。
+ try { source.Rollback(); } catch { }
+
+ throw ex;
+ }
+ }
+
+ #endregion
+
#region Parameter
///
- static List ParametersByProperites(IDbClient dbClient, string sql, object parameters)
+ static List ParametersByProperites(IDbAdo dbClient, string sql, object parameters)
{
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
if (parameters == null) return null;
@@ -574,7 +609,7 @@ namespace Apewer.Source
}
///
- static List Parameters(IDbClient dbClient, string sql, IEnumerable> parameters)
+ static List Parameters(IDbAdo dbClient, string sql, IEnumerable> parameters)
{
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
if (parameters == null) return null;
diff --git a/Apewer/Source/Timeout.cs b/Apewer/Source/Timeout.cs
index 3a22956..f7e04c9 100644
--- a/Apewer/Source/Timeout.cs
+++ b/Apewer/Source/Timeout.cs
@@ -18,29 +18,29 @@ namespace Apewer.Source
_execute = execute;
}
- /// 连接。
+ /// 连接超时秒数。
public int Connect
{
get { return _connect; }
set { _connect = (value >= 0) ? value : 0; }
}
- /// 查询。
+ /// 查询超时秒数。
public int Query
{
get { return _query; }
set { _query = (value >= 0) ? value : 0; }
}
- /// 执行。
+ /// 执行超时秒数。
public int Execute
{
get { return _execute; }
set { _execute = (value >= 0) ? value : 0; }
}
- /// 默认超时设置:连接 5000、查询 60000,执行 60000。
- public static Timeout Default { get => new Timeout(5000, 60000, 60000); }
+ /// 默认超时设置:连接 5 秒、查询 3600 秒,执行 3600 秒。
+ public static Timeout Default { get => new Timeout(5, 3600, 3600); }
}
diff --git a/ChangeLog.md b/ChangeLog.md
index da70e18..ae285f0 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,6 +1,16 @@
### 最新提交
+### 6.7.4
+- 问题修正
+ - Source:修正了 Timeout 的默认值,已经改为连接 5 秒,查询、执行 3600 秒。
+- 新特性
+ - Source:增加扩展方法 IDbAdo.InTransaction;
+ - WindowsUtility:增加 GetIdleTime 方法,获取系统空闲毫秒数。
+- 已迁移特性
+ - Source:调用 Close 后,不再允许再次连接(之前可以再次发起连接),错误的操作将返回错误信息或抛出 ObjectDisposedException;
+ - Source:分离 ClearPool 方法,去除自动执行,现在可以使用 IDbAdo.OnClosed 属性设置关闭连接之后的回调。
+
### 6.7.3
- 问题修正
- ClockUtility:修正 Query.DateTime() 出现 null.ToString 问题;
@@ -32,7 +42,8 @@
- 迁移 Query 方法,改为 DataTable 的扩展方法。
- 弃用特性
- 去除 object 转数值类型的扩展方法,现在扩展方法只支持明确支持的类型。
- - Class 去除了无用的 HasValue 属性
+ - Class 去除了无用的 HasValue 属性。
+
### 6.7.2
- 问题修正