4

我打算在我的 WPF 应用程序中使用 EPPlus(4.0.4 版本)库将我的实时数据导出到使用 C# .NET 的 Excel 工作表。然而,我的问题是针对 EPPLus 库的。

我已经浏览了 EPPlus 示例 (),您可以在其中使用 SetValue 将值写到工作表中。

问题是,由于我的应用程序需要在可能的几天内写入实时数据(!),我想确保我的数据每隔一段时间保存一次,以避免在应用程序/系统的情况下丢失数据碰撞。

通常,我希望有一个自动保存功能,其中可以保存当前状态,然后像往常一样继续添加新记录。

但是,EPPlus 似乎没有这个(?)......我将如何实现这一目标?

示例源代码

using (ExcelPackage package = new ExcelPackage())
{
      //Load the sheet with one string column, one date column and a few random numbers.
    var ws = package.Workbook.Worksheets.Add("Performance Test");

    //Format all cells
    ExcelRange cols = ws.Cells["A:XFD"];
    cols.Style.Fill.PatternType = ExcelFillStyle.Solid;
    cols.Style.Fill.BackgroundColor.SetColor(Color.LightGray);

    var rnd = new Random();                
    for (int row = 1; row <= Rows; row++)
    {
        ws.SetValue(row, 1, row);
        ws.SetValue(row, 2, string.Format("Row {0}", row));
        ws.SetValue(row, 3, DateTime.Today.AddDays(row));
        ws.SetValue(row, 4, rnd.NextDouble() * 10000);
        ws.SetValue(row, 5, rnd.NextDouble() * 100);
        ws.SetValue(row, 6, rnd.NextDouble() * 10);
        ws.SetValue(row, 7, rnd.NextDouble() * 78);
        ws.SetValue(row, 8, rnd.NextDouble() * 5300);
        ws.SetValue(row, 9, rnd.NextDouble() * 1250);
        ws.SetValue(row, 10, rnd.NextDouble() * 670);

        if (row % 10000 == 0)
        {
            Console.WriteLine("{0:HH.mm.ss}\tWriting row {1}...", DateTime.Now, row);

            //I need a way to save the existing data say every 10K records or so.

        }
    }             

    ws.Select("C2");
    Console.WriteLine("{0:HH.mm.ss}\tSaving...", DateTime.Now);
    package.Compression = CompressionLevel.BestSpeed;
    package.SaveAs(newFile); //this seems to be done only at the end of processing!
}
4

3 回答 3

2

AutoSave 它通常用于基于用户界面的程序。EPPlus 不是。

你必须自己做。每隔一段时间就调用package.SaveAs(newFile);一次,也许在你的循环中。你似乎if已经为此做好了准备。

于 2015-06-30T10:32:48.130 回答
2

为什么不在每批后保存并打开现有的 xls 并进行更新并再次保存?

就像

ExcelPackage package = new ExcelPackage(newFile);

(*请注意这里newFile只是变量名,在这种情况下,它更像是existingFile

您不会丢失工作表中已有的内容,您只需要找到最后一行即可从上次结束的地方继续:

ws.Dimension.End.Row;

作为奖励,您不会阻止用户访问文件(您的应用程序现在保持写入权限并阻止文件)以保存他自己的更改(比如说,在他添加“他想要从您的数据中获得的免费且非常酷的图表”之后:) )

于 2015-06-30T10:34:24.383 回答
0

我已经接受 Jan 'splite' Kondelík 的回答作为解决方案,因为它提供了解决问题所需的信息。

但是,我发布了我自己的示例,以帮助其他用户更快地获得 Jan 建议的解决方案。

基本上,在重新打开文件时,您需要在 ExcelPackage() 构造函数中传递文件的名称。此外,您可以通过传递现有工作表的名称或如下索引来获取对所需工作表的引用

package.Workbook.Worksheets["PerformanceTest"] 

或者

package.Workbook.Worksheets[1] //1 to n, 1 being base

我发布了一个示例,帮助演示如何实现“自动保存”逻辑。希望这对其他人也有帮助。

我在 C# 中创建了一个控制台应用程序来演示这一点。

示例代码

程序.cs


using System;
using System.IO;

namespace EPPlus_AutoSave_Excel_Export
{
    class Program
    {
        static void Main(string[] args)
        {
            DirectoryInfo outputDir = new DirectoryInfo(@"c:\temp\Auto-Save");

            if (!outputDir.Exists) 
                throw new Exception("outputDir does not exist!");

            Sample_Auto_Save obj = new Sample_Auto_Save(outputDir, 50000);
            obj.ExportWithAutoSave();
        }
    }
}

Sample_Auto_Save.cs


using OfficeOpenXml;
using OfficeOpenXml.Style;
using System;
using System.IO;
using System.Drawing;

namespace EPPlus_AutoSave_Excel_Export
{
    class Sample_Auto_Save
    {
        int Rows;
        int nRowsWritten = 0;
        bool bAppendData = false;
        ExcelWorksheet ws;
        DirectoryInfo outputDir;


        public Sample_Auto_Save(DirectoryInfo outputDir, int Rows)
        {
            this.Rows = Rows;
            this.outputDir = outputDir;
        }

        public void ExportWithAutoSave()
        {

            FileInfo newFile = new FileInfo(outputDir.FullName + @"\Auto_Save_Export.xlsx");
            if (newFile.Exists)
            {
                newFile.Delete();  // ensures we create a new workbook
                newFile = new FileInfo(outputDir.FullName + @"\Auto_Save_Export.xlsx");
            }

            Console.WriteLine("{0:HH.mm.ss}\tStarting...", DateTime.Now);
            while (nRowsWritten < Rows)
            {
                if (bAppendData == false)
                {
                    using (ExcelPackage package = new ExcelPackage())
                    {

                        //Load the sheet with one string column, one date column and a few random numbers
                        ws = package.Workbook.Worksheets.Add("Performance Test");
                        InsertRows();
                        bAppendData = true;
                        AutoSave(newFile, package);
                    }
                }
                else
                {
                    using (ExcelPackage package = new ExcelPackage(newFile))
                    {
                        Console.WriteLine("{0:HH.mm.ss}\tOpening existing file again!...", DateTime.Now);
                        ws = package.Workbook.Worksheets["Performance Test"];

                        InsertRows();

                        AutoSave(newFile, package);
                    }
                }       
            }
            Console.WriteLine("{0:HH.mm.ss}\tDone!!", DateTime.Now);           
        }

        private void AutoSave(FileInfo newFile, ExcelPackage package)
        {
            ws.Select("C2");
            Console.WriteLine("{0:HH.mm.ss}\tAuto-Saving...", DateTime.Now);
            package.Compression = CompressionLevel.BestSpeed;
            package.SaveAs(newFile);
            bAppendData = true;
        }

        private void InsertRows()
        {

            //Format all cells
            ExcelRange cols = ws.Cells["A:XFD"];
            cols.Style.Fill.PatternType = ExcelFillStyle.Solid;
            cols.Style.Fill.BackgroundColor.SetColor(Color.LightGray);

            var rnd = new Random();
            int startRow;
            if (ws.Dimension == null)
                startRow = 0;
            else
                startRow = ws.Dimension.End.Row;
            for (int row = startRow + 1; row <= Rows; row++, nRowsWritten++)
            {
                ws.SetValue(row, 1, row);                               //The SetValue method is a little bit faster than using the Value property
                ws.SetValue(row, 2, string.Format("Row {0}", row));
                ws.SetValue(row, 3, DateTime.Now.ToShortTimeString());
                ws.SetValue(row, 4, rnd.NextDouble() * 10000);


                if (row % (Rows/5) == 0)
                {
                    Console.WriteLine("{0:HH.mm.ss}\tWritten {1} records!...", DateTime.Now, row);
                    nRowsWritten++;
                    return;
                }
            }
        }
    }
}

更新:看起来自动保存大文件的开销非常高。在与 Jan 和 Patrick 讨论之后,我最终意识到最好将我所有的实时数据写入一个单独的数据文件,最后在我的测试结束时使用 EPPlus 导出到 excel。这将是最快,最简单的方法!

或者,如果不强制所有数据都应存在于一个文件中,请考虑将数据拆分到多个 Excel 文件中。例如,您可以为每个文件保存 10K 条记录,这样就可以同时确保速度、低内存要求和数据安全。

于 2015-06-30T12:41:52.987 回答