2009-10-06 10 views
9

Estoy escribiendo una aplicación donde necesito recuperar algunas filas de un DB y volcarlas en una hoja de cálculo de Excel. Estoy usando Linq para recuperar estas filas.¿Cómo puedo escribir en una hoja de cálculo de Excel usando Linq?

¿Es posible volcar estas filas directamente en sus contrapartidas en la hoja de Excel (donde una celda en Excel corresponde a una celda del DB)?

Respuesta

5

No hay una manera directa de conectar estos dos. Parece que desea que LINQ to SQL maneje la generación de consultas, pero no la asignación O/R (porque Excel no sabría qué hacer con los objetos que salen de LINQ, ya no se parecen a las filas de datos)) Puede hacer esa primera parte llamando a (datacontext) .GetCommand (yourLinqQueryHere), y luego ejecutándolo como CommandText en un SqlCommand. Llame ExecuteReader(), luego GetSchemaTable() para averiguar el orden de las columnas. Luego (suponiendo que está automatizando Excel) pase el resultado de (DbDataReader) .GetValues ​​() a Excel (Hoja de cálculo) .Row [x] .Values ​​y mostrará los resultados. Puede que necesite reordenar cosas. Si no está automatizando Excel, deberá volcar los valores utilizando el proveedor OLEDB de Jet para Excel con un OleDbConnection, o utilizar un componente de terceros para generar la hoja de cálculo.

0

La solución más rápida será la creación de un archivo csv:

col1, colb, colc 
col1, colb, colc 

Excel funciona muy bien con archivos CSV.

1

Echa un vistazo a este Excel Data Object Provider. No he utilizado personalmente la funcionalidad de escritura y he adaptado el soporte de lectura para permitir identificadores de columna ordinales (y también nombrados), pero puede ser un paso en la dirección correcta. Tenga en cuenta que no puede escribir o leer desde archivos XLSX a menos que Excel 2003+ esté instalado en la máquina de destino; Sin embargo, los archivos XLS estándar funcionarán en cualquier caja de Windows.

Mi versión adaptada con columnas ordinales se puede encontrar en here. Puede que sea necesario/útil implementarlo en la versión actual (en el enlace de arriba) si decide usar el código. Mi versión es un híbrido de características del version 2.0 y 2.5, tiene toda la funcionalidad de lectura (con algunas actualizaciones de 2.5), pero no escribe. Ah, ya diferencia de la versión 2.0 o 2.5, mi versión no requiere que la primera hoja del documento de Excel se llame "Hoja1".

Espero que ayude!

0

El hecho de que esté recuperando sus datos con LINQ es algo irrelevante. Lo que realmente buscas es una buena biblioteca para escribir Excel. Una vez que tenga eso, puede simplemente iterar a través de sus resultados para crear filas en su hoja de cálculo de Excel.

En cuanto a una biblioteca que he usado NPOI, y ha sido genial.

17

Personalmente, no soy un gran fanático del uso de bibliotecas a cosas que siempre me parecen limitadas en algún momento posterior ...

Utilicé la reflexión para generar los encabezados de columna y obtener los valores de celda de cada fila. Y si usa .NET framework 3.5, puede aprovechar los métodos de extensión para poder exportar cualquier IEnumerable<object> a un archivo Excel XDocument.

Aquí es cómo lo hice:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Xml.Linq; 

namespace YourNameSpace 
{ 
    public static class ExcelExportExtensions 
    { 
     public static XDocument ToExcelXml(this IEnumerable<object> rows) 
     { 
      return rows.ToExcelXml("Sheet1"); 
     } 

     public static XDocument ToExcelXml(this IEnumerable<object> rows, string sheetName) 
     { 
      sheetName = sheetName.Replace("/", "-"); 
      sheetName = sheetName.Replace("\\", "-"); 

      XNamespace mainNamespace = "urn:schemas-microsoft-com:office:spreadsheet"; 
      XNamespace o = "urn:schemas-microsoft-com:office:office"; 
      XNamespace x = "urn:schemas-microsoft-com:office:excel"; 
      XNamespace ss = "urn:schemas-microsoft-com:office:spreadsheet"; 
      XNamespace html = "http://www.w3.org/TR/REC-html40"; 

      XDocument xdoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes")); 

      var headerRow = from p in rows.First().GetType().GetProperties() 
          select new XElement(mainNamespace + "Cell", 
           new XElement(mainNamespace + "Data", 
            new XAttribute(ss + "Type", "String"), p.Name)); //Generate header using reflection 

      XElement workbook = new XElement(mainNamespace + "Workbook", 
       new XAttribute(XNamespace.Xmlns + "html", html), 
       new XAttribute(XName.Get("ss", "http://www.w3.org/2000/xmlns/"), ss), 
       new XAttribute(XName.Get("o", "http://www.w3.org/2000/xmlns/"), o), 
       new XAttribute(XName.Get("x", "http://www.w3.org/2000/xmlns/"), x), 
       new XAttribute(XName.Get("xmlns", ""), mainNamespace), 
       new XElement(o + "DocumentProperties", 
         new XAttribute(XName.Get("xmlns", ""), o), 
         new XElement(o + "Author", "Smartdesk Systems Ltd"), 
         new XElement(o + "LastAuthor", "Smartdesk Systems Ltd"), 
         new XElement(o + "Created", DateTime.Now.ToString()) 
        ), //end document properties 
       new XElement(x + "ExcelWorkbook", 
         new XAttribute(XName.Get("xmlns", ""), x), 
         new XElement(x + "WindowHeight", 12750), 
         new XElement(x + "WindowWidth", 24855), 
         new XElement(x + "WindowTopX", 240), 
         new XElement(x + "WindowTopY", 75), 
         new XElement(x + "ProtectStructure", "False"), 
         new XElement(x + "ProtectWindows", "False") 
        ), //end ExcelWorkbook 
       new XElement(mainNamespace + "Styles", 
         new XElement(mainNamespace + "Style", 
          new XAttribute(ss + "ID", "Default"), 
          new XAttribute(ss + "Name", "Normal"), 
          new XElement(mainNamespace + "Alignment", 
           new XAttribute(ss + "Vertical", "Bottom") 
          ), 
          new XElement(mainNamespace + "Borders"), 
          new XElement(mainNamespace + "Font", 
           new XAttribute(ss + "FontName", "Calibri"), 
           new XAttribute(x + "Family", "Swiss"), 
           new XAttribute(ss + "Size", "11"), 
           new XAttribute(ss + "Color", "#000000") 
          ), 
          new XElement(mainNamespace + "Interior"), 
          new XElement(mainNamespace + "NumberFormat"), 
          new XElement(mainNamespace + "Protection") 
         ), 
         new XElement(mainNamespace + "Style", 
          new XAttribute(ss + "ID", "Header"), 
          new XElement(mainNamespace + "Font", 
           new XAttribute(ss + "FontName", "Calibri"), 
           new XAttribute(x + "Family", "Swiss"), 
           new XAttribute(ss + "Size", "11"), 
           new XAttribute(ss + "Color", "#000000"), 
           new XAttribute(ss + "Bold", "1") 
          ) 
         ) 
        ), // close styles 
        new XElement(mainNamespace + "Worksheet", 
         new XAttribute(ss + "Name", sheetName /* Sheet name */), 
         new XElement(mainNamespace + "Table", 
          new XAttribute(ss + "ExpandedColumnCount", headerRow.Count()), 
          new XAttribute(ss + "ExpandedRowCount", rows.Count() + 1), 
          new XAttribute(x + "FullColumns", 1), 
          new XAttribute(x + "FullRows", 1), 
          new XAttribute(ss + "DefaultRowHeight", 15), 
          new XElement(mainNamespace + "Column", 
           new XAttribute(ss + "Width", 81) 
          ), 
          new XElement(mainNamespace + "Row", new XAttribute(ss + "StyleID", "Header"), headerRow), 
          from contentRow in rows 
          select new XElement(mainNamespace + "Row", 
           new XAttribute(ss + "StyleID", "Default"), 
            from p in contentRow.GetType().GetProperties() 
            select new XElement(mainNamespace + "Cell", 
             new XElement(mainNamespace + "Data", new XAttribute(ss + "Type", "String"), p.GetValue(contentRow, null))) /* Build cells using reflection */) 
         ), //close table 
         new XElement(x + "WorksheetOptions", 
          new XAttribute(XName.Get("xmlns", ""), x), 
          new XElement(x + "PageSetup", 
           new XElement(x + "Header", 
            new XAttribute(x + "Margin", "0.3") 
           ), 
           new XElement(x + "Footer", 
            new XAttribute(x + "Margin", "0.3") 
           ), 
           new XElement(x + "PageMargins", 
            new XAttribute(x + "Bottom", "0.75"), 
            new XAttribute(x + "Left", "0.7"), 
            new XAttribute(x + "Right", "0.7"), 
            new XAttribute(x + "Top", "0.75") 
           ) 
          ), 
          new XElement(x + "Print", 
           new XElement(x + "ValidPrinterInfo"), 
           new XElement(x + "HorizontalResolution", 600), 
           new XElement(x + "VerticalResolution", 600) 
          ), 
          new XElement(x + "Selected"), 
          new XElement(x + "Panes", 
           new XElement(x + "Pane", 
            new XElement(x + "Number", 3), 
            new XElement(x + "ActiveRow", 1), 
            new XElement(x + "ActiveCol", 0) 
           ) 
          ), 
          new XElement(x + "ProtectObjects", "False"), 
          new XElement(x + "ProtectScenarios", "False") 
         ) // close worksheet options 
        ) // close Worksheet 
       ); 

      xdoc.Add(workbook); 

      return xdoc; 
     } 
    } 
} 

También he creado otro método de extensión para facilitar el volver XDocument en escenarios web:

public static DownloadableFile ToDownloadableXmlFileForExcel2003(this System.Xml.Linq.XDocument file, string fileName) 
{ 
    MemoryStream ms = new MemoryStream(); 

    XmlWriterSettings xmlWriterSettings = new XmlWriterSettings() { Encoding = Encoding.UTF8 }; 
    XmlWriter xmlWriter = XmlWriter.Create(ms, xmlWriterSettings); 

    file.Save(xmlWriter); //.Save() adds the <xml /> header tag! 
    xmlWriter.Close();  //Must close the writer to dump it's content its output (the memory stream) 

    DownloadableFile dbf = 
      new DownloadableFile 
      { 
       FileName = String.Format("{0}.xls", fileName.Replace(" ", "")), 
       Content = ms.ToArray(), 
       MimeType = "application/vnd.ms-excel" 
      }; 

    ms.Close(); 
    ms.Dispose(); 

    return dbf; 
} 

Espero que esto ayude!

+1

He actualizado el método de extensión ToDownloadableXmlFileForExcel2003() para usar un objeto MemoryStream en lugar de un .ToString() que causaba excepciones de OutOfMemoryException. Un problema con mi forma de generar documentos XML XLS es el tamaño de los archivos. Está bien para documentos pequeños pero no para grandes xls con más de 100k líneas. – bounav

+2

Esto parece genial, así que lo intenté, pero aparece un error "Excel no puede abrir el archivo 'newexcelfil2.xlsx' porque el formato de archivo o la extensión del archivo no es válido. Verifique que el archivo no esté dañado y que la extensión del archivo coincida con formato del archivo ". He verificado que el xml está bien formado. – tjp69

1

Escribir al Excel XML con LINQ to XML

es increíblemente sencilla, no requiere bibliotecas externas, y utiliza la reflexión para convertir cualquier IEnumerable en una hoja!

http://scottstjohn.wordpress.com/2011/04/02/write-to-excel-xml-with-linq-to-xml/

según lo descrito por el autor, Scott St. John (supongo que esto de su URL - que no tiene ninguna información bio en su sitio):

encontré una clase de extensión realmente agradable para escribir cualquier IEnumerable en una hoja de cálculo XML de Excel . Vea C# write to Excel using Linq by bounav. I hice un par de modificaciones porque quería poder agregar nuevos IEnumerables como hojas de trabajo. Vea un ejemplo y el código fuente debajo de

Copié/pegué bastante su código en MyExtensions en LINQPad y lo usé de inmediato.

Cuestiones relacionadas