2009-01-23 37 views
5

Chúng tôi có ứng dụng C#/ASP.NET (2.0) chạy trên IIS 6 trên Windows Server 2003 Enterprise Edition. Ứng dụng này đọc các tệp Excel bằng OleDb, nhưng có trường hợp khi chúng tôi nhận được một ngoại lệ "Lỗi không xác định" được ném từ bên trong ứng dụng.C#/ASP.NET Oledb - MS Excel đọc "Lỗi không xác định"

Tệp được lưu trữ trong thư mục tạm thời bằng mã tải lên tệp của chúng tôi trước khi mở. Vì chúng tôi đã vô hiệu hóa quyền truy cập vô danh trong IIS và vì chúng tôi cũng sử dụng mạo danh trong web.config, thư mục C: \ Windows \ Temp \ có quyền thích hợp cho Tài khoản Người dùng Khách Internet (IUSR_ [MachineName]) để có thể tạo, sửa đổi và xóa các tập tin ở đó.

chuỗi kết nối OleDb:
Provider = Microsoft.Jet.OLEDB.4.0; Data Source = C: \ Windows \ Temp \ tmp123.tmp.xls;
Thuộc tính mở rộng = "Excel 8.0; HDR = Có; IMEX = 1;"

[Các "Data Source" thuộc tính trên sẽ thay đổi cho mỗi tập tin.]

 
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() 

Cách giải quyết:
Cho đến nay, thực hiện giải pháp duy nhất chúng ta có thể đưa ra là để làm một iisreset (chúng tôi cũng có ứng dụng tái chế hồ bơi được cấu hình để xảy ra một lần mỗi ngày trong IIS, nhưng nó dường như không giúp đỡ vì vấn đề đôi khi vẫn tồn tại trong vài ngày liên tiếp). Mặc dù đây không phải là điều tốt để làm, điều khiến chúng ta trở nên tồi tệ hơn là chúng ta có các ứng dụng khác trên cùng một trang web sẽ bị ảnh hưởng bất cứ khi nào chúng ta thiết lập lại IIS.

Những câu hỏi:
1. Làm thế nào để chúng ta giải quyết lỗi này vì nó xảy ra thường xuyên và chúng tôi không thấy một mô hình?
2. Có cách nào tốt hơn (và miễn phí) để xử lý các tệp Excel từ C#/ASP.NET ngoài OleDb? (Chúng tôi không muốn cài đặt MS Office trên các máy chủ vì nó không được khuyến khích bởi Microsoft)

hạn chế của chúng tôi:
1. Chúng tôi đang mắc kẹt với các định dạng (.xls) MS Office 2003 và không thể di chuyển đến Định dạng MS Office 2007 (OOXML). 2. Lý do chúng tôi không sử dụng CSV là vì chúng tôi có thể có dấu phẩy trong dữ liệu của chúng tôi (đây là một nỗi đau để giải quyết ngay cả khi chúng tôi sử dụng trích dẫn) và chúng tôi cũng sử dụng nhiều trang tính trong bảng tính của mình CSV).

Cảm ơn! :)

Cập nhật:
Cảm ơn, Keith. Nó có vẻ giống như một vấn đề với động cơ phản lực, nhưng chúng tôi sử dụng nó vì thiếu (miễn phí và dễ sử dụng) lựa chọn thay thế.
Cảm ơn, Joe. Nhưng chúng tôi đang có ngân sách hạn chế - vì vậy, chúng tôi chủ yếu tìm kiếm các công cụ/thư viện miễn phí.

Trả lời

2

Tôi nghi ngờ lỗi này liên quan đến động cơ phản lực OLEDB đáng kính. Nó khá vất vả - tốt cho hầu hết mọi thứ trên máy tính để bàn nhưng không sử dụng nhiều cho trao đổi dữ liệu doanh nghiệp.

Nếu bạn có thể nâng cấp lên C# 3/.Net 3.5 gần đây, bạn có thể sử dụng thư viện System.IO.Packaging để mở tệp Office 2007 (tệp .xlsx hoặc .xlsm).

Những tệp này thực sự là zips - đổi tên chúng thành tệp .zip và bạn chỉ có thể xem các tệp XML bên trong.

Định dạng tệp XML khá khủng khiếp (ví dụ: nhận xét ô là VML, ugh!) Nhưng có thể đọc được.

Hoặc để người dùng lưu bảng Excel dưới dạng CSV. Tôi muốn tránh các nhà cung cấp trình điều khiển văn bản Microsoft DB mặc dù - đó là rác và không thể xử lý unicode. CSV dễ đọc.

3

Đảm bảo bạn đang đóng kết nối.

Ví dụ: khi phát triển các ứng dụng MS Access (Jet), lỗi này xảy ra nếu có quá nhiều kết nối được mở. Nó hoạt động tốt (có đôi khi của bạn) trong một thời gian cho đến khi nó đạt đến các kết nối mở tối đa.

2

Tôi đã sử dụng SpreadSheetGear.NET trong một thời gian, chủ yếu để tạo tệp Excel và hoạt động tốt.

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

Nó cung cấp nhị phân Excel tập tin đọc/ghi trong .NET bản xứ, giải quyết tất cả các vấn đề trước đó tôi đã gặp phải cố gắng sử dụng OLE và JET để đọc và tạo các file Excel.

Phiên bản cơ bản được sử dụng miễn phí như đăng ký cho Visual C++ Express 2005. Điều này không được sửa đổi, vì vậy có thể có hoặc không tồn tại với ấn bản năm 2008.

1

SpreadsheetGear for .NET cung cấp cho bạn API để làm việc với sổ làm việc xls và xlsx từ .NET. Nó dễ sử dụng và nhanh hơn OleDB hoặc mô hình đối tượng COM của Excel (tiếp tục đọc cho một số bằng chứng về điều này).

Disclaimer: Tôi sở hữu SpreadsheetGear LLC

Dưới đây là đoạn code để tạo ra một hàng 50.000 bởi 10 cột bảng tính với SpreadsheetGear, lưu nó vào đĩa, và sau đó tổng hợp các con số sử dụng OleDb và SpreadsheetGear. SpreadsheetGear đọc các tế bào 500K trong 0,31 giây so với 0,63 giây với OleDB - nhanh hơn gấp hai lần. SpreadsheetGear thực sự tạo và đọc sổ làm việc trong thời gian ít hơn cần để đọc sổ làm việc với OleDB.

Mã bên dưới. Bạn có thể xem số live samples hoặc dùng thử cho mình với free trial.

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; 
     } 
    } 
} 
1

Tôi gặp vấn đề tương tự và có vẻ như nó đã được sửa bằng cách đóng kết nối với tệp (xls hoặc csv) trên mỗi lần lặp của vòng lặp. Tôi giả sử bạn cũng đang lặp qua danh sách các tệp và .Open() một kết nối mới cho mỗi tệp. Nếu bạn .Đóng() kết nối ở cuối vòng lặp, vấn đề dường như biến mất.

1

Thời gian kết nốiĐó có thể là một trong những lý do. Kiểm tra truy vấn mất bao lâu để thực hiện trong ứng dụng bằng cách gỡ lỗi.

1

Có vẻ như tôi đã nhận nó sai, xem Problem with OleDbConnection, Excel and connection pooling

Về cơ bản những gì CRice nói, nhưng có vẻ là một vấn đề trong việc thực hiện Dispose() khi OleDbDataAdapter (String, String) constructor được gọi với Excel-ConnectionString, như implicitely tạo kết nối dường như không bị đóng.

Cách giải quyết là gói tất cả các cuộc gọi OleDbDataApater tập quán (bạn đang sử dụng ...thứ vì nó thực hiện IDisposable) với một riêng biệt

using (var conn = new OleDbConnection(connectionString)) 

và sau đó gọi các nhà xây dựng OleDbDataAdapter (String, OleDbConnection).

CHỈNH SỬA: Tôi đã sai về việc ngắt kết nối khi xử lý. conn.Dispose() không đóng kết nối, vì vậy bên trong using (var conn = new OleDbConnection(connectionString)) bạn vẫn cần phải thực hiện conn.Close().

Các vấn đề liên quan