写入Access数据库时间歇性报Data Type Mismatch错误问题咨询
问题描述
开发Windows窗体应用用于采集员工工伤数据,数据写入Access数据库后支持打印表单、提交保险机构,并按OSHA规范完成数据格式化。
使用的Access数据库包含三类核心字段:短文本、是/否(对应程序复选框)、长文本。程序开发阶段运行完全正常,部署beta版本供测试人员使用时立即抛出*Data Type Mismatch(数据类型不匹配)*错误,部署前后未修改任何代码或数据库结构。
异常特征
逐行排查问题字段时,发现异常偶发于长文本字段:
- 单独测试单个长文本字段时可正常写入
- 继续测试后续字段过程中会突然复现报错
- 回溯排查发现报错源为此前测试正常的早期字段,相关逻辑从未改动
已尝试排查方案
- 重建Access数据表
- 多次重命名字段
- 核对字段数据类型匹配性(同类型数据数分钟前可正常写入)
- 确认写入长文本字段的内容为字符串类型,其他窗体的长文本字段写入逻辑运行正常
- 尝试将对应字段改为短文本类型,但业务场景需要存储超过255字符的事故过程描述,必须使用长文本类型
目前已准备完全重建窗体排查异常,怀疑问题属于软件本身故障,附相关代码与报错截图如下:
报错截图
业务实现代码
Private Function ReportInj(path As String) If String.IsNullOrEmpty(path) Then path = "C:\Users\Public\Documents\EmpManager\Placeholder Images\NoSig.jpg" Dim buffer As Byte() Using img As Image = Image.FromFile(path) Dim ms As New MemoryStream() img.Save(ms, img.RawFormat) buffer = ms.GetBuffer() End Using access.AddParam("@Today", Today) access.AddParam("@EmpName", EmpName) access.AddParam("@EmpID", lblEmpID.Text) access.AddParam("@Add", txtAddress1.Text + " " + txtAddress2.Text) access.AddParam("@City", City) access.AddParam("@State", St) access.AddParam("@Zip", Zip) access.AddParam("@Phone", txtPhone.Text) access.AddParam("@DOB", lblDOB.Text) access.AddParam("@M", Male) access.AddParam("@F", Female) access.AddParam("@SSN", lblSSN.Text) access.AddParam("@HDate", lblHDate.Text) access.AddParam("@DOA", dpDOA.Value.Date) access.AddParam("@TOA", TOA) access.AddParam("@Part", txtBodyPt.Text) access.AddParam("@Before", txtBefore.Text) access.AddParam("@Cause", txtCause.Text) 'access.AddParam("@Prior", Prior) 'access.AddParam("@Witness", Witness) 'access.AddParam("@Who", txtWho.Text) 'access.AddParam("@Boss", cbxBoss.SelectedItem) 'access.AddParam("@Sig", buffer) 'access.AddParam("@FileLoc", lblSigFileLoc.Text) 'access.AddParam("@Job", cbxJob.Text) 'access.AddParam("@Shift", cbxShift.Text) 'access.AddParam("@Where", Where) access.ExecQuery("INSERT INTO AccEmp1(ReportDate, EmpName, EmpID, Address, City, State, Zip, " & "Phone, DOB, Male, Female, SSN, HireDate, AccidentDate, AccTime, BodyPt, BeforeDoing, " & "WhatDidDamage)" & "VALUES (@Today, @EmpName1, @EmpID, @Add, @City, @State, @Zip, " & "@Phone, @DOB, @M, @F, @SSN, @HDate, @DOA, @TOA, @Part, @Before," & "@Cause) ") 'This is all my notes where i'm taking stuff out and adding stuff back in above. ', DoingBefore" & ' ", PriorInjury, Witness, [Names], Supervisor, EmpSig, SigFileLoc, JobTitle, ShiftStart, AccLoc ' " & ' ", @Prior, @Witness, @Who, @Boss, @Sig, @FileLoc, @Job, @Shift, @Where ' On Errors - Return false If Not String.IsNullOrEmpty(access.exception) Then MsgBox(access.exception) : Return False 'On Success - Return True Return True End Function
数据库操作类代码
Imports System.Data.OleDb Public Class dbControl 'CREATE YOUR DB CONNECTION Private DBCon As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;" & "Data Source=\\thebrain\shared\Database-DO NOT TOUCH!!\NewEmpManager\EmpManager.accdb;Persist Security Info=True;Jet OLEDB:Database Password=*****") ' PREPARE DB COMMAND Private DBCmd As OleDbCommand 'DB DATA Public DBDA As OleDbDataAdapter Public DBDT As DataTable ' QUERY PARAMETERS Public Params As New List(Of OleDbParameter) ' QUERY STATISTICS Public RecordCount As Integer Public exception As String Public Sub ExecQuery(Query As String) ' RESET QUERY STATS RecordCount = 0 exception = "" Try ' OPEN A CONNECTION DBCon.Open() ' CREATE DB COMMAND DBCmd = New OleDbCommand(Query, DBCon) ' LOAD PARAMS INTO DB COMMAND Params.ForEach(Sub(p) DBCmd.Parameters.Add(p)) ' CLEAR PARAMS LIST Params.Clear() ' EXECUTE COMMAND AND FILL DATATABLE DBDT = New DataTable DBDA = New OleDbDataAdapter(DBCmd) RecordCount = DBDA.Fill(DBDT) Catch ex As Exception exception = ex.Message End Try 'CLOSE YOUR CONNECTION If DBCon.State = ConnectionState.Open Then DBCon.Close() End Sub 'INCLUDE QUERY & COMMAND PARAMETERS Public Sub AddParam(Name As String, Value As Object) Dim NewParam As New OleDbParameter(Name, Value) Params.Add(NewParam) End Sub End Class
问题根因与修复方案
该问题是OleDb连接Access的经典特性/bug,和窗体逻辑、Access本身故障无关,核心诱因有三个:
- OleDb参数严格按添加顺序绑定,不识别参数名
OleDb提供程序不会按参数名匹配占位符,只会按照参数添加的先后顺序,依次绑定到VALUES语句的对应位置。当前代码里VALUES占位符写的@EmpName1和添加的参数名@EmpName不一致,且后续注释/取消注释参数时,只要参数添加顺序和字段顺序有一处错位,就会把布尔值、日期值绑定到文本字段,触发类型不匹配。 - 未显式指定长文本参数类型,触发ACE引擎类型推断bug
现有AddParam方法直接传入对象,未显式指定OleDbType:传入字符串时,OleDb会默认根据字符串长度推断类型,长度≤255时自动映射为短文本(VarWChar,最大长度255),不会识别为长文本(LongVarWChar)。当写入的长文本内容超过255字符时,短文本类型参数写入长文本字段就会报类型不匹配;如果内容短于255就能正常写入,这就是异常偶发的核心原因——测试时输入内容长度不固定,参数类型推断结果随机。 - 空值未做DBNull转换
当文本框内容为空、变量值为Nothing时,OleDb不接受null值,必须显式传入DBNull.Value,否则也会随机触发类型不匹配。
修复步骤
1. 修正参数顺序与占位符
将INSERT语句VALUES中的@EmpName1改为@EmpName,后续加回注释的参数时,必须严格保证AddParam调用顺序、INSERT后字段顺序、VALUES占位符顺序三者完全一致,不要依赖参数名匹配。
2. 改造AddParam方法
替换原有AddParam方法,支持显式指定参数类型、自动转换空值:
' 重载AddParam,支持指定字段类型、自动处理空值 Public Sub AddParam(Name As String, Value As Object, Optional dbType As OleDbType = OleDbType.Empty, Optional size As Integer = -1) Dim NewParam As New OleDbParameter() NewParam.ParameterName = Name ' 空值统一转换为DBNull NewParam.Value = If(Value Is Nothing, DBNull.Value, Value) ' 显式指定字段类型 If dbType <> OleDbType.Empty Then NewParam.OleDbType = dbType End If ' 指定字段长度(长文本无需设置) If size > 0 Then NewParam.Size = size End If Params.Add(NewParam) End Sub
3. 长文本字段传参时显式标记类型
所有映射到Access长文本类型的字段,添加参数时显式指定类型为OleDbType.LongVarWChar:
' 原有短文本、日期、布尔参数写法不变 access.AddParam("@Before", txtBefore.Text, OleDbType.LongVarWChar) access.AddParam("@Cause", txtCause.Text, OleDbType.LongVarWChar) ' 后续加回其他长文本字段(事故地点、备注等)均按此写法传参
额外优化
后续加回签名图片参数时,将buffer = ms.GetBuffer()改为buffer = ms.ToArray()。GetBuffer()会返回内存流的全量预留缓冲区,包含大量无效空字节,容易导致二进制字段写入异常。
内容的提问来源于stack exchange,提问作者Catherine Wisian
相关产品推荐
相关产品推荐

