使用OleDB将旧版工具迁移到新的Windows版本时出现编码问题

时间:2019-09-30 11:00:27

标签: unicode encoding oledb informix

将用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中),而无需更改代码。

任何帮助将不胜感激。

谢谢

1 个答案:

答案 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,我想将您的答案标记为正确的答案。我是该平台的新手,所以我不太了解它的工作原理。如果您愿意发表评论作为答复,我很乐意将其标记为正确(如果可能)。