将表格而不是范围定义为数据透视表“cacheSource”

我正在构build一个工具来自动创build包含一个表和一个关联的数据透视表的Excel工作簿。 表结构在一张纸上,稍后使用另一个工具从数据库中提取数据。 数据透视表是在第二张纸上使用上一张表中的表作为源。

我正在使用EPPlus来帮助构build工具,但遇到指定cacheSource问题。 我正在使用以下来创build范围和数据透视表:

  var dataRange = dataWorksheet.Cells[dataWorksheet.Dimension.Address.ToString()]; var pivotTable = pivotWorksheet.PivotTables.Add(pivotWorksheet.Cells["B3"], dataRange, name); 

这将cacheSource设置为:

 <x:cacheSource type="worksheet" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> <x:worksheetSource ref="A1:X2" sheet="dataWorksheet" /> 

或在Excel中,数据源设置为:

 dataWorksheet!$A$1:$X$2 

这工作正常,如果表大小永远不会改变,但作为行数将是dynamic的,我发现数据刷新时,数据只从指定的初始范围读取。

我想要做的是以编程方式将cacheSource设置为:

 <x:cacheSource type="worksheet" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> <x:worksheetSource name="dataWorksheet" /> </x:cacheSource> 

或在Excel中,将数据源设置为:

 dataWorksheet 

我相信有可能通过直接访问XML来做到这一点(任何指针都将是最受欢迎的),但有什么办法可以使用EPPlus来做到这一点?

它可以做,但它不是世界上最漂亮的东西。 您可以提取cachingdef xml并从创build的EPPlus数据透视表对象中进行编辑,但是当您调用package.save() (或GetAsByteArray() )时,会parsing保存中的xml以生成最终文件。 正如你所说,这是EPPlus无法处理表格的结果。

所以,你的select是用EPPlus正常保存文件,然后使用.net ZipArchive操纵xlsx的内容,这是一个重命名的zip文件。 诀窍是你不能在zip中乱序操作文件,否则当Excel打开文件时会报错。 既然你不能插入一个条目(只能添加到最后),你必须重新创buildzip。 这是一个ZipArchive的扩展方法,可以让你更新caching源:

 public static bool SetCacheSourceToTable(this ZipArchive xlsxZip, FileInfo destinationFileInfo, string tablename, int cacheSourceNumber = 1) { var cacheFound = false; var cacheName = String.Format("pivotCacheDefinition{0}.xml", cacheSourceNumber); using (var copiedzip = new ZipArchive(destinationFileInfo.Open(FileMode.Create, FileAccess.ReadWrite), ZipArchiveMode.Update)) { //Go though each file in the zip one by one and copy over to the new file - entries need to be in order xlsxZip.Entries.ToList().ForEach(entry => { var newentry = copiedzip.CreateEntry(entry.FullName); var newstream = newentry.Open(); var orgstream = entry.Open(); //Copy all other files except the cache def we are after if (entry.Name != cacheName) { orgstream.CopyTo(newstream); } else { cacheFound = true; //Load the xml document to manipulate var xdoc = new XmlDocument(); xdoc.Load(orgstream); //Get reference to the worksheet xml for proper namespace var nsm = new XmlNamespaceManager(xdoc.NameTable); nsm.AddNamespace("default", xdoc.DocumentElement.NamespaceURI); //get the source var worksheetSource = xdoc.SelectSingleNode("/default:pivotCacheDefinition/default:cacheSource/default:worksheetSource", nsm); //Clear the attributes var att = worksheetSource.Attributes["ref"]; worksheetSource.Attributes.Remove(att); att = worksheetSource.Attributes["sheet"]; worksheetSource.Attributes.Remove(att); //Create the new attribute for table att = xdoc.CreateAttribute("name"); att.Value = tablename; worksheetSource.Attributes.Append(att); xdoc.Save(newstream); } orgstream.Close(); newstream.Flush(); newstream.Close(); }); } return cacheFound; } 

这里是如何使用它:

 //Throw in some data var datatable = new DataTable("tblData"); datatable.Columns.AddRange(new[] { new DataColumn("Col1", typeof (int)), new DataColumn("Col2", typeof (int)), new DataColumn("Col3", typeof (object)) }); for (var i = 0; i < 10; i++) { var row = datatable.NewRow(); row[0] = i; row[1] = i*10; row[2] = Path.GetRandomFileName(); datatable.Rows.Add(row); } const string tablename = "PivotTableSource"; using (var pck = new ExcelPackage()) { var workbook = pck.Workbook; var source = workbook.Worksheets.Add("source"); source.Cells.LoadFromDataTable(datatable, true); var datacells = source.Cells["A1:C11"]; source.Tables.Add(datacells, tablename); var pivotsheet = workbook.Worksheets.Add("pivot"); pivotsheet.PivotTables.Add(pivotsheet.Cells["A1"], datacells, "PivotTable1"); using (var orginalzip = new ZipArchive(new MemoryStream(pck.GetAsByteArray()), ZipArchiveMode.Read)) { var fi = new FileInfo(@"c:\temp\Pivot_From_Table.xlsx"); if (fi.Exists) fi.Delete(); var result = orginalzip.SetCacheSourceToTable(fi, tablename, 1); Console.Write("Cache source was updated: "); Console.Write(result); } }