using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Spreadsheet; //using AsbSaubReport; //using AutoMapper; using System; using System.Linq; namespace ConsoleApp1 { //var options = new DbContextOptionsBuilder() // .UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True") // .Options; //var context = new AsbCloudDbContext(options); class Program { static void Main(/*string[] args*/) { const string firstExcelPath = @"D:\excels\excel1.xlsx"; const string secondExcelPath = @"D:\excels\excel2.xlsx"; const string resultExcelPath = @"D:\excels\result.xlsx"; using var firstExcel = SpreadsheetDocument.Open(firstExcelPath, true); using var secondExcel = SpreadsheetDocument.Open(secondExcelPath, true); using (var resultExcel = SpreadsheetDocument.Create(resultExcelPath, SpreadsheetDocumentType.Workbook, true)) { var workbookPart = resultExcel.AddWorkbookPart(); workbookPart.Workbook = new Workbook(); var sheets = workbookPart.Workbook.AppendChild(new Sheets()); var sharedStringTablePart = resultExcel.WorkbookPart. AddNewPart(); var stylesPart = resultExcel.WorkbookPart. AddNewPart(); workbookPart.Workbook.Save(); } using (var resultExcel = SpreadsheetDocument.Open(resultExcelPath, true)) { foreach (var sheet in firstExcel.WorkbookPart.Workbook.Descendants()) { // Чтобы писать на новый лист, нужен новый WorkSheetPart, WorkSheet, SheetData и Sheet. // https://stackoverflow.com/questions/9120544/openxml-multiple-sheets var resultExcelWorkSheetPart = resultExcel.WorkbookPart.AddNewPart(); resultExcelWorkSheetPart.Worksheet = new Worksheet(); var resultExcelSheetData = resultExcelWorkSheetPart. Worksheet.AppendChild(new SheetData()); var newSheet = new Sheet() { Id = resultExcel.WorkbookPart.GetIdOfPart(resultExcelWorkSheetPart), SheetId = sheet.SheetId.Value, Name = sheet.Name }; resultExcel.WorkbookPart.Workbook.Sheets.Append(newSheet); var firstExcelWorkSheetPart = firstExcel.WorkbookPart. GetPartById(sheet.Id) as WorksheetPart; // Не самая лучшая выборка рядов текущей страницы исходного файла. // Наверняка для этого есть метод. var firstExcelRows = firstExcelWorkSheetPart.Worksheet.ChildElements .FirstOrDefault(w => w.LocalName == "sheetData").ChildElements; // Числовые ячейки хранятся как есть и переносятся без проблем, а вот значения строковых ячеек хранятся // в отдельной таблице. При этом в самой строковой ячейке хранится индекс этой строки в таблице // (чтоб не хранить кучу одинаковых строк в документе. Что-то вроде интернирования строк). // Тут как раз переносится клон этой таблицы из исходного файла в результирующий. // Разные всяческие Part'ы можно найти тут: // https://docs.microsoft.com/ru-ru/dotnet/api/documentformat.openxml.packaging.spreadsheetdocument.workbookpart?view=openxml-2.8.1 var firstExcelStringTable = firstExcel.WorkbookPart.GetPartsOfType() .FirstOrDefault().SharedStringTable; resultExcel.WorkbookPart.SharedStringTablePart.SharedStringTable = firstExcelStringTable.CloneNode(true) as SharedStringTable; // Аналогично переносим таблицу стилей ячеек (жирный шрифт и прочее). // Иначе ячейки переносятся абсолютно обычными, без всякой стилизации. var stylesSheet = firstExcel.WorkbookPart.GetPartsOfType() .FirstOrDefault().Stylesheet; resultExcel.WorkbookPart.WorkbookStylesPart.Stylesheet = stylesSheet.CloneNode(true) as Stylesheet; // Клонируем ряды в новый файл (без клонирования они не вытаскиваются из общего дерева) foreach (var row in firstExcelRows) resultExcelSheetData.AppendChild(row.CloneNode(true)); } } Console.WriteLine("Done. Press any key to quit."); Console.ReadKey(); } } }