C#中SQLite数据库锁定异常排查:已尝试方案仍未解决
解决SQLite数据库锁定异常的问题
嘿,我帮你找到了几个导致SQLite数据库锁定的关键问题,咱们一步步拆解并修复:
1. 别再用MultipleActiveResultSets=True了!
这个参数是SQL Server专属的特性,SQLite完全不支持它——你加在连接字符串里不仅没用,反而可能干扰连接的正常行为,直接删掉就好。
2. 类级别的连接对象是大坑(核心问题)
你在AdminForm里定义了全局的ObservationDBConnection,然后在构造函数里打开又关闭,后续SaveButton_Click再复用这个连接。但SQLite是文件型数据库,同一时间只能有一个写入连接,一旦连接因为异常没被正确释放(比如Close()没执行到),就会直接锁死数据库。
正确做法:永远不要复用连接对象,每次操作数据库时,用using语句创建新连接——using会自动帮你释放连接(哪怕代码抛出异常),这是数据库操作的标准最佳实践。
3. 忘了关闭DataReader?它会占着连接不放
你的代码里很多SQLiteDataReader都没调用Close(),也没包在using里。DataReader会保持连接处于占用状态,直到被关闭,这也是导致锁定的常见原因。
4. 紧急提醒:你的代码有严重的SQL注入风险
所有直接拼接用户输入的SQL语句(比如VALUES ('" + originatorName + "', ...))都是高危操作——不仅会导致SQL语法错误(比如用户输入里有单引号),还会被黑客利用注入恶意代码。必须改用参数化查询。
修复后的示例代码
1. 插入Originator的正确写法
// 去掉无用的MARS参数 string connectionString = @"Data Source=D:\CCIW\LCM\Organisational Database\OrganisationalDB;"; using (SQLiteConnection originatorDBConnection = new SQLiteConnection(connectionString)) { originatorDBConnection.Open(); // 用参数占位符代替直接拼接 string originatorINSERT = @"INSERT INTO Originator (Name, Organisation, Address, CellphoneNumber, TelephoneNumber, Email) VALUES (@Name, @Organisation, @Address, @CellphoneNumber, @TelephoneNumber, @Email);"; using (SQLiteCommand originatorCommand = new SQLiteCommand(originatorINSERT, originatorDBConnection)) { // 添加参数,既防注入又避免语法错误 originatorCommand.Parameters.AddWithValue("@Name", originatorNameTextBox.Text); originatorCommand.Parameters.AddWithValue("@Organisation", originatorOrganisationTextBox.Text); originatorCommand.Parameters.AddWithValue("@Address", originatorAddressRichTextBox.Text); originatorCommand.Parameters.AddWithValue("@CellphoneNumber", originatorCellTextBox.Text); originatorCommand.Parameters.AddWithValue("@TelephoneNumber", originatorTelTextBox.Text); originatorCommand.Parameters.AddWithValue("@Email", originatorEmailTextBox.Text); originatorCommand.ExecuteNonQuery(); } // 不需要手动Close(),using会自动处理连接释放 }
2. AdminForm构造函数的修复版
public AdminForm() { InitializeComponent(); string connectionString = @"Data Source=D:\CCIW\LCM\Organisational Database\OrganisationalDB;"; // 用using包裹连接,确保自动释放 using (SQLiteConnection observationDBConnection = new SQLiteConnection(connectionString)) { observationDBConnection.Open(); // 查询Originator,用using包裹DataReader自动关闭 string originatorSELECT = "SELECT * FROM Originator;"; using (SQLiteCommand command = new SQLiteCommand(originatorSELECT, observationDBConnection)) using (SQLiteDataReader reader = command.ExecuteReader()) { List<string> originatorNames = new List<string>(); while (reader.Read()) { originatorNames.Add(Convert.ToString(reader["Name"])); } OriginatorNameComboBox.DataSource = originatorNames; } // 查询ECP的逻辑同理 string ecpNumberSELECT = "SELECT * FROM ECP"; using (SQLiteCommand command2 = new SQLiteCommand(ecpNumberSELECT, observationDBConnection)) using (SQLiteDataReader reader2 = command2.ExecuteReader()) { List<string> ecpNumbers = new List<string>(); while (reader2.Read()) { ecpNumbers.Add(Convert.ToString(reader2["Number"])); } ECPNumComboBox.DataSource = ecpNumbers; } // 填充TC Decision下拉框的逻辑不变 List<string> tcDecision = new List<string>(); tcDecision.Add("Rework"); tcDecision.Add("Reject"); tcDecision.Add("Approve"); TCDecisionComboBox.DataSource = tcDecision; } }
3. SaveButton_Click的修复片段
private void SaveButton_Click(object sender, EventArgs e) { string connectionString = @"Data Source=D:\CCIW\LCM\Organisational Database\OrganisationalDB;"; using (SQLiteConnection observationDBConnection = new SQLiteConnection(connectionString)) { observationDBConnection.Open(); // 参数化插入ImpactType string impactTypeINSERT = @"INSERT INTO ImpactType (ImpactType, Description) VALUES (@ImpactType, @Description);"; using (SQLiteCommand impactTypeCommand = new SQLiteCommand(impactTypeINSERT, observationDBConnection)) { impactTypeCommand.Parameters.AddWithValue("@ImpactType", impactType); impactTypeCommand.Parameters.AddWithValue("@Description", impactDescription); impactTypeCommand.ExecuteNonQuery(); } // 修复你之前的笔误:ExecuteReader用错了命令对象 string tcDecisionINSERT = @"INSERT INTO TCDecision (Decision, Description) VALUES (@Decision, @Description);"; using (SQLiteCommand tcDecisionCommand = new SQLiteCommand(tcDecisionINSERT, observationDBConnection)) { tcDecisionCommand.Parameters.AddWithValue("@Decision", TechnicalCommitteeDecision); tcDecisionCommand.Parameters.AddWithValue("@Description", TechnicalCommitteeDescription); tcDecisionCommand.ExecuteNonQuery(); } // 其他插入/查询逻辑都照这个模式:用using包裹命令和DataReader,用参数化查询 // 示例:参数化查询OriginatorID string originatorIDSELECT = @"SELECT ID FROM Originator WHERE Name=@Name;"; using (SQLiteCommand originatorIDCommand = new SQLiteCommand(originatorIDSELECT, observationDBConnection)) { originatorIDCommand.Parameters.AddWithValue("@Name", OriginatorNameComboBox.Text); using (SQLiteDataReader originatorIDReader = originatorIDCommand.ExecuteReader()) { // 先判断是否有结果再取值,避免空引用 if (originatorIDReader.Read()) { originatorIDOBS = originatorIDReader.GetInt32(0); } } } } }
额外排查小技巧
- 检查有没有其他程序(比如SQLite管理工具)正在打开这个数据库文件,有的话关掉它;
- 确保你用的是最新版本的SQLite库,旧版本可能存在锁定相关的bug。
内容的提问来源于stack exchange,提问作者Zenadia Groenewald
相关产品推荐
相关产品推荐

