Проблема с кодировкой при переносе устаревшего инструмента на новую версию Windows с OleDB - PullRequest
1 голос
/ 30 сентября 2019

У нас возникла странная проблема после переноса старого приложения (> 15 лет), написанного на c #, на новый Windows Server. Приложение использует OleDB для подключения к базе данных, которая является базой данных Informix. В этой базе данных есть таблица с текстами на нескольких языках. Приложение, работающее на сервере Windows 2003, работает нормально, однако в новой Windows 2016 возникает ошибка: «Значение данных не может быть преобразовано по причинам, отличным от несоответствия знака или переполнения данных. Например, данные были повреждены вхранилище данных, но строка все еще была извлекаема. "

После некоторого исследования мы обнаружили, что проблема заключается в строке, содержащей несколько символов Юникода.

Это часть текстаэто порождает проблему (только часть текста, иллюстрирующая проблему:

"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? Schaltknauf "

Обратите внимание, что один и тот же код с подключением odbc не может понять черту Unicode.

Это код OleDB, который работает в Windows 2003:


    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 столбца, первый - это целочисленный идентификатор, а второй - символ (3100).

Как вы видите, код делает некоторые странные вещи, такие как получение описания столбца изсхема таблицы в базе данных SQLServer и преобразование выходных данных БД из CP1252 в CP1252. Я не уверен, почему это было закодировано таким образом. Мой обходной путь для этой проблемы делал эти изменения в коде (используя вместо этого соединение odbcof oledb):


    iEnCodingFrom = 28591;
    ...
       sCurrentValue = Encoding.GetEncoding(iEnCodingTo).GetString(Encoding.GetEncoding(iEnCodingFrom).GetBytes(val.ToCharArray()));
    ...

Таким образом, изменив соединение с соединением ODBC на DB Informix, которое предотвращает возникновение исключения, и выполнив преобразование из кодовой страницы 28591 (8859-1) в 1252 (CP1252)выдает в Windows 2016 тот же результат, что и старый код в Windows 2013.

Так что у меня есть обходной путь, и я могу его использовать, однако яЯ хотел бы понять, почему это происходит, почему я не могу продолжать использовать OleDB и если есть способ заставить его работать в новой среде Windows (не работает также в Windows 10) без необходимости изменения кода.

Любая помощь будет принята с благодарностью.

Спасибо

1 Ответ

0 голосов
/ 02 октября 2019

Спасибо @ LuísMarques и @jsagrera Это именно то объяснение, которое я искал, так что теперь я могу понять проблему. В статье говорится, что:

"Начиная с версии CSDK 2.80, драйвер ODBC поддерживает Unicode, это означает, что все данные, которые обрабатывает драйвер, должны быть в формате Unicode. Это означает, что дополнительное преобразование должнобыть сделано ".

Версия csdk на старом сервере - 2.71. Версия на новом сервере - 4.10.

Теперь «UNDOC» был там по этой причине, БД была создана с использованием en_us.819, но это было проигнорировано с переменной «undoc» для моего клиентского приложения,которая предполагает, что данные поступают в CP1252 и распечатывают их в CP1252, без какой-либо внутренней конвертации программа работала.

Но данные в БД все равно повреждены. После обновления драйвера это внутреннее преобразование выдает ошибку.

Я все еще могу обойти это, я не использую "UNDOC" в соединении ODBC, затем я получаю поток байтов из БД и делаю преобразование из 8859-1 в CP1252 в моем C #код. Таким образом, я получаю точно такой же вывод, как на старом сервере.

Однако это не правильное решение, а уменьшение проблемы, и окончательное решение будет состоять в том, чтобы изменить DB на UTF8, чтобы избежать дальнейших проблем. И вот что мы, наконец, сделаем.

Спасибо, @jsagrera. Я бы хотел отметить ваш ответ как правильный. Я новичок в платформе, поэтому я не знаю, как она работает. Если бы вы опубликовали свой комментарий в качестве ответа, я бы с радостью объявил его как правильный, если это возможно.

...