ASP .NET Excel Interop を実行するとメモリ不足エラーが発生するのはなぜですか?
-
12-09-2019 - |
質問
これ だった 動作しています。そして、廃棄コードをfinallyブロックに移動しましたが、毎回失敗するようになりました。
4 つのレコード、6 列の長さを持つテスト スプレッドシートがあります。これを取り込むために使用しているコードは次のとおりです。これは、IIS 5 (私の PC) と IIS 6 (Web サーバー) 上の ASP .Net 3.5 です。
キャッチの直前にラインで爆発します。"values =(object [、])range.value2;"次のエラーで:
11/2/2009 8:47:43 AM :: Not enough storage is available to complete this operation. (Exception from HRESULT: 0x8007000E (E_OUTOFMEMORY))
何か案は?提案はありますか?このコードの大部分は codeproject から取得したものなので、これが Excel で作業する正しい方法であるかどうかはわかりません。ご協力いただきありがとうございます。
これが私のコードです:
Excel.ApplicationClass app = null; Excel.Workbook book = null; Excel.Worksheet sheet = null; Excel.Range range = null; object[,] values = null; try { // Configure Excel app = new Excel.ApplicationClass(); app.Visible = false; app.ScreenUpdating = false; app.DisplayAlerts = false; // Open a new instance of excel with the uploaded file book = app.Workbooks.Open(path); // Get first worksheet in book sheet = (Excel.Worksheet)book.Worksheets[1]; // Start with first cell on second row range = sheet.get_Range("A2", Missing.Value); // Get all cells to the right range = range.get_End(Excel.XlDirection.xlToRight); // Get all cells downwards range = range.get_End(Excel.XlDirection.xlDown); // Get address of bottom rightmost cell string downAddress = range.get_Address(false, false, Excel.XlReferenceStyle.xlA1, Type.Missing, Type.Missing); // Get complete range of data range = sheet.get_Range("A2", downAddress); // get 2d array of all data values = (object[,])range.Value2; } catch (Exception e) { LoggingService.log(e.Message); } finally { // Clean up range = null; sheet = null; if (book != null) book.Close(false, Missing.Value, Missing.Value); book = null; if (app != null) app.Quit(); app = null; } return values;
解決
これはあなたの問題であるかどうか、それは非常によくなる可能性がある場合、私はわかりません。あなたは適切にあなたのExcelオブジェクトをクリーンアップされていません。彼らは、アンマネージコードであり、クリーンアップするのが難しいことができます。最後に、このようになります。そして、コメントはasp.netからエクセルで作業を指摘しているように良いアイデアではありません。このクリーンアップコードは、Winフォームアプリケーションからです。
GC.Collect();
GC.WaitForPendingFinalizers();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(range);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(sheet);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(book);
WB.Close(false, Type.Missing, Type.Missing);
Excel.Quit();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(Excel);
編集
代替ブックを開くためにado.netを使用することです。
DataTable dt = new DataTable();
string connectionString;
System.Data.OleDb.OleDbConnection excelConnection;
System.Data.OleDb.OleDbDataAdapter da;
DataTable dbSchema;
string firstSheetName;
string strSQL;
connectionString = @"provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filename + @";Extended Properties=""Excel 12.0;HDR=YES;IMEX=1""";
excelConnection = new System.Data.OleDb.OleDbConnection(connectionString);
excelConnection.Open();
dbSchema = excelConnection.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables, null);
firstSheetName = dbSchema.Rows[0]["TABLE_NAME"].ToString();
strSQL = "SELECT * FROM [" + firstSheetName + "]";
da = new OleDbDataAdapter(strSQL, excelConnection);
da.Fill(dt);
da.Dispose();
excelConnection.Close();
excelConnection.Dispose();
他のヒント
リクエストごとにExcelを破壊する/作成すると、何をするかは問題で絶対にひどいパフォーマンスを持っています。一般的には、オートメーションを使用して任意のOfficeアプリケーションを実行するには厄介な事業は、(ここを参照してください多くの理由のためでありますを)。私はそれが動作するようになった唯一の方法は、一度初期化された(私の場合Wordの)アプリの単一のインスタンスを持つことです、その後、要求が処理のために、このインスタンスにキューイングされます。
あなたが離れたアプリから滞在し、ファイル(XMLだけのMSライブラリを使用して、)自分で解析することができた場合は、
ASP.NET から相互運用機能を使用すると、多くの問題が発生することになります。これが小規模な社内アプリケーションを対象としたものでない限り、これを進めないことをお勧めします。
Office Interop は、従来の意味でのプログラミング API ではありません。Office マクロ システムを最大限に活用し、プロセス間で動作する機能を備えています。たとえば、Excel マクロは Outlook と対話できます。
相互運用性を使用すると、次のような影響が生じます。
- 実際には、Office アプリケーションの完全なコピーを開いていることになります。
- アクションは、ユーザーがアクションを開始したかのようにアプリケーションによって実行されます。つまり、エラー メッセージがコードで返されるのではなく、GUI に表示されます。
- アプリケーションのコピーは、明示的にコマンドを実行した場合にのみ閉じられますが、その場合でも、エラーにより実際には閉じられなくなる可能性があります (変更を必要としないことを Excel にプログラム的に指示しなかった場合、アプリケーションは「保存しますか?」ダイアログを表示することがあります)保存されます)。これにより、通常、Excel の隠しコピーがシステム上で多数実行されたままになります。タスク マネージャーを開いて、実行されている Excel.exe プロセスの数を確認してください。
これらすべてにより、相互運用は通常のデスクトップ アプリケーションでは避けるべきものとなり、サーバー アプリケーションでは最後の手段としてのみ使用すべきものになります。アクションを必要とする GUI ポップアップや、プロセスをリークするスクリプトはサーバー環境では殺人行為だからです。
いくつかの代替案は次のとおりです。
- Microsoft Office 2007 XML ベースの形式を使用し、XML ファイルを自分で作成できるようにします。
- 使用する スプレッドシートギア.ネット, .NET バイナリ Excel ファイル リーダー/ライターです (完全にスタンドアロンであるため、Excel をインストールする必要はありません)。SpreadsheetGear は、古いコードの変換を容易にするために、Intertop インターフェイスに従って自らをモデル化します。
エラーはおそらくそのメッセージのとおりで、メモリ不足エラーが発生しています。Range 全体を一度に取得するのではなく、値の配列の読み込みをいくつかの小さなチャンクに分割してみてください。コードを C# で試してみましたが、問題はありませんでしたが、読み込んでいたスプレッドシートはほとんど空でした。
ただし、範囲がスプレッドシート全体 (A2 から IV65536 など) であることに気付きました。それが意図されているかどうかはわかりません。
使用してみるとよいのは、sheet. UsedRange です。これは、読み込むセルの数を削減します。
私が学んだ、役に立つかもしれない追加の小さなことをいくつか紹介します。
- ApplicationClass の代わりに Application を使用する
- Marshal.FinalReleaseComObject(range) (シート、ブック、アプリにも) を使用してください。そうしないと、EXCEL.EXE プロセスが残ります。