5

我们有一个 C#/ASP.NET (2.0) 应用程序在 Windows Server 2003 企业版的 IIS 6 上运行。此应用程序使用 OleDb 读取 Excel 文件,但在某些情况下,我们会从应用程序中收到“未指定错误”异常。

该文件在打开之前由我们的文件上传代码存储在临时目录中。由于我们在 IIS 中启用了匿名访问,并且我们还在 web.config 中使用了模拟,因此文件夹 C:\Windows\Temp\ 具有 Internet 访客用户帐户 (IUSR_[MachineName]) 能够创建的适当权限,在那里修改和删除文件。

OleDb 连接字符串:
Provider=Microsoft.Jet.OLEDB.4.0;数据源=C:\Windows\Temp\tmp123.tmp.xls;
扩展属性="Excel 8.0;HDR=Yes;IMEX=1;"

[上面的“数据源”属性会因每个文件而改变。]

异常的堆栈跟踪是:
    System.Exception:FileParsingFailed ---> System.Data.OleDb.OleDbException:  
    未指定的错误  
    System.Data.OleDb.OleDbConnectionInternal..ctor(OleDbConnectionString 常量,  
    OleDbConnection 连接)在  
    System.Data.OleDb.OleDbConnectionFactory.CreateConnection(DbConnectionOptions 选项,   
    对象 poolGroupProviderInfo、DbConnectionPool 池、DbConnection owningObject) 在  
    System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection  
    owningConnection, DbConnectionPoolGroup poolGroup) 在  
    System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection  
    拥有连接)在  
    System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection  
    外部连接,DbConnectionFactory 连接工厂)在  
    System.Data.OleDb.OleDbConnection.Open()  

解决方法:
到目前为止,我们能想出的唯一解决方法是执行 iisreset(我们还在 IIS 中将应用程序池回收配置为每天发生一次,但这似乎无济于事,因为该问题有时会持续数天)。虽然这不是一件好事,但更糟糕的是,我们在同一个网站上还有其他应用程序,每当我们重置 IIS 时这些应用程序都会受到影响。

问题:
1. 我们如何解决这个错误,因为它偶尔会发生并且我们看不到模式?
2. 除了 OleDb 之外,还有没有更好的(免费的)处理 C#/ASP.NET 的 Excel 文件的方法?(我们不喜欢在服务器上安装 MS Office,因为微软不推荐它)

我们的局限:
1. 我们被 MS Office 2003 (.xls) 格式困住,无法迁移到 MS Office 2007 (OOXML) 格式。
2. 我们不使用 CSV 的原因是因为我们的数据中可能有逗号(即使我们使用引号也很难处理)并且我们还在电子表格中使用多个工作表(这不能用 CSV 完成) .

谢谢!:)

更新:
谢谢,基思。这似乎是 Jet 引擎的一个问题,但我们使用它是因为缺乏(免费且易于使用的)替代品。
谢谢,乔。但是我们的预算有限——所以我们主要在寻找免费的工具/库。

4

7 回答 7

3

确保您正在关闭您的连接。

例如,在开发 MS Access 应用程序 (Jet) 时,如果有太多连接处于打开状态,则会出现此错误。它可以正常工作(偶尔会有)一段时间,直到达到最大打开连接数。

于 2009-01-29T00:14:27.313 回答
2

我怀疑这个错误与古老的 Jet OLEDB 引擎有关。它相当糟糕 - 对于大多数桌面设备来说都很好,但对于企业数据交换来说没有多大用处。

如果您可以升级到最新的 C# 3/.Net 3.5,您可以使用该System.IO.Packaging库打开 Office 2007 文件(.xlsx 或 .xlsm 文件)。

这些文件实际上是 zip - 将它们重命名为 .zip,您就可以查看其中的 XML 文件。

XML 文件的格式相当糟糕(例如单元格注释是 VML,啊!)但可读。

或者让您的用户将 Excel 表格保存为 CSV。不过,我会避免使用 Microsoft 文本驱动程序 DB 提供程序 - 它很垃圾并且无法处理 unicode。无论如何,CSV 都很容易阅读。

于 2009-01-23T11:32:53.493 回答
2

我使用 SpreadSheetGear.NET 已经有一段时间了,主要用于创建 Excel 文件,而且效果很好。

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

它在本机 .NET 中提供二进制 Excel 文件读取/写入,解决了我之前在尝试使用 OLE 和 JET 读取和创建 Excel 文件时遇到的所有问题。

基本版本曾经作为注册 Visual C++ Express 2005 的一项福利免费提供。这是未经宣传的,因此它可能会或可能不会在 2008 年版中存在。

于 2009-02-19T19:10:28.017 回答
1

SpreadsheetGear for .NET为您提供了使用 .NET 中的 xls 和 xlsx 工作簿的 API。它比 OleDB 或 Excel COM 对象模型更容易使用和更快(继续阅读以获取一些证据)。

免责声明:我拥有 SpreadsheetGear LLC

下面是使用 SpreadsheetGear 创建 50,000 行 x 10 列工作簿的代码,将其保存到磁盘,然后使用 OleDb 和 SpreadsheetGear 对数字求和。SpreadsheetGear 在 0.31 秒内读取 500K 单元格,而使用 OleDB 则为 0.63 秒 - 快了两倍多。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;
        }
    }
}
于 2009-02-23T19:28:37.477 回答
1

我遇到了同样的问题,似乎可以通过在循环的每次迭代中关闭与文件(xls 或 csv)的连接来解决。我假设您还在循环文件列表和 .Open() 到每个文件的新连接。如果在循环结束时 .Close() 连接,问题似乎就消失了。

于 2009-12-02T11:04:16.893 回答
1

好像我弄错了,请参阅OleDbConnection、Excel 和连接池的问题

Dispose()基本上是CRice所说的,但是在使用Excel-ConnectionString调用构造函数时的实现似乎存在问题OleDbDataAdapter (String, String),因为隐式创建的连接显然没有关闭。

解决方法是使用单独 的包装所有调用OleDbDataApater用法(您正在执行 using ... 的东西,因为它实现了)IDisposable

using (var conn = new OleDbConnection(connectionString))

然后调用OleDbDataAdapter (String, OleDbConnection)构造函数。

编辑: 我在处置时关闭连接是错误的。conn.Dispose()不会关闭连接,所以在里面using (var conn = new OleDbConnection(connectionString))你仍然需要做一个conn.Close().

于 2011-04-11T13:15:34.777 回答
1

连接超时可能是原因之一。通过调试检查查询在应用程序中执行需要多长时间。

于 2011-03-08T10:56:39.170 回答