如何使用Linq将数据写入Excel电子表格?

10

我正在编写一个需要从数据库中检索一些行并将它们转储到Excel电子表格中的应用程序。 我正在使用Linq来检索这些行。

是否有可能直接将这些行倒入其在Excel表格中的对应位置(其中一个Excel单元格对应于一个来自数据库的单元格)?

6个回答

18

我个人不太喜欢使用库来做这样的事情,因为我总觉得在后面的某个时刻会受到限制...

我使用反射来生成列标题并获取每行的单元格值。如果您正在使用.NET框架3.5,您可以利用扩展方法将任何IEnumerable<object>导出到Excel XDocument文件中。

这是我的做法:

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

我还创建了另一个扩展方法,以便在Web场景中轻松返回XDocument:

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;
}
希望这有所帮助!

1
我已更新ToDownloadableXmlFileForExcel2003()扩展方法,使用MemoryStream对象代替.ToString(),因为后者会导致OutOfMemoryException异常。我生成XML XLS文档的方式存在一个问题,即文件大小。对于小型文档来说还好,但对于具有10万行以上的大型xls表格来说就不太适用了。 - bounav
2
这看起来很酷,所以我尝试了一下,但是出现了一个错误:“Excel无法打开文件'newexcelfil2.xlsx',因为文件格式或文件扩展名无效。请验证文件未被损坏并且文件扩展名与文件格式匹配。” 我已经验证了xml格式正确。 - tjp69
有人找到了你的代码并进行了小修改,还添加了一个名为ToExcelXmlWorksheet()的方法。他们的代码在https://scottstjohn.wordpress.com/2011/04/02/write-to-excel-xml-with-linq-to-xml/上。 - Colin

5

这两者之间没有直接的连接方法。你似乎想让LINQ to SQL处理查询生成,但不进行对象关系映射(O/R映射)(因为Excel不知道如何处理从LINQ中得到的对象——它们不再像数据行一样)。你可以通过调用(datacontext).GetCommand(yourLinqQueryHere)来完成第一部分,然后在SqlCommand中将其作为CommandText运行。调用ExecuteReader(),然后使用GetSchemaTable()来确定列的顺序。然后(假设你正在自动化Excel),将(DbDataReader).GetValues()的结果传递给Excel的(Worksheet).Row[x].Values,它会将结果分散开。你可能需要重新排序一下。如果你不是自动化Excel,那么你需要使用Jet OLEDB提供程序将值倒入Excel中的OleDbConnection,或者使用第三方组件来生成电子表格。


1

1

看看这个Excel数据对象提供程序。我个人没有使用过它的写入功能,并且我改编了读取支持以允许序数(以及命名)列标识符,但这可能是朝着正确方向迈出的一步。请记住,除非在目标计算机上安装了Excel 2003+,否则无法从XLSX文件中写入或读取;标准XLS文件将在任何Windows计算机上工作。

你可以在这里找到我改编的带序数列的版本。如果你决定使用这个代码,可能会发现在当前版本(上面的链接)中实现它是必要/有帮助的。我的版本是从版本2.0和2.5的功能中混合而来-它具有所有的读取功能(带有一些2.5的升级),但没有写入功能。哦,还有,与2.0或2.5版本不同,我的版本不需要将Excel文档中的第一个工作表命名为“Sheet1”。

希望这有所帮助!


0
最快的解决方案是创建一个csv文件:
col1, colb, colc
col1, colb, colc

Excel非常适合处理csv文件。


0

你使用LINQ检索数据的事实有点无关紧要。你真正需要的是一个好的库来编写Excel。一旦你拥有了它,你可以简单地遍历你的结果,在Excel工作表中创建行。

至于我使用过的库,NPOI非常棒。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接