C# WinForms更新Access数据库触发数据类型不匹配异常求解决
Access更新报错"Data type mismatch in criteria expression"的解决思路
核心问题定位
你遇到的类型不匹配异常,核心原因是OLEDB的参数绑定规则:包括Access驱动在内的OLEDB组件不支持按参数名称匹配,而是严格按照参数在SQL语句中出现的顺序绑定后续添加的参数。你的代码中SQL语句的参数顺序和cmd.Parameters.Add的顺序完全错位,导致类型不兼容的值被绑定到错误字段,触发异常。
具体错位对比
SQL语句中参数出现的顺序:
@sendDaily(对应SEND_DAILY字段,布尔类型)@sendDailyTime(对应SEND_DAILY_TIME字段,日期类型)@sendMonthly(对应SEND_MONTHLY字段,布尔类型)@sendMonthlyTime(对应SEND_MONTHLY_TIME字段,日期类型)@mailTo1(对应MAIL_TO_1字段,字符串类型)@mailTo2(对应MAIL_TO_2字段,字符串类型)@mailTo3(对应MAIL_TO_3字段,字符串类型)
而你添加参数的顺序是:
@mailTo1(字符串)→ 对应SQL第一个布尔字段→类型不匹配@mailTo2(字符串)→ 对应SQL第二个日期字段→类型不匹配
...以此类推,必然触发类型错误。
解决方案
1. 严格对齐参数顺序
调整cmd.Parameters.Add的顺序,完全匹配SQL语句中参数出现的顺序:
internal static string SaveTripleMail(TripleMail mailSett, OleDbConnection cnn) { string response; StringBuilder sb = new StringBuilder(); string PRM_MAIL_TO_1 = "@mailTo1"; string PRM_MAIL_TO_2 = "@mailTo2"; string PRM_MAIL_TO_3 = "@mailTo3"; string PRM_SEND_DAILY = "@sendDaily"; string PRM_SEND_MONTHLY = "@sendMonthly"; string PRM_SEND_TIME = "@sendDailyTime"; string PRM_MONTHLY_TIME = "@sendMonthlyTime"; try { ReopenConnection(cnn); sb.Append("UPDATE " + AccessDbConstants.TABLE_MAIL + " "); sb.Append("SET " + AccessDbConstants.SEND_DAILY + " = " + PRM_SEND_DAILY + ", "); sb.Append(AccessDbConstants.SEND_DAILY_TIME + " = " + PRM_SEND_TIME + ", "); sb.Append(AccessDbConstants.SEND_MONTHLY + " = " + PRM_SEND_MONTHLY + ", "); sb.Append(AccessDbConstants.SEND_MONTHLY_TIME + " = " + PRM_MONTHLY_TIME + ", "); sb.Append(AccessDbConstants.MAIL_TO_1 + " = " + PRM_MAIL_TO_1 + ", "); sb.Append(AccessDbConstants.MAIL_TO_2 + " = " + PRM_MAIL_TO_2 + ", "); sb.Append(AccessDbConstants.MAIL_TO_3 + " = " + PRM_MAIL_TO_3 + " "); // 必须添加WHERE条件,否则会更新表中所有记录! // sb.Append("WHERE [主键字段名] = @PrimaryKey"); // 替换为实际主键 using (OleDbCommand cmd = new OleDbCommand(sb.ToString(), cnn)) { // 严格按照SQL中参数出现的顺序添加 cmd.Parameters.Add(new OleDbParameter(PRM_SEND_DAILY, Convert.ToBoolean(mailSett.bSendDaily))); cmd.Parameters.Add(new OleDbParameter(PRM_SEND_TIME, Convert.ToDateTime(mailSett.sendDaily))); cmd.Parameters.Add(new OleDbParameter(PRM_SEND_MONTHLY, Convert.ToBoolean(mailSett.bSendMonthly))); cmd.Parameters.Add(new OleDbParameter(PRM_MONTHLY_TIME, Convert.ToDateTime(mailSett.sendMonthly))); // 处理空值:Access不接受直接传null,用DBNull.Value替代 cmd.Parameters.Add(new OleDbParameter(PRM_MAIL_TO_1, string.IsNullOrEmpty(mailSett.mailTo1) ? DBNull.Value : (object)mailSett.mailTo1)); cmd.Parameters.Add(new OleDbParameter(PRM_MAIL_TO_2, string.IsNullOrEmpty(mailSett.mailTo2) ? DBNull.Value : (object)mailSett.mailTo2)); cmd.Parameters.Add(new OleDbParameter(PRM_MAIL_TO_3, string.IsNullOrEmpty(mailSett.mailTo3) ? DBNull.Value : (object)mailSett.mailTo3)); cmd.ExecuteNonQuery(); response = "Settings saved successfully"; } } catch (Exception ex) { response = "There was an error: " + ex.Message; } return response; }
2. 额外优化建议
- 强制添加WHERE条件:原代码没有WHERE子句,会更新表中所有记录,这几乎肯定不是预期行为,务必加上主键过滤条件。
- 用
?替代命名参数:因为OLEDB不识别命名参数,用?作为占位符更直观,避免误解,示例:sb.Append("SET " + AccessDbConstants.SEND_DAILY + " = ?, "); sb.Append(AccessDbConstants.SEND_DAILY_TIME + " = ?, "); // ...其他字段同理 // 添加参数时无需指定名称,直接按顺序绑定 cmd.Parameters.Add(null, OleDbType.Boolean).Value = mailSett.bSendDaily; cmd.Parameters.Add(null, OleDbType.Date).Value = mailSett.sendDaily; // ... - 验证数据库字段类型:确认Access表中字段类型与C#参数类型匹配:
- SEND_DAILY/SEND_MONTHLY:Access的
Yes/No类型对应C#的bool - SEND_DAILY_TIME/SEND_MONTHLY_TIME:Access的
Date/Time类型对应C#的DateTime - MAIL_TO_1/2/3:Access的
Short Text类型对应C#的string
- SEND_DAILY/SEND_MONTHLY:Access的
内容的提问来源于stack exchange,提问作者Ofir Kadosh
相关产品推荐
相关产品推荐

