【问题标题】:How do I insert data into an existing Excel spreadsheet from data table by mapping Excel sheet column header?如何通过映射 Excel 工作表列标题将数据从数据表插入现有 Excel 电子表格?
【发布时间】:2017-09-25 21:57:16
【问题描述】:

我有带有列标题的宏 Excel 电子表格,有些列是计算列。

我需要做的是从数据库中获取数据并填充这个文件。

我在查询后检索到的数据表没有 Excel 文件中的所有列,因为 Excel 文件具有计算列。

所以我需要将数据表中的列映射到 Excel 文件并加载数据。 我需要确保在加载新数据之前从文件中删除现有数据,因为这个文件需要每周创建。

我从未在 OpenXML Document 和 EPPlus 中工作过。

尝试 #1:使用 EPPlus

private static void OtehrMethod(DataTable dataTable, string filePath)
{
    // using EPPlus
    var package = new ExcelPackage(new FileInfo(filePath));

    ExcelWorksheet workSheet = package.Workbook.Worksheets["MySheet"];

    foreach (DataRow row in dataTable.Rows)
    {
        int i = 1;
        object cellValue = workSheet.Cells[2, i].Value;

        workSheet.Cells[1, 1].Value = Conver.ToInt(row["Id"]);
        // break;
        //workSheet.Cells[2, i].Value =row["First_Name"].ToString();
        //workSheet.Cells[3, i].Value = row["Last_Name"].ToString();
        //workSheet.Cells[4, i].Value = row["Job_Title"].ToString();
        //workSheet.Cells[5, i].Value = row["Skills"].ToString();
        i++;
    }

    package.Save();
}

尝试 #2:使用 Open XML

private static void SomeMethod()
{
    string filePath = ConfigurationManager.AppSettings["ExcelFilePath"];
    string workingSheetName = ConfigurationManager.AppSettings["WorkingSheetName"];

    using (SpreadsheetDocument document = SpreadsheetDocument.Open(filePath, true))
    {
        // WorkbookPart workbook = document.WorkbookPart;
        WorkbookPart workbookPart = document.WorkbookPart;
        Workbook workbook = document.WorkbookPart.Workbook;

        int sheetIndex = 0;

        foreach (WorksheetPart worksheetpart in workbook.WorkbookPart.WorksheetParts)
        {
            Worksheet worksheet = worksheetpart.Worksheet;

            string sheetName = workbookPart.Workbook.Descendants<Sheet>().ElementAt(sheetIndex).Name;

            if (sheetName.ToUpper() == workingSheetName.ToUpper())
            {
                IEnumerable<Row> rows = worksheet.GetFirstChild<SheetData>().Descendants<Row>();

                foreach (Row row in rows)
                {
                    // How do I map Excel sheet column with data table column and insert the values into Excel ?

                    // Column["FirstName"] = DTRow["FirstName"]
                    //Column["LastName"] = DTRow["LastName"]
                }
            }

            sheetIndex++;
        }
    }
    // throw new NotImplementedException();
}

【问题讨论】:

    标签: openxml epplus


    【解决方案1】:

    更清晰。您处于我没有亲自编码的领域,但每张工作表的 SheetData 将是清除数据的地方。下面是 Vincent Tan 的另一个程序,展示了如何在现有工作表中添加和更改数据:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;
    using System.Drawing;
    using DocumentFormat.OpenXml;
    using DocumentFormat.OpenXml.Packaging;
    using DocumentFormat.OpenXml.Spreadsheet;
    using DocumentFormat.OpenXml.Drawing.Spreadsheet;
    
    namespace ExcelOpenXmlFromTemplate
    {
        class Program
        {
            static void Main(string[] args)
            {
                // Switch between an Excel file with the styles and stuff
                // and an actual Excel template file.
                // I suggest you check out the accompanying template files first
                // to compare how the original and final files will look like.
    
                //string sFileTemplate = "FinancialYearReport.xlsx";
                string sFileTemplate = "FinancialYearReportTemplate.xltx";
                string sFile = "FinancialYearReportFinal.xlsx";
                if (File.Exists(sFile))
                {
                    File.Delete(sFile);
                }
    
                if (sFileTemplate.ToLower().EndsWith(".xlsx"))
                {
                    File.Copy(sFileTemplate, sFile);
                }
                else
                {
                    // ends with .xltx
                    // Excel template files work differently. You can't work on it
                    // directly and then save the result to a different file.
                    // More details on this in the accompanying PDF.
                    byte[] ba = File.ReadAllBytes(sFileTemplate);
                    using (MemoryStream ms = new MemoryStream())
                    {
                        ms.Write(ba, 0, ba.Length);
                        using (SpreadsheetDocument xl = SpreadsheetDocument.Open(ms, true))
                        {
                            xl.ChangeDocumentType(SpreadsheetDocumentType.Workbook);
                            xl.Close();
                        }
                        File.WriteAllBytes(sFile, ms.ToArray());
                    }
                }
    
                try
                {
                    BuildWorkbookFromTemplate(sFile);
                    Console.WriteLine("Program end");
                    Console.ReadLine();
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                    Console.ReadLine();
                }
            }
    
            private static void BuildWorkbookFromTemplate(string filename)
            {
                string sSheetname = "FYReport";
                // take note that we're opening a file, and not creating a file as before
                using (SpreadsheetDocument xl = SpreadsheetDocument.Open(filename, true))
                {
                    WorkbookPart wbp = xl.WorkbookPart;
    
                    // Get the worksheet with the required name.
                    // To be used to match the ID for the required sheet data
                    // because the Sheet class and the SheetData class aren't
                    // linked to each other directly.
                    Sheet s = null;
                    if (wbp.Workbook.Sheets.Elements<Sheet>().Count(nm => nm.Name == sSheetname) == 0)
                    {
                        // no such sheet with that name
                        xl.Close();
                        return;
                    }
                    else
                    {
                        s = (Sheet)wbp.Workbook.Sheets.Elements<Sheet>().Where(nm => nm.Name == sSheetname).First();
                    }
    
                    WorksheetPart wsp = (WorksheetPart)xl.WorkbookPart.GetPartById(s.Id.Value);
                    SheetData sd = (SheetData)wsp.Worksheet.GetFirstChild<SheetData>();
    
                    // We will update in 2 ways.
                    // 1) Get the required cell, do the updates on it, then save the worksheet.
                    // 2) Create a Cell with our data, then throw it to the SheetData, then save the worksheet.
                    //    If the required cell already exists, we overwrite it. If not, we create it.
    
                    // The difference between the 2 is that, if your worksheet doesn't have the required cell,
                    // it becomes more difficult, because you'll have to also append it to the row.
                    // More details explained in the accompanying PDF.
    
                    // I left the saving of the worksheet out of the UpdateCell() function
                    // because I want to be consistent with the GetCell() version of updating.
                    // So wsp.Worksheet.Save(); is done after every cell modification.
                    // Otherwise I'd put it at the end of the update function.
                    // The code is written for you to understand what's going on, not for optimisation.
    
                    int iPreviousYear = DateTime.Now.Year - 1;
                    int iTheYearBeforeThat = iPreviousYear - 1;
    
                    Cell c = null;
    
                    // main title
                    c = GetCell(sd, "A", 1);
                    if (c != null)
                    {
                        if (c.DataType == CellValues.SharedString)
                        {
                            c.DataType = CellValues.String;
                        }
                        c.CellValue = new CellValue(string.Format("Financial Year Report For {0}/{1}", iTheYearBeforeThat, iPreviousYear));
                        // I was going to show you how to work with an existing value like so:
                        // c.CellValue = new CellValue(string.Format("{0} {1}/{2}", c.CellValue.Text.Trim(), iTheYearBeforeThat, iPreviousYear));
                        // But the existing value is a string, and Excel had kindly saved it into the SharedString table
                        // when I created the template file.
                        // The shared string concept makes things a little more interesting if you really want to work with it.
                        // For now, it's what-you-see-is-what-you-get, which is much simpler.
                        wsp.Worksheet.Save();
                    }
    
                    // description of the year before the last
                    c = GetCell(sd, "A", 4);
                    if (c != null)
                    {
                        if (c.DataType == CellValues.SharedString)
                        {
                            c.DataType = CellValues.String;
                        }
                        c.CellValue = new CellValue(string.Format("Year {0}", iTheYearBeforeThat));
                        wsp.Worksheet.Save();
                    }
    
                    // description of last year
                    // I'll show you the difference in code for the 2nd method of updating
                    c = new Cell();
                    c.DataType = CellValues.String;
                    c.CellReference = "A13";
                    c.CellValue = new CellValue(string.Format("Year {0}", iPreviousYear));
                    UpdateCell(sd, c, 13);
                    wsp.Worksheet.Save();
    
                    // We will update the revenue and operational costs of "the year before the last" FY.
                    // You would probably retrieve the values from a database and fill it here.
                    // I don't have any meaningful data anyway, so I'm gonna cheat with a randomiser...
                    // I'll use the 2nd method of updating for convenience.
                    Random rd = new Random();
                    int i;
    
                    for (i = 5; i <= 8; ++i)
                    {
                        // revenue values
                        c = new Cell();
                        c.DataType = CellValues.Number;
                        c.CellReference = "B" + i.ToString();
                        // range from 8.0 to 12.0
                        c.CellValue = new CellValue((rd.NextDouble() * 4.0 + 8.0).ToString("f2"));
                        UpdateCell(sd, c, (uint)i);
                        wsp.Worksheet.Save();
    
                        // operational cost values
                        c = new Cell();
                        c.DataType = CellValues.Number;
                        c.CellReference = "C" + i.ToString();
                        // range from 0.5 to 1.5
                        c.CellValue = new CellValue((rd.NextDouble() + 0.5).ToString("f2"));
                        UpdateCell(sd, c, (uint)i);
                        wsp.Worksheet.Save();
                    }
    
                    // Previous financial year's revenue and operational cost values
                    for (i = 14; i <= 17; ++i)
                    {
                        // revenue values
                        c = new Cell();
                        c.DataType = CellValues.Number;
                        c.CellReference = "B" + i.ToString();
                        // range from 9.0 to 14.0
                        // We've got to have increased revenue, right? :)
                        c.CellValue = new CellValue((rd.NextDouble() * 5.0 + 9.0).ToString("f2"));
                        UpdateCell(sd, c, (uint)i);
                        wsp.Worksheet.Save();
    
                        // operational cost values
                        c = new Cell();
                        c.DataType = CellValues.Number;
                        c.CellReference = "C" + i.ToString();
                        // range from 0.4 to 1.4
                        // We've got to have decreased costs, right? :)
                        c.CellValue = new CellValue((rd.NextDouble() + 0.4).ToString("f2"));
                        UpdateCell(sd, c, (uint)i);
                        wsp.Worksheet.Save();
                    }
    
                    // this section is to show you that UpdateCell() works for
                    // out of order cells in the same row. Go ahead and mix up
                    // the order of the 5 cells (A25, B25, C25, D25 and E25) in
                    // the code and check that they are still ordered correctly in the file.
                    c = new Cell();
                    c.DataType = CellValues.String;
                    c.CellReference = "B25";
                    c.CellValue = new CellValue("2nd");
                    UpdateCell(sd, c, 25u);
                    wsp.Worksheet.Save();
    
                    c = new Cell();
                    c.DataType = CellValues.String;
                    c.CellReference = "E25";
                    c.CellValue = new CellValue("5th");
                    UpdateCell(sd, c, 25u);
                    wsp.Worksheet.Save();
    
                    c = new Cell();
                    c.DataType = CellValues.String;
                    c.CellReference = "C25";
                    c.CellValue = new CellValue("3rd");
                    UpdateCell(sd, c, 25u);
                    wsp.Worksheet.Save();
    
                    c = new Cell();
                    c.DataType = CellValues.String;
                    c.CellReference = "A25";
                    c.CellValue = new CellValue("1st");
                    UpdateCell(sd, c, 25u);
                    wsp.Worksheet.Save();
    
                    c = new Cell();
                    c.DataType = CellValues.String;
                    c.CellReference = "D25";
                    c.CellValue = new CellValue("4th");
                    UpdateCell(sd, c, 25u);
                    wsp.Worksheet.Save();
    
                    // Credits!
                    c = new Cell();
                    c.DataType = CellValues.String;
                    c.CellReference = "A27";
                    // well, we wouldn't want upper management to think this
                    // financial report was *easy* to generate, right? Claim credit!
                    c.CellValue = new CellValue("Generated by Vincent");
                    UpdateCell(sd, c, 27u);
                    wsp.Worksheet.Save();
    
                    c = new Cell();
                    c.DataType = CellValues.String;
                    c.CellReference = "E27";
                    // you could show upper management that you're working hard,
                    // and print a time that's in the wee hours of the morning...
                    // But shame on you...
                    c.CellValue = new CellValue(string.Format("Generated at {0}", DateTime.Now.ToString("dd MMM yyyy HH:mm:ss")));
                    UpdateCell(sd, c, 27u);
                    wsp.Worksheet.Save();
    
                    // we have formulae and charts to update in the template
                    xl.WorkbookPart.Workbook.CalculationProperties.ForceFullCalculation = true;
                    xl.WorkbookPart.Workbook.CalculationProperties.FullCalculationOnLoad = true;
    
                    xl.WorkbookPart.Workbook.Save();
                    xl.Close();
                }
            }
    
            private static Cell GetCell(SheetData sd, string ColumnName, UInt32 RowIndex)
            {
                // There's a small chance that the row has no RowIndex assigned.
                // You should know that the property RowIndex is optional.
                // This means the following will fail.
                // But we can't do much about a missing RowIndex in the template.
                Row r = null;
                Cell c = null;
                if (sd.Elements<Row>().Count(ri => ri.RowIndex == RowIndex) > 0)
                {
                    r = (Row)sd.Elements<Row>().Where(ri => ri.RowIndex == RowIndex).First();
                }
    
                if (r != null)
                {
                    string sCellReference = ColumnName.ToUpper() + RowIndex.ToString();
                    if (r.Elements<Cell>().Count(cr => cr.CellReference.Value == sCellReference) > 0)
                    {
                        c = (Cell)r.Elements<Cell>().Where(cr => cr.CellReference.Value == sCellReference).First();
                    }
                }
    
                return c;
            }
    
            private static void UpdateCell(SheetData sd, Cell CellData, UInt32 RowIndex)
            {
                // There's a small chance that the row has no RowIndex assigned.
                // You should know that the property RowIndex is optional.
                // This means the following will fail.
                // But we can't do much about a missing RowIndex in the template.
                Row r = null;
                if (sd.Elements<Row>().Count(ri => ri.RowIndex == RowIndex) == 0)
                {
                    // There's no row at this index, so it also means there's no cell
                    // with the data required. So just create the row with the cell.
                    r = new Row();
                    r.RowIndex = RowIndex;
                    r.Append(CellData);
                    sd.Append(r);
                }
                else
                {
                    r = (Row)sd.Elements<Row>().Where(ri => ri.RowIndex == RowIndex).First();
                    // we will assume the cell's CellReference is consistent with the given RowIndex
                    if (r.Elements<Cell>().Count(cr => cr.CellReference.Value == CellData.CellReference.Value) > 0)
                    {
                        // there's an existing cell
                        Cell c = (Cell)r.Elements<Cell>().Where(cr => cr.CellReference.Value == CellData.CellReference.Value).First();
                        c.DataType = CellData.DataType;
                        // reset to "normal" string value if it's a shared string.
                        // Otherwise, we'll have to go change the value in the SharedStringTable class,
                        // and that's out of scope for now. Let's focus on updating cells, shall we?
                        // We are also assuming we're not stupid enough to set the given cell's
                        // data type to be SharedString...
                        if (c.DataType == CellValues.SharedString)
                        {
                            c.DataType = CellValues.String;
                        }
                        c.CellValue = new CellValue(CellData.CellValue.Text);
    
                        // set any other additional properties you want
                    }
                    else
                    {
                        // no such cell
    
                        IEnumerable<Cell> iec = r.Elements<Cell>();
                        // in case there are no cells in the row
                        if (iec.Count() == 0)
                        {
                            r.Append(CellData);
                        }
                        else
                        {
                            bool bFound = false;
                            // cells need to be in order of their CellReference values
                            foreach (Cell c in iec)
                            {
                                // We assume the existing Cells are already in order.
                                // This means the moment our CellData's CellReference is before
                                // a particular existing Cell's CellReference, we can insert before
                                // that particular existing Cell.
                                if (string.Compare(CellData.CellReference.Value, c.CellReference.Value) < 0)
                                {
                                    bFound = true;
                                    r.InsertBefore(CellData, c);
                                    break;
                                }
                            }
                            // well, we have to insert our CellData *somewhere*.
                            // If not found in the loop above, then it must have the highest CellReference.
                            // So we just append it at the end of the row.
                            if (!bFound)
                            {
                                r.Append(CellData);
                            }
                        }
    
                        // we don't append the row to the SheetData variable because
                        // the row already exists. We just need to append the cell.
                    }
                }
            }
        }
    }
    

    Vincent 的示例是使用设置为模板的工作簿并且不包括清除单元格,但他确实展示了如何覆盖单元格。在我看来,他似乎涵盖了很多可能出错的事情。我会先尝试用什么都没有覆盖单元格,看看是否可行。

    【讨论】:

      【解决方案2】:

      首先,EPPlus 是 OpenXML 的包装器。因此,以下概念适用于两者。学习曲线可能很陡峭,因此我将从 Vincent Tan 程序开始供您检查:

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.IO;
      using System.Drawing;
      using DocumentFormat.OpenXml;
      using DocumentFormat.OpenXml.Packaging;
      using DocumentFormat.OpenXml.Spreadsheet;
      using DocumentFormat.OpenXml.Drawing.Spreadsheet;
      
      namespace ExcelOpenXmlSetCellValue
      {
          class Program
          {
              static void Main(string[] args)
              {
                  string sFile = "ExcelOpenXmlSetCellValue.xlsx";
                  if (File.Exists(sFile))
                  {
                      File.Delete(sFile);
                  }
                  try
                  {
                      BuildWorkbook(sFile);
                      Console.WriteLine("Program end");
                      Console.ReadLine();
                  }
                  catch (Exception e)
                  {
                      Console.WriteLine(e.ToString());
                      Console.ReadLine();
                  }
              }
      
              private static void BuildWorkbook(string filename)
              {
                  using (SpreadsheetDocument xl = SpreadsheetDocument.Create(filename, SpreadsheetDocumentType.Workbook))
                  {
                      WorkbookPart wbp = xl.AddWorkbookPart();
                      WorksheetPart wsp = wbp.AddNewPart<WorksheetPart>();
                      Workbook wb = new Workbook();
                      FileVersion fv = new FileVersion();
                      fv.ApplicationName = "Microsoft Office Excel";
                      Worksheet ws = new Worksheet();
                      SheetData sd = new SheetData();
      
                      Row r;
                      Cell c;
      
                      r = new Row();
                      // For the 2nd row. I know, it's obvious, but it's recommended
                      // that you assign a value. While Excel might be able to handle
                      // a Row class without the RowIndex assigned (in a certain case),
                      // other applications might find that difficult, in particular,
                      // your own application.
                      r.RowIndex = 2;
                      c = new Cell();
                      // "D" for 4th column. The "2" is equal to the RowIndex of the Row
                      // that the Cell class is in. Note that the number part has to be
                      // equal to the RowIndex or disaster will happen. Or worse, Excel
                      // spits out the "file corrupted" error and your user sees it.
                      c.CellReference = "D2";
                      // by default, an unassigned DataType means CellValues.Number
                      //c.DataType = CellValues.Number;
                      c.CellValue = new CellValue("9.81");
                      r.Append(c);
                      sd.Append(r);
      
                      // What if you need more Cell's in a Row? Blank lines added for readability.
                      r = new Row();
                      r.RowIndex = 5;
      
                      c = new Cell();
                      // this way, the number part is always right, but you need to
                      // assign the RowIndex first. This will make sense when you have
                      // to append many Cell classes. This is just an alternative to
                      // assigning the cell reference.
                      c.CellReference = "F" + r.RowIndex;
                      // There are 2 other string types, the SharedString and InlineString.
                      // They are discussed in another chapter. The CellValues.String type
                      // provides the most straightforward way of including text strings.
                      c.DataType = CellValues.String;
                      c.CellValue = new CellValue("Is");
                      r.Append(c);
      
                      c = new Cell();
                      c.CellReference = "G" + r.RowIndex;
                      c.DataType = CellValues.String;
                      c.CellValue = new CellValue("that in");
                      r.Append(c);
      
                      c = new Cell();
                      c.CellReference = "H" + r.RowIndex;
                      c.DataType = CellValues.String;
                      c.CellValue = new CellValue("metres per second squared?");
                      r.Append(c);
      
                      // This is for row 5
                      sd.Append(r);
      
                      // And now, to show you sometimes the code can be rearranged a little.
                      // But because you need to append the Cell class to the Row class,
                      // it makes sense (logically and conceptually) to initialise a new Row first.
                      c = new Cell();
                      c.CellReference = "F7";
                      c.DataType = CellValues.String;
                      c.CellValue = new CellValue("It'd better be. Just got used to the metric system.");
                      r = new Row();
                      r.RowIndex = 7;
                      r.Append(c);
                      sd.Append(r);
      
                      ws.Append(sd);
                      wsp.Worksheet = ws;
      
                      wsp.Worksheet.Save();
                      Sheets sheets = new Sheets();
                      Sheet sheet = new Sheet();
                      sheet.Name = "Sheet1";
                      sheet.SheetId = 1;
                      sheet.Id = wbp.GetIdOfPart(wsp);
                      sheets.Append(sheet);
                      wb.Append(fv);
                      wb.Append(sheets);
      
                      xl.WorkbookPart.Workbook = wb;
                      xl.WorkbookPart.Workbook.Save();
                      xl.Close();
                  }
              }
          }
      }
      

      那么从他的书中摘录可能会在概念上有所帮助:

      所以 SheetData 类包含 Row 类作为子类。每个 Row 类都包含 Cell 类作为子类。一个 Row 类代表一行 在电子表格中。 Cell 类表示电子表格中的一个单元格。 有时,显而易见的事情仍然需要说出来。

      建议您为 RowIndex 属性分配一个值 行类。这基本上是该 Row 类的行号。如果 它是第 4 行,分配 4。RowIndex 属性是可选的 Open XML 规范,但电子表格应用程序可能没有 处理这个问题的方法。甚至 Excel 也处理空白的 RowIndex 属性 在特殊条件下正确(我们将在后面讨论 本章)。

      对于 Cell 类,您需要注意以下属性: 

      • DataType - 单元格值的数据类型。您很可能会处理 CellValues.Number 和 CellValues.String。
      • CellReference - “A1 格式”的单元格引用。这意味着列字母后跟行索引。例如,“C5”表示 第 3 列,第 5 行。有“R1C1 格式”(“R”然后是行索引, 然后是“C”,然后是列索引,给我们前面的“R5C3” 示例),但我们没有涵盖这一点。 “A1 格式”是默认的 Excel。
      • CellValue - 动作发生的地方。你可能注意到 CellValue 实际上是一个类,它的主要值是一个字符串。这 意味着您也以字符串形式存储数字值(DataType 属性确定数据类型)。
      • CellFormula - 你有一个单元格公式。但是您可以在另一章中了解它。以上3个属性是常用的 处理。

      还有一件事。 Row 类必须附加到 SheetData 按 RowIndex 升序排列的类。必须附加单元类 按升序排列到其 Row 类。这是没有商量余地的。 否则 Excel 会因错误呕吐而窒息。

      希望这足以让您走上正轨。如果没有,我会回来查看更多问题。

      在回答您的其他问题时,对于计算列,在右侧为您为计算列构建的单元格构建计算。

      【讨论】:

      • 我不想删除现有的 XLSM SpreedSheet。我想清除 excel 表中的数据,或者你可以说覆盖。它有多张纸。它本身就是一个报告应用程序,而且非常大。我正在处理的工作表上的数据将数据提供给其他工作表和图表。我想将此现有文件用作模板并插入数据,以便其他计算列和其他引用的工作表在我正在处理的工作表被填充后自动获取数据。
      猜你喜欢
      • 1970-01-01
      • 2013-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多