2009-01-23 8 views
5

Nous avons une application C#/ASP.NET (2.0) s'exécutant sur IIS 6 sur Windows Server 2003 Enterprise Edition. Cette application lit les fichiers Excel en utilisant OleDb, mais il y a des cas où une exception "Unspecified Error" est générée depuis l'application.C#/ASP.NET Oledb - MS Excel lire "Erreur non spécifiée"

Le fichier est stocké dans le répertoire temporaire par notre code de téléchargement de fichier avant ouverture. Comme nous avons activé l'accès anonyme dans IIS et que nous utilisons également l'emprunt d'identité dans web.config, le dossier C: \ Windows \ Temp \ possède les droits appropriés pour que le compte d'utilisateur invité Internet (IUSR_ [NomOrdinateur]) puisse créer, modifier et supprimer des fichiers là-bas.

chaîne de connexion OleDb:
Provider = Microsoft.Jet.OLEDB.4.0; Data Source = C: \ Windows \ Temp \ tmp123.tmp.xls;
Propriétés étendues = "Excel 8.0; HDR = Oui; IMEX = 1;"

[La « source de données » attribut serait au-dessus de changer pour chaque fichier.]

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

Solution:
Jusqu'à présent, la seule solution que nous pourrions trouver était de faire une iisreset (nous Le recyclage des pools d'applications est également configuré pour se produire une fois par jour dans IIS, mais cela ne semble pas aider puisque le problème persiste parfois sur plusieurs jours consécutifs. Bien que ce ne soit pas une bonne chose à faire, ce qui l'aggrave, c'est que nous avons d'autres applications sur le même site Web qui seraient affectées chaque fois que nous réinitialiserions IIS.

Les questions:
1. Comment résoudre cette erreur, car il arrive de temps en temps et nous ne voyons pas un modèle?
2. Existe-t-il de meilleures façons (et gratuites) de traiter les fichiers Excel de C#/ASP.NET en dehors de OleDb? (Nous préférons ne pas installer MS Office sur les serveurs car il est pas recommandé par Microsoft)

Nos limites:
1. Nous sommes coincés avec le format MS Office 2003 (.xls) et ne peut se déplacer à la MS Office 2007 (OOXML) format.
2. Les raisons pour lesquelles nous n'utilisons pas CSV sont parce que nous pouvons avoir des virgules dans nos données (c'est une douleur à traiter même si nous utilisons des citations) et nous utilisons également plusieurs feuilles de calcul dans notre tableur (cela ne peut pas être fait CSV).

Merci! :)

Mise à jour:
Merci, Keith. Cela semble être un problème avec le moteur Jet, mais nous l'utilisons en raison de l'absence de (libre et facile à utiliser) alternatives.
Merci, Joe. Mais nous avons un budget limité - nous cherchons donc principalement des outils/bibliothèques gratuits.

Répondre

2

Je soupçonne que l'erreur est quelque chose à voir avec le vénérable moteur Jet OLEDB. C'est plutôt grincheux - bien pour la plupart des choses de bureau mais pas beaucoup pour l'échange de données d'entreprise.

Si vous pouvez effectuer une mise à niveau vers un C# 3/.Net 3.5 récent, vous pouvez utiliser la bibliothèque System.IO.Packaging pour ouvrir les fichiers Office 2007 (fichiers .xlsx ou .xlsm).

Ces fichiers sont en fait des zips - renommez-les en .zip et vous pouvez simplement voir les fichiers XML à l'intérieur.

Les formats des fichiers XML sont assez horribles (par exemple, les commentaires de cellule sont VML, pouah!) Mais lisibles.

Vous pouvez également demander à vos utilisateurs d'enregistrer les tableaux Excel au format CSV. Cependant, j'éviterais le fournisseur de la base de données de pilotes de texte Microsoft - c'est de la foutaise et ne peut pas gérer unicode. Les CSV sont faciles à lire de toute façon.

3

Assurez-vous de fermer vos connexions.

Par exemple, lors du développement d'applications MS Access (Jet), cette erreur se produit si trop de connexions restent ouvertes. Cela fonctionne bien (il y a votre occasionnellement) pendant un moment jusqu'à ce qu'il atteigne les connexions max ouvertes.

2

J'utilise SpreadSheetGear.NET depuis un moment, principalement pour créer des fichiers Excel, et cela fonctionne bien.

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

Il permet la lecture de fichiers Excel binaire/écriture dans .NET natif, résoudre tous les problèmes précédents que j'ai rencontrés essayer d'utiliser OLE et JET pour lire et créer des fichiers Excel.

La version de base est utilisée gratuitement pour l'enregistrement de Visual C++ Express 2005. Cette version n'a pas été publiée, elle peut donc exister ou non avec l'édition 2008.

1

SpreadsheetGear for .NET vous donne une API pour travailler avec les classeurs xls et xlsx de .NET. Il est plus facile à utiliser et plus rapide que OleDB ou le modèle d'objet COM Excel (continuez à lire pour une preuve de cela).

Disclaimer: Je possède SpreadsheetGear LLC

Voici le code pour créer une ligne par 50 000 10 classeur de colonne avec SpreadsheetGear, enregistrez-le sur le disque, et puis on additionne les chiffres à l'aide OleDb et SpreadsheetGear. SpreadsheetGear lit les cellules 500K en 0.31 secondes par rapport à 0.63 secondes avec OleDB - un peu plus de deux fois plus vite. SpreadsheetGear crée et lit le classeur en moins de temps qu'il n'en faut pour lire le classeur avec OleDB.

Le code est ci-dessous. Vous pouvez voir le live samples ou l'essayer par vous-même avec le 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

J'ai eu le même problème et il semble qu'il est fixé en fermant la connexion au fichier (xls ou csv) à chaque itération de la boucle. Je suppose que vous parcourez également une liste de fichiers et .Open() une nouvelle connexion à chaque fichier. Si vous fermez() la connexion à la fin de la boucle, le problème semble disparaître.

1

Connexion TimeOut peut être l'une des raisons. Vérifiez la requête combien de temps il faut pour exécuter dans l'application en déboguant.

1

On dirait que je l'ai eu tort, voir Problem with OleDbConnection, Excel and connection pooling

Fondamentalement, ce que dit crice, mais il semble y avoir un problème dans la mise en œuvre de Dispose() lorsque le constructeur OleDbDataAdapter (String, String) est appelée avec Excel-ConnectionString, comme implicitement créé la connexion n'est apparemment pas fermée.

La solution consiste à envelopper tous les appels OleDbDataApater usages (vous faisiez l'utilisation de ...choses comme il met en œuvre IDisposable) avec un séparé

using (var conn = new OleDbConnection(connectionString)) 

puis appeler le constructeur OleDbDataAdapter (String, OleDbConnection).

EDIT: Je me suis trompé sur fermeture de la connexion sur l'élimination. conn.Dispose() ne ferme pas la connexion, donc à l'intérieur du using (var conn = new OleDbConnection(connectionString)) vous devez toujours faire un conn.Close().

Questions connexes