WPF应用出现Sql连接池超时异常,寻求解决方案
解决数据库连接池超时问题的方案
根本原因
你的代码存在无限递归调用,短时间内创建大量数据库连接导致连接池耗尽:
- 当
UserName属性的setter触发时,会调用UsersCollection.GetAllUserNames()拉取所有用户名 GetAllUserNames()中每读取一个用户名,就会通过Users.GetUserNamesFromResultSet(reader)创建Users对象- 若
Users构造函数是通过UserName属性赋值(而非直接操作私有字段),会再次触发UserName的setter,进而重复调用GetAllUserNames(),形成无限递归 - 每次递归都会打开新的数据库连接,连接池被快速占满,最终抛出超时异常
具体修复步骤
1. 修复Users构造函数,终止递归
确保构造函数直接给私有字段_username赋值,不触发属性setter:
// 在Users类中添加对应构造函数 public Users(string username) { // 直接操作私有字段,避免触发UserName属性的setter _username = username; }
2. 优化用户名重复检查逻辑,减少连接消耗
不要一次性拉取所有用户名到内存遍历,改成直接在数据库中查询目标用户名是否存在,仅需一次数据库交互:
// 替换UserName setter中检查重复的代码段 using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnString"].ConnectionString)) { conn.Open(); string checkSql = "SELECT COUNT(1) FROM Users WHERE UserName = @UserName"; using (SqlCommand command = new SqlCommand(checkSql, conn)) { command.Parameters.AddWithValue("@UserName", value); int count = (int)command.ExecuteScalar(); if (count > 0) { errors.Add("Username already exist."); SetErrors("UserName", errors); valid = false; } } }
3. 避免在属性setter中执行耗时操作(推荐优化)
属性setter应仅负责快速赋值和通知变更,将验证逻辑移到专门方法中,在需要时(如用户提交表单)调用:
// 在Users类中添加专用验证方法 public bool ValidateUserName(string userName) { List<string> errors = new List<string>(); bool valid = true; if (string.IsNullOrEmpty(userName) || userName.Length < 5) { errors.Add("Username can not have less than 5 characters."); SetErrors("UserName", errors); valid = false; } // 数据库重复检查 using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnString"].ConnectionString)) { conn.Open(); string checkSql = "SELECT COUNT(1) FROM Users WHERE UserName = @UserName"; using (SqlCommand command = new SqlCommand(checkSql, conn)) { command.Parameters.AddWithValue("@UserName", userName); int count = (int)command.ExecuteScalar(); if (count > 0) { errors.Add("Username already exist."); SetErrors("UserName", errors); valid = false; } } } if (!Regex.Match(userName, @"^\w+$").Success) { errors.Add("Username can only contain letters, numbers and underscore characters."); SetErrors("UserName", errors); valid = false; } if (valid) { ClearErrors("UserName"); } return valid; } // 修改UserName属性的setter,仅保留核心逻辑 public string UserName { get { return _username; } set { if (_username == value) return; _username = value; OnPropertyChanged(new PropertyChangedEventArgs("UserName")); } }
额外建议
- 始终用
using语句包裹数据库连接,确保连接被正确释放回连接池 - 不要随意调整连接字符串的
Max Pool Size参数,解决递归和逻辑问题才是根本
内容的提问来源于stack exchange,提问作者Aleksandar Veljkovic
相关产品推荐
相关产品推荐

