C # / ASP.NET Oledb - ошибка чтения MS Excel Unspecified

У нас есть приложение C # / ASP.NET (2.0), работающее на IIS 6 в Windows Server 2003 Enterprise Edition. Это приложение читает файлы Excel с помощью OleDb, но бывают случаи, когда мы получаем исключение «Неопределенная ошибка», возникающее изнутри приложения.

Файл сохраняется во временном каталоге нашим кодом загрузки файла перед открытием. Поскольку у нас включен анонимный доступ в IIS и поскольку мы также используем олицетворение в web.config, папка C: \ Windows \ Temp \ имеет соответствующие разрешения для гостевой учетной записи пользователя Интернета (IUSR_ [MachineName]), чтобы иметь возможность создавать, изменять и удалять файлы там.

Строка подключения OleDb:
Provider = Microsoft.Jet.OLEDB.4.0; Источник данных = C: \ Windows \ Temp \ tmp123.tmp.xls;
Extended Properties = "Excel 8.0; HDR = Да; IMEX = 1; "

[Атрибут "Источник данных" выше будет изменяться для каждого файла.]

The stack trace of the exception is:
    System.Exception: FileParsingFailed ---> System.Data.OleDb.OleDbException:  
    Unspecified error at  
    System.Data.OleDb.OleDbConnectionInternal..ctor(OleDbConnectionString constr,  
    OleDbConnection connection) at  
    System.Data.OleDb.OleDbConnectionFactory.CreateConnection(DbConnectionOptions options,   
    Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningObject) at  
    System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection  
    owningConnection, DbConnectionPoolGroup poolGroup) at  
    System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection  
    owningConnection) at  
    System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection  
    outerConnection, DbConnectionFactory connectionFactory) at  
    System.Data.OleDb.OleDbConnection.Open()  

Обходной путь:
Пока что единственный обходной путь, который мы могли придумать, - это выполнить iisreset (у нас также настроена перезапись пула приложений, которая выполняется один раз в день в IIS, но, похоже, это не так помощь, поскольку проблема иногда сохраняется в течение нескольких дней подряд). Хотя это не очень хорошо, хуже всего то, что у нас есть другие приложения на том же веб-сайте, на которые будет влиять каждый раз, когда мы перезагружаем IIS.

Вопросы:
1. Как мы можем устранить эту ошибку, если она случается время от времени, и мы не видим закономерности?
2. Есть ли лучшие (и бесплатные) способы обработки Файлы Excel из C # / ASP.NET помимо OleDb? (Мы предпочитаем не устанавливать MS Office на серверах, так как это не рекомендуется Microsoft)

Наши ограничения:
1. Мы застряли в формате MS Office 2003 (.xls) и не можем перейти в формат MS Office 2007 (OOXML).
2. Причины, по которым мы не использовать CSV, потому что у нас могут быть запятые в наших данных (с этим трудно справиться, даже если мы используем цитирование), и мы также используем несколько рабочих листов в нашей электронной таблице (это невозможно сделать с CSV).

Спасибо! :)

Обновление:
Спасибо, Кейт. Это действительно похоже на проблему с движком Jet, но мы используем его из-за отсутствия (бесплатных и простых в использовании) альтернатив.
Спасибо, Джо. Но у нас ограниченный бюджет - поэтому мы в основном ищем бесплатные инструменты / библиотеки.


person Community    schedule 23.01.2009    source источник


Ответы (7)


Убедитесь, что вы закрываете свои связи.

Например, при разработке приложений MS Access (Jet) эта ошибка возникает, если слишком много соединений остается открытыми. Он работает нормально (иногда бывает) какое-то время, пока не достигнет максимального количества открытых соединений.

person CRice    schedule 29.01.2009

Я подозреваю, что ошибка связана с почтенным движком Jet OLEDB. Он довольно скрипучий - подходит для большинства настольных компьютеров, но не особо полезен для обмена корпоративными данными.

Если вы можете выполнить обновление до последней версии C # 3 / .Net 3.5, вы можете использовать библиотеку System.IO.Packaging для открытия файлов Office 2007 (файлов .xlsx или .xlsm).

Эти файлы на самом деле представляют собой zip-архивы - переименуйте их в .zip, и вы сможете просто просматривать файлы XML внутри.

Форматы файлов XML довольно ужасны (например, комментарии к ячейкам - это VML, тьфу!), Но читабельны.

В качестве альтернативы попросите пользователей сохранить таблицы Excel в виде CSV-файлов. Я бы избегал поставщика БД текстовых драйверов Microsoft - это мусор и не может обрабатывать Unicode. В любом случае CSV легко читать.

person Keith    schedule 23.01.2009

Некоторое время я использую SpreadSheetGear.NET, в основном для создания файлов Excel, и он хорошо работает.

http://www.spreadsheetgear.com/products/spreadsheetgear.net.aspx

Он обеспечивает чтение / запись двоичных файлов Excel в собственном .NET, решая все предыдущие проблемы, с которыми я столкнулся, пытаясь использовать OLE и JET для чтения и создания файлов Excel.

Базовая версия раньше предоставлялась бесплатно в качестве привилегии для регистрации Visual C ++ Express 2005. Об этом не сообщалось, поэтому она может существовать, а может и не существовать с версией 2008 года.

person David    schedule 19.02.2009

SpreadsheetGear для .NET предоставляет API для работы с книгами xls и xlsx из .СЕТЬ. Это проще в использовании и быстрее, чем OleDB или объектная модель COM Excel (продолжайте читать, чтобы увидеть доказательства этого).

Отказ от ответственности: я владею SpreadsheetGear LLC

Ниже приведен код для создания книги размером 50 000 строк на 10 столбцов с помощью SpreadsheetGear, сохранения ее на диск, а затем суммирования чисел с помощью OleDb и SpreadsheetGear. SpreadsheetGear считывает 500 тыс. Ячеек за 0,31 секунды по сравнению с 0,63 секунды с OleDB - чуть более чем в два раза быстрее. SpreadsheetGear фактически создает и читает книгу за меньшее время, чем это требуется для чтения книги с помощью OleDB.

Код ниже. Вы можете увидеть живые образцы или попробовать сами с помощью бесплатная пробная версия.

using System;
using System.Data; 
using System.Data.OleDb; 
using SpreadsheetGear;
using SpreadsheetGear.Advanced.Cells;
using System.Diagnostics;

namespace SpreadsheetGearAndOleDBBenchmark
{
    class Program
    {
        static void Main(string[] args)
        {
            // Warm up (get the code JITed).
            BM(10, 10);

            // Do it for real.
            BM(50000, 10);
        }

        static void BM(int rows, int cols)
        {
            // Compare the performance of OleDB to SpreadsheetGear for reading
            // workbooks. We sum numbers just to have something to do.
            //
            // Run on Windows Vista 32 bit, Visual Studio 2008, Release Build,
            // Run Without Debugger:
            //  Create time: 0.25 seconds
            //  OleDb Time: 0.63 seconds
            //  SpreadsheetGear Time: 0.31 seconds
            //
            // SpreadsheetGear is more than twice as fast at reading. Furthermore,
            // SpreadsheetGear can create the file and read it faster than OleDB
            // can just read it.
            string filename = @"C:\tmp\SpreadsheetGearOleDbBenchmark.xls";
            Console.WriteLine("\nCreating {0} rows x {1} columns", rows, cols);
            Stopwatch timer = Stopwatch.StartNew();
            double createSum = CreateWorkbook(filename, rows, cols);
            double createTime = timer.Elapsed.TotalSeconds;
            Console.WriteLine("Create sum of {0} took {1} seconds.", createSum, createTime);
            timer = Stopwatch.StartNew();
            double oleDbSum = ReadWithOleDB(filename);
            double oleDbTime = timer.Elapsed.TotalSeconds;
            Console.WriteLine("OleDb sum of {0} took {1} seconds.", oleDbSum, oleDbTime);
            timer = Stopwatch.StartNew();
            double spreadsheetGearSum = ReadWithSpreadsheetGear(filename);
            double spreadsheetGearTime = timer.Elapsed.TotalSeconds;
            Console.WriteLine("SpreadsheetGear sum of {0} took {1} seconds.", spreadsheetGearSum, spreadsheetGearTime);
        }

        static double CreateWorkbook(string filename, int rows, int cols)
        {
            IWorkbook workbook = Factory.GetWorkbook();
            IWorksheet worksheet = workbook.Worksheets[0];
            IValues values = (IValues)worksheet;
            double sum = 0.0;
            Random rand = new Random();
            // Put labels in the first row.
            foreach (IRange cell in worksheet.Cells[0, 0, 0, cols - 1])
                cell.Value = "Cell-" + cell.Address;
            // Using IRange and foreach would be less code, 
            // but we'll do it the fast way.
            for (int row = 1; row <= rows; row++)
            {
                for (int col = 0; col < cols; col++)
                {
                    double number = rand.NextDouble();
                    sum += number;
                    values.SetNumber(row, col, number);
                }
            }
            workbook.SaveAs(filename, FileFormat.Excel8);
            return sum;
        }

        static double ReadWithSpreadsheetGear(string filename)
        {
            IWorkbook workbook = Factory.GetWorkbook(filename);
            IWorksheet worksheet = workbook.Worksheets[0];
            IValues values = (IValues)worksheet;
            IRange usedRahge = worksheet.UsedRange;
            int rowCount = usedRahge.RowCount;
            int colCount = usedRahge.ColumnCount;
            double sum = 0.0;
            // We could use foreach (IRange cell in usedRange) for cleaner 
            // code, but this is faster.
            for (int row = 1; row <= rowCount; row++)
            {
                for (int col = 0; col < colCount; col++)
                {
                    IValue value = values[row, col];
                    if (value != null && value.Type == SpreadsheetGear.Advanced.Cells.ValueType.Number)
                        sum += value.Number;
                }
            }
            return sum;
        }

        static double ReadWithOleDB(string filename)
        {
            String connectionString =  
                "Provider=Microsoft.Jet.OLEDB.4.0;" + 
                "Data Source=" + filename + ";" + 
                "Extended Properties=Excel 8.0;"; 
            OleDbConnection connection = new OleDbConnection(connectionString); 
            connection.Open(); 
            OleDbCommand selectCommand =new OleDbCommand("SELECT * FROM [Sheet1$]", connection); 
            OleDbDataAdapter dataAdapter = new OleDbDataAdapter(); 
            dataAdapter.SelectCommand = selectCommand; 
            DataSet dataSet = new DataSet(); 
            dataAdapter.Fill(dataSet); 
            connection.Close(); 
            double sum = 0.0;
            // We'll make some assumptions for brevity of the code.
            DataTable dataTable = dataSet.Tables[0];
            int cols = dataTable.Columns.Count;
            foreach (DataRow row in dataTable.Rows)
            {
                for (int i = 0; i < cols; i++)
                {
                    object val = row[i];
                    if (val is double)
                        sum += (double)val;
                }
            }
            return sum;
        }
    }
}
person Joe Erickson    schedule 23.02.2009

У меня была такая же проблема, и кажется, что она исправляется закрытием соединения с файлом (xls или csv) на каждой итерации цикла. Я предполагаю, что вы также просматриваете список файлов и .Open () новое соединение с каждым файлом. Если вы .Close () соединение в конце цикла, проблема, кажется, исчезнет.

person mt99    schedule 02.12.2009

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

person LearnEarn    schedule 08.03.2011