5

Windows Server 2003 Enterprise Edition の IIS 6 で実行されている C#/ASP.NET (2.0) アプリケーションがあります。このアプリケーションは OleDb を使用して Excel ファイルを読み取りますが、アプリケーション内からスローされる「特定できないエラー」例外が発生する場合があります。

ファイルは、開く前にファイル アップロード コードによって一時ディレクトリに保存されます。IIS で匿名アクセスを有効にし、web.config で偽装も使用しているため、フォルダー C:\Windows\Temp\ には、インターネット ゲスト ユーザー アカウント (IUSR_[MachineName]) を作成できる適切なアクセス許可があります。そこでファイルを変更および削除します。

OleDb 接続文字列:
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Windows\Temp\tmp123.tmp.xls;
拡張プロパティ="Excel 8.0;HDR=はい;IMEX=1;"

[上記の「データ ソース」属性は、ファイルごとに変更されます。]

例外のスタック トレースは次のとおりです。
    System.Exception: FileParsingFailed ---> System.Data.OleDb.OleDbException:  
    での不明なエラー  
    System.Data.OleDb.OleDbConnectionInternal..ctor(OleDbConnectionString constr,  
    OleDbConnection 接続) で  
    System.Data.OleDb.OleDbConnectionFactory.CreateConnection(DbConnectionOptions オプション,   
    オブジェクト プールGroupProviderInfo、DbConnectionPool プール、DbConnection owningObject) で  
    System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection)  
    owningConnection、DbConnectionPoolGroup プール グループ) で  
    System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection)  
    owningConnection) で  
    System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection  
    outerConnection、DbConnectionFactory connectionFactory) で  
    System.Data.OleDb.OleDbConnection.Open()  

回避策:
これまでのところ、私たちが思いついた唯一の回避策は、iisreset を実行することでした (IIS でアプリケーション プールのリサイクルを 1 日 1 回実行するように構成しましたが、問題が数日連続して持続する場合があるため、役に立たないようです。 )。これは良いことではありませんが、さらに悪いことに、IIS をリセットするたびに影響を受ける同じ Web サイトに他のアプリケーションがあることです。

質問:
1. このエラーは時折発生し、パターンが見られないため、このエラーをどのように解決しますか?
2. OleDb 以外に、C#/ASP.NET から Excel ファイルを処理するためのより良い (そして無料の) 方法はありますか? (Microsoft では推奨されていないため、サーバーに MS Office をインストールしないことをお勧めします)

私たちの制限:
1. MS Office 2003 (.xls) 形式に固執しており、MS Office 2007 (OOXML) 形式に移行できません。
2. CSV を使用しない理由は、データ内にカンマが含まれる可能性があるため (引用を使用しても対処するのが面倒です)、スプレッドシートで複数のワークシートを使用するためです (これは CSV では実行できません)。 .

ありがとう!:)

更新:
ありがとう、キース。Jet エンジンの問題のように思えますが、(無料で使いやすい) 代替手段がないため、Jet エンジンを使用しています。
ありがとう、ジョー。しかし、予算が限られているため、主に無料のツール/ライブラリを探しています。

4

7 に答える 7

2

私はしばらくの間、主に Excel ファイルを作成するために SpreadSheetGear.NET を使用してきましたが、うまく機能しています。

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

ネイティブ .NET でのバイナリ Excel ファイルの読み取り/書き込みを提供し、OLE と JET を使用して Excel ファイルの読み取りと作成を試みたときに遭遇した以前の問題をすべて解決します。

基本バージョンは、Visual C++ Express 2005 を登録するための特典として無料で提供されていました。

于 2009-02-19T19:10:28.017 に答える
1

接続タイムアウトが理由の 1 つかもしれません。デバッグにより、アプリケーションでの実行にかかるクエリを確認します。

于 2011-03-08T10:56:39.170 に答える
1

私は同じ問題を抱えていましたが、ループの繰り返しごとにファイル (xls または csv) への接続を閉じることで修正されたようです。ファイルのリストと .Open() 各ファイルへの新しい接続もループしていると思います。ループの最後で接続を .Close() すると、問題は解決したようです。

于 2009-12-02T11:04:16.893 に答える
1

OleDbConnection、Excel、および接続プーリングの問題を参照してください。

基本的に CRice が言ったことですが、暗黙的に作成された接続が明らかに閉じられていないため、コンストラクターが Excel-ConnectionString で呼び出されたDispose()ときの実装に問題があるようです。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

SpreadsheetGear for .NETは、.NET から xls および xlsx ワークブックを操作するための API を提供します。OleDB や Excel COM オブジェクト モデルよりも使いやすく、高速です (これについては、このまま読み進めてください)。

免責事項: 私は SpreadsheetGear LLC を所有しています

以下は、SpreadsheetGear を使用して 50,000 行 x 10 列のワークブックを作成し、ディスクに保存してから、OleDb と SpreadsheetGear を使用して数値を合計するコードです。SpreadsheetGear は、OleDB の 0.63 秒と比較して 0.31 秒で 500K セルを読み取ります - 2 倍以上の速さです。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 に答える