将用C#编写的旧应用程序(> 15岁)迁移到新的Windows Server之后,我们遇到了一个奇怪的问题。 该应用程序使用OleDB连接到数据库,该数据库是Informix数据库。该数据库有一个表,其中包含多种语言的文本。在Windows 2003服务器上运行的应用程序运行正常,但是在新的Windows 2016中会引发错误: “除了符号不匹配或数据溢出以外,无法转换数据值。例如,数据在数据存储中已损坏,但该行仍可检索。”
经过调查,我们发现问题出在包含某些Unicode字符的字符串中。
这是产生问题的文本部分(仅部分文本说明了问题:
“ 17”-Leichtmetallräder...... Ziffern-Schaltknauf”
这是德语文本,似乎还可以,问题实际上出在“-”上。查看十六进制的数据库记录,第一个“-”编码为“ 3F”,但是第二个破折号编码为“ C296”,对应于U + 0096(Unicode中的破折号)
数据库的设置为en_US.819(对应于ISO-8859-1,以支持所有需要支持的语言)。
现在,问题在于,在Windows 2003中运行该程序时,结果将正确写入文件中,例如:
“ 17”-Leichtmetallräder...... Ziffern-Schaltknauf”
但是,在Windows 2016中会引发上述异常,并且不会写入任何内容。
我进行了一些代码更改,我所做的第一件事是更改了OleDB的Odbc连接,并且消失了异常,但是输出中的文本不正确:
“ 17”-Leichtmetallräder...... Ziffern吗?沙尔特瑙夫”
请注意,使用odbc连接的相同代码如何无法理解Unicode破折号。
这是在Windows 2003中运行的OleDB代码:
OleDbConnection ConnOleDbIDD = new OleDbConnection("Provider=Ifxoledbc.2;Data Source=db;INFORMIXSERVER=localhost;IFMX_UNDOC_B168163=1;"); string sConnectTemplateDB = "Data Source=SQLServerDB;Initial Catalog=DB1; Connect Timeout = 28800; Integrated Security=True"; ConnOleDbIDD.Open(); sExportSQL = "SELECT * From MyTable"; OleDbCommand cmdIDD = new OleDbCommand(sExportSQL, ConnOleDbIDD); cmdIDD.CommandTimeout = 28800; SqlDataAdapter da; ConnSchemaIDD = new SqlConnection (sConnectTemplateDB); ConnSchemaIDD.Open(); SqlCommand cmdSQLServerTemplate = new SqlCommand(sExportSQL.Replace("TRIM","LTRIM"), ConnSchemaIDD); cmdSQLServerTemplate.CommandTimeout = 28800; da = new SqlDataAdapter(cmdSQLServerTemplate); OleDbDataReader dr; DataSet ds = new DataSet(); da.MissingSchemaAction = MissingSchemaAction.AddWithKey; da.Fill(ds, sSourceTable); DataTable dt = ds.Tables[sSourceTable]; dr = cmdIDD.ExecuteReader() iEnCodingFrom = 1252; iEnCodingTo = 1252; while (dr.Read()) { sValue = ""; sCurrentValue = ""; bDelimiterPosition = false; foreach (DataColumn cCol in dt.Columns) { object oval = dr.GetValue(dr.GetOrdinal(cCol.ColumnName)); string val = Convert.ToString(dr[cCol.ColumnName]); sCurrentValue = System.Text.Encoding.GetEncoding(iEnCodingTo).GetString(System.Text.Encoding.Convert(System.Text.Encoding.GetEncoding(iEnCodingFrom), System.Text.Encoding.GetEncoding(iEnCodingTo), System.Text.Encoding.GetEncoding(iEnCodingFrom).GetBytes(val))); if (bDelimiterPosition == true) { sValue = sValue + sDelimiter + sCurrentValue.Trim(); } else { sValue = sValue + sCurrentValue.Trim(); } bDelimiterPosition = true; } w.WriteLine(sValue); w.Flush(); } dr.Close();
在此示例中,假设“ Mytable”有2列,第一列是整数ID,第二列是char(3100)。
如您所见,代码做了一些奇怪的事情,例如从SQLServer数据库的表的架构中获取列说明,以及将db输出从CP1252转换为CP1252。我不确定为什么要这样编码。 对于这个问题,我的解决方法是对代码进行以下更改(使用odbc连接而不是oledb):
iEnCodingFrom = 28591; ... sCurrentValue = Encoding.GetEncoding(iEnCodingTo).GetString(Encoding.GetEncoding(iEnCodingFrom).GetBytes(val.ToCharArray())); ...
因此,将与ODBC连接的连接更改为notifyix DB,以防止引发异常,并从代码页28591(8859-1)转换为1252(CP1252),在Windows 2016中将产生与旧版本相同的结果Windows 2013中的代码。
所以我有一个解决方法,可以使用它,但是我想了解为什么会发生这种情况,为什么我不能继续使用OleDB,以及是否有办法使它在新的Windows环境中工作(也失败了)在Windows 10中),而无需更改代码。
任何帮助将不胜感激。
谢谢
答案 0 :(得分:0)
感谢@LuísMarques和@jsagrera这正是我想要的解释,所以现在我可以理解问题了。在文章中说:
“从CSDK 2.80版开始,ODBC驱动程序已启用Unicode,这意味着驱动程序处理的所有数据都必须为Unicode格式。这意味着必须进行额外的转换”。
旧服务器中csdk的版本为2.71。新服务器中的版本为4.10。
现在,由于这个原因,“ UNDOC”在那里,数据库是使用en_us.819创建的,但是我的客户端应用程序的“ undoc”变量忽略了该数据库,它假定数据来自CP1252并在其中打印CP1252,该程序无需任何内部转换即可工作。
但是数据库中的数据仍然被破坏。升级驱动程序后,进行内部转换会产生错误。
我仍然可以解决此问题,在ODBC连接中不使用“ UNDOC”,然后从DB中获取字节流,并在C#代码中从8859-1转换为CP1252。这样,我将获得与旧服务器完全相同的输出。
但是,这不是正确的解决方案,而是缓解问题的方法,最终的解决方案是将数据库更改为UTF8,以避免出现更多问题。这就是我们最终要做的。
谢谢@jsagrera,我想将您的答案标记为正确的答案。我是该平台的新手,所以我不太了解它的工作原理。如果您愿意发表评论作为答复,我很乐意将其标记为正确(如果可能)。