From 59e4e98a601a59768fff1dae0dab3f84129f2e3a Mon Sep 17 00:00:00 2001 From: Elivo Date: Sat, 16 Sep 2023 15:12:24 +0800 Subject: [PATCH] Apewer-6.7.4 --- Apewer.Source/Source/DbClient.cs | 71 ++++++++++++++----- Apewer.Source/Source/MySql.cs | 39 +++++++--- Apewer.Source/Source/SqlClient.cs | 39 +++++++--- Apewer.Windows/Internals/Interop/Kernel32.cs | 3 + .../Internals/Interop/LASTINPUTINFO.cs | 10 +++ Apewer.Windows/Internals/Interop/User32.cs | 6 ++ Apewer.Windows/WindowsUtility.cs | 9 +++ Apewer/Apewer.props | 2 +- Apewer/Source/IDbAdo.cs | 3 + Apewer/Source/SourceUtility.cs | 47 ++++++++++-- Apewer/Source/Timeout.cs | 10 +-- ChangeLog.md | 13 +++- 12 files changed, 204 insertions(+), 48 deletions(-) create mode 100644 Apewer.Windows/Internals/Interop/LASTINPUTINFO.cs 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 - 问题修正