[NWD] More EPPlus improvements.

- Revamp save code to avoid memory copies
- Remove more unnecessary code for mutating sheets
- General cleanup

Change-Id: I9faf6c7cc94a50600076bd42ac456ba6f96cd407
Reviewed-on: https://gnocchi-internal-review.git.corp.google.com/c/third_party/epplus/+/208000
Reviewed-by: Hughes Hilton <hugheshilton@google.com>
diff --git a/EPPlus/CellStore.cs b/EPPlus/CellStore.cs
index a00f890..684cf3b 100644
--- a/EPPlus/CellStore.cs
+++ b/EPPlus/CellStore.cs
@@ -51,16 +51,7 @@
 }
 
 internal class ColumnIndex : IndexBase {
-  internal IndexBase _searchIx = new();
-
-  public ColumnIndex() {
-    _pages = new PageIndex[CellStore<int>._pagesPerColumnMin];
-    PageCount = 0;
-  }
-
-  ~ColumnIndex() {
-    _pages = null;
-  }
+  private readonly IndexBase _searchIx = new();
 
   internal int GetPosition(int row) {
     var page = (short)(row >> CellStore<int>._pageBits);
@@ -150,20 +141,12 @@
     return -1;
   }
 
-  internal int FindNext(int page) {
-    var p = GetPosition(page);
-    if (p < 0) {
-      return ~p;
-    }
-    return p;
-  }
-
-  internal PageIndex[] _pages;
+  internal PageIndex[] _pages = new PageIndex[CellStore<int>._pagesPerColumnMin];
   internal int PageCount;
 }
 
 internal class PageIndex : IndexBase {
-  internal IndexBase _searchIx = new();
+  private readonly IndexBase _searchIx = new();
 
   public PageIndex() {
     Rows = new IndexItem[CellStore<int>._pageSizeMin];
@@ -186,10 +169,6 @@
     Offset = offset;
   }
 
-  ~PageIndex() {
-    Rows = null;
-  }
-
   internal int Offset;
 
   internal int IndexOffset => IndexExpanded + Offset;
@@ -262,14 +241,10 @@
   internal const int _pagesPerColumnMin = 32;
 
   private List<T> _values = new();
-  internal ColumnIndex[] _columnIndex;
+  internal ColumnIndex[] _columnIndex = new ColumnIndex[_colSizeMin];
   internal IndexBase _searchIx = new();
   internal int ColumnCount;
 
-  public CellStore() {
-    _columnIndex = new ColumnIndex[_colSizeMin];
-  }
-
   ~CellStore() {
     if (_values != null) {
       _values.Clear();
@@ -290,34 +265,6 @@
         : Array.BinarySearch(_columnIndex, 0, ColumnCount, _searchIx);
   }
 
-  internal CellStore<T> Clone() {
-    int row,
-        col;
-    var ret = new CellStore<T>();
-    for (int c = 0; c < ColumnCount; c++) {
-      col = _columnIndex[c].Index;
-      for (int p = 0; p < _columnIndex[c].PageCount; p++) {
-        for (int r = 0; r < _columnIndex[c]._pages[p].RowCount; r++) {
-          row = _columnIndex[c]._pages[p].IndexOffset + _columnIndex[c]._pages[p].Rows[r].Index;
-          ret.SetValue(row, col, _values[_columnIndex[c]._pages[p].Rows[r].IndexPointer]);
-        }
-      }
-    }
-    return ret;
-  }
-
-  internal int Count {
-    get {
-      int count = 0;
-      for (int c = 0; c < ColumnCount; c++) {
-        for (int p = 0; p < _columnIndex[c].PageCount; p++) {
-          count += _columnIndex[c]._pages[p].RowCount;
-        }
-      }
-      return count;
-    }
-  }
-
   internal bool GetDimension(out int fromRow, out int fromCol, out int toRow, out int toCol) {
     if (ColumnCount == 0) {
       fromRow = fromCol = toRow = toCol = 0;
@@ -403,45 +350,6 @@
       return _values[i];
     }
     return default(T);
-    //var col = GetPosition(Column);
-    //if (col >= 0)
-    //{
-    //    var pos = _columnIndex[col].GetPosition(Row);
-    //    if (pos >= 0)
-    //    {
-    //        var pageItem = _columnIndex[col].Pages[pos];
-    //        if (pageItem.MinIndex > Row)
-    //        {
-    //            pos--;
-    //            if (pos < 0)
-    //            {
-    //                return default(T);
-    //            }
-    //            else
-    //            {
-    //                pageItem = _columnIndex[col].Pages[pos];
-    //            }
-    //        }
-    //        short ix = (short)(Row - pageItem.IndexOffset);
-    //        var cellPos = Array.BinarySearch(pageItem.Rows, 0, pageItem.RowCount, new IndexBase() { Index = ix });
-    //        if (cellPos >= 0)
-    //        {
-    //            return _values[pageItem.Rows[cellPos].IndexPointer];
-    //        }
-    //        else //Cell does not exist
-    //        {
-    //            return default(T);
-    //        }
-    //    }
-    //    else //Page does not exist
-    //    {
-    //        return default(T);
-    //    }
-    //}
-    //else //Column does not exist
-    //{
-    //    return default(T);
-    //}
   }
 
   private int GetPointer(int row, int column) {
@@ -492,125 +400,121 @@
   }
 
   internal void SetValue(int row, int column, T value) {
-    lock (_columnIndex) {
-      var col =
-          (_columnIndex != null
-                  && column > 0
-                  && column - 1 < ColumnCount
-                  && column - 1 < _columnIndex.Length
-                  && _columnIndex[column - 1] != null
-                  && _columnIndex[column - 1].Index == column)
-              ? column - 1
-              : Array.BinarySearch(
-                  _columnIndex,
-                  0,
-                  ColumnCount,
-                  new IndexBase {
-                    Index = (short)(column),
-                  });
-      var page = (short)(row >> _pageBits);
-      if (col >= 0) {
-        //var pos = Array.BinarySearch(_columnIndex[col].Pages, 0, _columnIndex[col].Count, new IndexBase() { Index = page });
-        var pos = _columnIndex[col].GetPosition(row);
-        if (pos < 0) {
-          pos = ~pos;
-          if (pos - 1 < 0 || _columnIndex[col]._pages[pos - 1].IndexOffset + _pageSize - 1 < row) {
-            AddPage(_columnIndex[col], pos, page);
-          } else {
-            pos--;
-          }
-        }
-        if (pos >= _columnIndex[col].PageCount) {
+    var col =
+        (_columnIndex != null
+                && column > 0
+                && column - 1 < ColumnCount
+                && column - 1 < _columnIndex.Length
+                && _columnIndex[column - 1] != null
+                && _columnIndex[column - 1].Index == column)
+            ? column - 1
+            : Array.BinarySearch(
+                _columnIndex,
+                0,
+                ColumnCount,
+                new IndexBase {
+                  Index = (short)(column),
+                });
+    var page = (short)(row >> _pageBits);
+    if (col >= 0) {
+      //var pos = Array.BinarySearch(_columnIndex[col].Pages, 0, _columnIndex[col].Count, new IndexBase() { Index = page });
+      var pos = _columnIndex[col].GetPosition(row);
+      if (pos < 0) {
+        pos = ~pos;
+        if (pos - 1 < 0 || _columnIndex[col]._pages[pos - 1].IndexOffset + _pageSize - 1 < row) {
           AddPage(_columnIndex[col], pos, page);
-        }
-        var pageItem = _columnIndex[col]._pages[pos];
-        if (pageItem.IndexOffset > row) {
-          pos--;
-          page--;
-          if (pos < 0) {
-            throw (new("Unexpected error when setting value"));
-          }
-          pageItem = _columnIndex[col]._pages[pos];
-        }
-
-        short ix = (short)(row - ((pageItem.Index << _pageBits) + pageItem.Offset));
-        _searchIx.Index = ix;
-        var cellPos =
-            (pageItem.Rows != null
-                    && ix >= 0
-                    && ix < pageItem.RowCount
-                    && ix < pageItem.Rows.Length
-                    && pageItem.Rows[ix] != null
-                    && pageItem.Rows[ix].Index == ix)
-                ? ix
-                : Array.BinarySearch(pageItem.Rows, 0, pageItem.RowCount, _searchIx);
-        if (cellPos < 0) {
-          cellPos = ~cellPos;
-          AddCell(_columnIndex[col], pos, cellPos, ix, value);
         } else {
-          _values[pageItem.Rows[cellPos].IndexPointer] = value;
+          pos--;
         }
-      } else //Column does not exist
-      {
-        col = ~col;
-        AddColumn(col, column);
-        AddPage(_columnIndex[col], 0, page);
-        short ix = (short)(row - (page << _pageBits));
-        AddCell(_columnIndex[col], 0, 0, ix, value);
       }
+      if (pos >= _columnIndex[col].PageCount) {
+        AddPage(_columnIndex[col], pos, page);
+      }
+      var pageItem = _columnIndex[col]._pages[pos];
+      if (pageItem.IndexOffset > row) {
+        pos--;
+        page--;
+        if (pos < 0) {
+          throw (new("Unexpected error when setting value"));
+        }
+        pageItem = _columnIndex[col]._pages[pos];
+      }
+
+      short ix = (short)(row - ((pageItem.Index << _pageBits) + pageItem.Offset));
+      _searchIx.Index = ix;
+      var cellPos =
+          (pageItem.Rows != null
+                  && ix >= 0
+                  && ix < pageItem.RowCount
+                  && ix < pageItem.Rows.Length
+                  && pageItem.Rows[ix] != null
+                  && pageItem.Rows[ix].Index == ix)
+              ? ix
+              : Array.BinarySearch(pageItem.Rows, 0, pageItem.RowCount, _searchIx);
+      if (cellPos < 0) {
+        cellPos = ~cellPos;
+        AddCell(_columnIndex[col], pos, cellPos, ix, value);
+      } else {
+        _values[pageItem.Rows[cellPos].IndexPointer] = value;
+      }
+    } else //Column does not exist
+    {
+      col = ~col;
+      AddColumn(col, column);
+      AddPage(_columnIndex[col], 0, page);
+      short ix = (short)(row - (page << _pageBits));
+      AddCell(_columnIndex[col], 0, 0, ix, value);
     }
   }
 
   internal void Insert(int fromRow, int fromCol, int rows, int columns) {
-    lock (_columnIndex) {
-      if (columns > 0) {
-        var col = GetPosition(fromCol);
-        if (col < 0) {
-          col = ~col;
-        }
-        for (var c = col; c < ColumnCount; c++) {
-          _columnIndex[c].Index += (short)columns;
-        }
-      } else {
-        var page = fromRow >> _pageBits;
-        for (int c = 0; c < ColumnCount; c++) {
-          var column = _columnIndex[c];
-          var pagePos = column.GetPosition(fromRow);
-          if (pagePos >= 0) {
-            if (fromRow >= column._pages[pagePos].MinIndex
-                && fromRow
-                    <= column._pages[pagePos].MaxIndex) //The row is inside the page
-            {
-              int offset = fromRow - column._pages[pagePos].IndexOffset;
-              var rowPos = column._pages[pagePos].GetPosition(offset);
-              if (rowPos < 0) {
-                rowPos = ~rowPos;
-              }
-              UpdateIndexOffset(column, pagePos, rowPos, fromRow, rows);
-            } else if (column._pages[pagePos].MinIndex > fromRow - 1
-                && pagePos
-                    > 0) //The row is on the page before.
-            {
-              int offset = fromRow - ((page - 1) << _pageBits);
-              var rowPos = column._pages[pagePos - 1].GetPosition(offset);
-              if (rowPos > 0 && pagePos > 0) {
-                UpdateIndexOffset(column, pagePos - 1, rowPos, fromRow, rows);
-              }
-            } else if (column.PageCount >= pagePos + 1) {
-              int offset = fromRow - column._pages[pagePos].IndexOffset;
-              var rowPos = column._pages[pagePos].GetPosition(offset);
-              if (rowPos < 0) {
-                rowPos = ~rowPos;
-              }
-              if (column._pages[pagePos].RowCount > rowPos) {
-                UpdateIndexOffset(column, pagePos, rowPos, fromRow, rows);
-              } else {
-                UpdateIndexOffset(column, pagePos + 1, 0, fromRow, rows);
-              }
+    if (columns > 0) {
+      var col = GetPosition(fromCol);
+      if (col < 0) {
+        col = ~col;
+      }
+      for (var c = col; c < ColumnCount; c++) {
+        _columnIndex[c].Index += (short)columns;
+      }
+    } else {
+      var page = fromRow >> _pageBits;
+      for (int c = 0; c < ColumnCount; c++) {
+        var column = _columnIndex[c];
+        var pagePos = column.GetPosition(fromRow);
+        if (pagePos >= 0) {
+          if (fromRow >= column._pages[pagePos].MinIndex
+              && fromRow
+                  <= column._pages[pagePos].MaxIndex) //The row is inside the page
+          {
+            int offset = fromRow - column._pages[pagePos].IndexOffset;
+            var rowPos = column._pages[pagePos].GetPosition(offset);
+            if (rowPos < 0) {
+              rowPos = ~rowPos;
             }
-          } else {
-            UpdateIndexOffset(column, ~pagePos, 0, fromRow, rows);
+            UpdateIndexOffset(column, pagePos, rowPos, fromRow, rows);
+          } else if (column._pages[pagePos].MinIndex > fromRow - 1
+              && pagePos
+                  > 0) //The row is on the page before.
+          {
+            int offset = fromRow - ((page - 1) << _pageBits);
+            var rowPos = column._pages[pagePos - 1].GetPosition(offset);
+            if (rowPos > 0 && pagePos > 0) {
+              UpdateIndexOffset(column, pagePos - 1, rowPos, fromRow, rows);
+            }
+          } else if (column.PageCount >= pagePos + 1) {
+            int offset = fromRow - column._pages[pagePos].IndexOffset;
+            var rowPos = column._pages[pagePos].GetPosition(offset);
+            if (rowPos < 0) {
+              rowPos = ~rowPos;
+            }
+            if (column._pages[pagePos].RowCount > rowPos) {
+              UpdateIndexOffset(column, pagePos, rowPos, fromRow, rows);
+            } else {
+              UpdateIndexOffset(column, pagePos + 1, 0, fromRow, rows);
+            }
           }
+        } else {
+          UpdateIndexOffset(column, ~pagePos, 0, fromRow, rows);
         }
       }
     }
@@ -625,93 +529,87 @@
   }
 
   internal void Delete(int fromRow, int fromCol, int rows, int columns, bool shift) {
-    lock (_columnIndex) {
-      if (columns > 0 && fromRow == 1 && rows >= ExcelPackage.MaxRows) {
-        DeleteColumns(fromCol, columns, shift);
-      } else {
-        var toCol = fromCol + columns - 1;
-        var pageFromRow = fromRow >> _pageBits;
-        for (int c = 0; c < ColumnCount; c++) {
-          var column = _columnIndex[c];
-          if (column.Index >= fromCol) {
-            if (column.Index > toCol) {
-              break;
+    if (columns > 0 && fromRow == 1 && rows >= ExcelPackage.MaxRows) {
+      DeleteColumns(fromCol, columns, shift);
+    } else {
+      var toCol = fromCol + columns - 1;
+      var pageFromRow = fromRow >> _pageBits;
+      for (int c = 0; c < ColumnCount; c++) {
+        var column = _columnIndex[c];
+        if (column.Index >= fromCol) {
+          if (column.Index > toCol) {
+            break;
+          }
+          var pagePos = column.GetPosition(fromRow);
+          if (pagePos < 0) {
+            pagePos = ~pagePos;
+          }
+          if (pagePos < column.PageCount) {
+            var page = column._pages[pagePos];
+            if (shift
+                && page.RowCount > 0
+                && page.MinIndex > fromRow
+                && page.MaxIndex >= fromRow + rows) {
+              var o = page.MinIndex - fromRow;
+              if (o < rows) {
+                rows -= o;
+                page.Offset -= o;
+                UpdatePageOffset(column, pagePos, o);
+              } else {
+                page.Offset -= rows;
+                UpdatePageOffset(column, pagePos, rows);
+                continue;
+              }
             }
-            var pagePos = column.GetPosition(fromRow);
-            if (pagePos < 0) {
-              pagePos = ~pagePos;
-            }
-            if (pagePos < column.PageCount) {
-              var page = column._pages[pagePos];
-              if (shift
-                  && page.RowCount > 0
-                  && page.MinIndex > fromRow
-                  && page.MaxIndex >= fromRow + rows) {
-                var o = page.MinIndex - fromRow;
-                if (o < rows) {
-                  rows -= o;
-                  page.Offset -= o;
-                  UpdatePageOffset(column, pagePos, o);
-                } else {
-                  page.Offset -= rows;
-                  UpdatePageOffset(column, pagePos, rows);
-                  continue;
+            if (page.RowCount > 0
+                && page.MinIndex <= fromRow + rows - 1
+                && page.MaxIndex
+                    >= fromRow) //The row is inside the page
+            {
+              var endRow = fromRow + rows;
+              var delEndRow = DeleteCells(column._pages[pagePos], fromRow, endRow, shift);
+              if (shift && delEndRow != fromRow) {
+                UpdatePageOffset(column, pagePos, delEndRow - fromRow);
+              }
+              if (endRow > delEndRow
+                  && pagePos < column.PageCount
+                  && column._pages[pagePos].MinIndex < endRow) {
+                pagePos = (delEndRow == fromRow ? pagePos : pagePos + 1);
+                var rowsLeft = DeletePage(
+                    shift ? fromRow : delEndRow,
+                    endRow - delEndRow,
+                    column,
+                    pagePos,
+                    shift);
+                //if (shift) UpdatePageOffset(column, pagePos, endRow - fromRow - rowsLeft);
+                if (rowsLeft > 0) {
+                  var fr = shift ? fromRow : endRow - rowsLeft;
+                  pagePos = column.GetPosition(fr);
+                  DeleteCells(column._pages[pagePos], fr, shift ? fr + rowsLeft : endRow, shift);
+                  if (shift) {
+                    UpdatePageOffset(column, pagePos, rowsLeft);
+                  }
                 }
               }
-              if (page.RowCount > 0
-                  && page.MinIndex <= fromRow + rows - 1
-                  && page.MaxIndex
-                      >= fromRow) //The row is inside the page
-              {
-                var endRow = fromRow + rows;
-                var delEndRow = DeleteCells(column._pages[pagePos], fromRow, endRow, shift);
-                if (shift && delEndRow != fromRow) {
-                  UpdatePageOffset(column, pagePos, delEndRow - fromRow);
+            } else if (pagePos > 0
+                && column._pages[pagePos].IndexOffset
+                    > fromRow) //The row is on the page before.
+            {
+              int offset = fromRow + rows - 1 - ((pageFromRow - 1) << _pageBits);
+              var rowPos = column._pages[pagePos - 1].GetPosition(offset);
+              if (rowPos > 0 && pagePos > 0) {
+                if (shift) {
+                  UpdateIndexOffset(column, pagePos - 1, rowPos, fromRow + rows - 1, -rows);
                 }
-                if (endRow > delEndRow
-                    && pagePos < column.PageCount
-                    && column._pages[pagePos].MinIndex < endRow) {
-                  pagePos = (delEndRow == fromRow ? pagePos : pagePos + 1);
-                  var rowsLeft = DeletePage(
-                      shift ? fromRow : delEndRow,
-                      endRow - delEndRow,
-                      column,
-                      pagePos,
-                      shift);
-                  //if (shift) UpdatePageOffset(column, pagePos, endRow - fromRow - rowsLeft);
-                  if (rowsLeft > 0) {
-                    var fr = shift ? fromRow : endRow - rowsLeft;
-                    pagePos = column.GetPosition(fr);
-                    delEndRow = DeleteCells(
-                        column._pages[pagePos],
-                        fr,
-                        shift ? fr + rowsLeft : endRow,
-                        shift);
-                    if (shift) {
-                      UpdatePageOffset(column, pagePos, rowsLeft);
-                    }
-                  }
-                }
-              } else if (pagePos > 0
-                  && column._pages[pagePos].IndexOffset
-                      > fromRow) //The row is on the page before.
-              {
-                int offset = fromRow + rows - 1 - ((pageFromRow - 1) << _pageBits);
-                var rowPos = column._pages[pagePos - 1].GetPosition(offset);
-                if (rowPos > 0 && pagePos > 0) {
-                  if (shift) {
-                    UpdateIndexOffset(column, pagePos - 1, rowPos, fromRow + rows - 1, -rows);
-                  }
-                }
-              } else {
-                if (shift && pagePos + 1 < column.PageCount) {
-                  UpdateIndexOffset(
-                      column,
-                      pagePos + 1,
-                      0,
-                      column._pages[pagePos + 1].MinIndex,
-                      -rows);
-                }
+              }
+            } else {
+              if (shift && pagePos + 1 < column.PageCount) {
+                UpdateIndexOffset(
+                    column,
+                    pagePos + 1,
+                    0,
+                    column._pages[pagePos + 1].MinIndex,
+                    -rows);
               }
             }
           }
@@ -737,31 +635,12 @@
           || Math.Abs(column._pages[pagePos].Rows[column._pages[pagePos].RowCount - 1].Index)
               > _pageSizeMax) //Split or Merge???
       {
-        rows = ResetPageOffset(column, pagePos, rows);
-        ////MergePages
-        //if (column.Pages[pagePos - 1].Index + 1 == column.Pages[pagePos].Index)
-        //{
-        //    if (column.Pages[pagePos].IndexOffset + column.Pages[pagePos].Rows[column.Pages[pagePos].RowCount - 1].Index + rows -
-        //        column.Pages[pagePos - 1].IndexOffset + column.Pages[pagePos - 1].Rows[0].Index <= PageSize)
-        //    {
-        //        //Merge
-        //        MergePage(column, pagePos - 1, -rows);
-        //    }
-        //    else
-        //    {
-        //        //Split
-        //    }
-        //}
-        //rows -= PageSize;
-        //for (int p = pagePos; p < column.PageCount; p++)
-        //{
-        //    column.Pages[p].Index -= 1;
-        //}
+        ResetPageOffset(column, pagePos, rows);
       }
     }
   }
 
-  private int ResetPageOffset(ColumnIndex column, int pagePos, int rows) {
+  private void ResetPageOffset(ColumnIndex column, int pagePos, int rows) {
     PageIndex fromPage = column._pages[pagePos];
     PageIndex toPage;
     short pageAdd = 0;
@@ -775,14 +654,6 @@
                 + toPage.Rows[0].Index
             <= _pageSizeMax) {
           MergePage(column, pagePos - 1);
-          //var newPage = new PageIndex(toPage, 0, GetSize(fromPage.RowCount + toPage.RowCount));
-          //newPage.RowCount = fromPage.RowCount + fromPage.RowCount;
-          //Array.Copy(toPage.Rows, 0, newPage.Rows, 0, toPage.RowCount);
-          //Array.Copy(fromPage.Rows, 0, newPage.Rows, toPage.RowCount, fromPage.RowCount);
-          //for (int r = toPage.RowCount; r < newPage.RowCount; r++)
-          //{
-          //    newPage.Rows[r].Index += (short)(fromPage.IndexOffset - toPage.IndexOffset);
-          //}
         }
       } else //No page after
       {
@@ -797,7 +668,6 @@
         fromPage.Offset += _pageSize;
       }
     }
-    return rows;
   }
 
   private int DeletePage(int fromRow, int rows, ColumnIndex column, int pagePos, bool shift) {
@@ -841,7 +711,6 @@
     return rows;
   }
 
-  ///
   private int DeleteCells(PageIndex page, int fromRow, int toRow, bool shift) {
     var fromPos = page.GetPosition(fromRow - (page.IndexOffset));
     if (fromPos < 0) {
@@ -908,13 +777,10 @@
     }
 
     if (_columnIndex[fPos].Index >= fromCol && _columnIndex[fPos].Index <= fromCol + columns) {
-      //if (_columnIndex[fPos].Index < ColumnCount)
-      //{
       if (tPos < ColumnCount) {
         Array.Copy(_columnIndex, tPos, _columnIndex, fPos, ColumnCount - tPos);
       }
       ColumnCount -= (tPos - fPos);
-      //}
     }
     if (shift) {
       for (var c = fPos; c < ColumnCount; c++) {
@@ -1243,22 +1109,6 @@
     ColumnCount++;
   }
 
-  private int _colPos = -1,
-      _row;
-
-  public ulong Current => ((ulong)_row << 32) | (uint)(_columnIndex[_colPos].Index);
-
-  //object IEnumerator.Current
-  //{
-  //    get
-  //    {
-  //        return GetValue(_row+1, _columnIndex[_colPos].Index);
-  //    }
-  //}
-  public bool MoveNext() {
-    return GetNextCell(ref _row, ref _colPos, 0, ExcelPackage.MaxRows, ExcelPackage.MaxColumns);
-  }
-
   internal bool NextCell(ref int row, ref int col) {
     return NextCell(ref row, ref col, 0, 0, ExcelPackage.MaxRows, ExcelPackage.MaxColumns);
   }
@@ -1379,47 +1229,11 @@
     return GetNextCell(ref row, ref colPos, startColPos, endRow, endColPos);
   }
 
-  internal bool GetNextCell(
-      ref int row,
-      ref int colPos,
-      int startColPos,
-      int endRow,
-      int endColPos,
-      ref int[] pagePos,
-      ref int[] cellPos) {
-    if (colPos == endColPos) {
-      colPos = startColPos;
-      row++;
-    } else {
-      colPos++;
-    }
-
-    if (pagePos[colPos] < 0) {
-      if (pagePos[colPos] == -1) {
-        pagePos[colPos] = _columnIndex[colPos].GetPosition(row);
-      }
-    } else if (_columnIndex[colPos]._pages[pagePos[colPos]].RowCount <= row) {
-      if (_columnIndex[colPos].PageCount > pagePos[colPos]) {
-        pagePos[colPos]++;
-      } else {
-        pagePos[colPos] = -2;
-      }
-    }
-
-    var r =
-        _columnIndex[colPos]._pages[pagePos[colPos]].IndexOffset
-            + _columnIndex[colPos]._pages[pagePos[colPos]].Rows[cellPos[colPos]].Index;
-    if (r == row) {
-      row = r;
-    }
-    return true;
-  }
-
   internal bool PrevCell(ref int row, ref int col) {
     return PrevCell(ref row, ref col, 0, 0, ExcelPackage.MaxRows, ExcelPackage.MaxColumns);
   }
 
-  internal bool PrevCell(
+  private bool PrevCell(
       ref int row,
       ref int col,
       int minRow,
@@ -1542,28 +1356,10 @@
     colPos = ColumnCount;
     row--;
     if (row < startRow) {
-      Reset();
       return false;
     }
     return GetPrevCell(ref colPos, ref row, startRow, startColPos, endColPos);
   }
-
-  public void Reset() {
-    _colPos = -1;
-    _row = 0;
-  }
-
-  //public IEnumerator<ulong> GetEnumerator()
-  //{
-  //    this.Reset();
-  //    return this;
-  //}
-
-  //IEnumerator IEnumerable.GetEnumerator()
-  //{
-  //    this.Reset();
-  //    return this;
-  //}
 }
 
 internal class CellsStoreEnumerator<T> : IEnumerable<T>, IEnumerator<T> {
@@ -1639,29 +1435,14 @@
   }
 
   internal T Value {
-    get {
-      lock (_cellStore) {
-        return _cellStore.GetValue(row, Column);
-      }
-    }
-    set {
-      lock (_cellStore) {
-        _cellStore.SetValue(row, Column, value);
-      }
-    }
+    get => _cellStore.GetValue(row, Column);
+    set => _cellStore.SetValue(row, Column, value);
   }
 
   internal bool Next() {
-    //return _cellStore.GetNextCell(ref row, ref colPos, minColPos, maxRow, maxColPos);
     return _cellStore.GetNextCell(ref row, ref colPos, minColPos, maxRow, maxColPos);
   }
 
-  internal bool Previous() {
-    lock (_cellStore) {
-      return _cellStore.GetPrevCell(ref row, ref colPos, minRow, minColPos, maxColPos);
-    }
-  }
-
   public string CellAddress => ExcelCellBase.GetAddress(Row, Column);
 
   public IEnumerator<T> GetEnumerator() {
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingCollection.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingCollection.cs
index 08605aa..8717d2e 100644
--- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingCollection.cs
+++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingCollection.cs
@@ -31,6 +31,7 @@
 
 using System.Collections;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.Xml;
 using OfficeOpenXml.ConditionalFormatting.Contracts;
 using OfficeOpenXml.Utils;
@@ -68,6 +69,9 @@
   private readonly List<IExcelConditionalFormattingRule> _rules = new();
   private readonly ExcelWorksheet _worksheet;
 
+  protected override ImmutableArray<string> SchemaNodeOrder =>
+    ExcelWorksheet.WorksheetSchemaNodeOrder;
+
   /// <summary>
   /// Initialize the <see cref="ExcelConditionalFormattingCollection"/>
   /// </summary>
@@ -77,7 +81,6 @@
     Require.Argument(worksheet).IsNotNull("worksheet");
 
     _worksheet = worksheet;
-    SchemaNodeOrder = _worksheet.SchemaNodeOrder;
 
     // Look for all the <conditionalFormatting>
     var conditionalFormattingNodes = TopNode.SelectNodes(
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingColorScaleValue.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingColorScaleValue.cs
index 84d8fb1..59376e7 100644
--- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingColorScaleValue.cs
+++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingColorScaleValue.cs
@@ -30,6 +30,7 @@
  *******************************************************************************/
 
 using System;
+using System.Collections.Immutable;
 using System.Xml;
 using OfficeOpenXml.Utils;
 
@@ -44,6 +45,11 @@
   private eExcelConditionalFormattingRuleType _ruleType;
   private readonly ExcelWorksheet _worksheet;
 
+  protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
+    ExcelConditionalFormattingConstants.Nodes._cfvo,
+    ExcelConditionalFormattingConstants.Nodes._color,
+  ];
+
   /// <summary>
   /// Initialize the cfvo (§18.3.1.11) node
   /// </summary>
@@ -78,12 +84,6 @@
     // Save the worksheet for private methods to use
     _worksheet = worksheet;
 
-    // Schema order list
-    SchemaNodeOrder = new[] {
-      ExcelConditionalFormattingConstants.Nodes._cfvo,
-      ExcelConditionalFormattingConstants.Nodes._color,
-    };
-
     // Check if the parent does not exists
     if (itemElementNode == null) {
       // Get the parent node path by the rule type
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingIconDatabarValue.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingIconDatabarValue.cs
index bffef35..c38d095 100644
--- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingIconDatabarValue.cs
+++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingIconDatabarValue.cs
@@ -30,6 +30,7 @@
  *******************************************************************************/
 
 using System;
+using System.Collections.Immutable;
 using System.Drawing;
 using System.Globalization;
 using System.Xml;
@@ -117,6 +118,10 @@
     Formula = formula;
   }
 
+  protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
+    ExcelConditionalFormattingConstants.Nodes._cfvo,
+  ];
+
   /// <summary>
   /// Initialize the cfvo (§18.3.1.11) node
   /// </summary>
@@ -139,9 +144,6 @@
     // Save the worksheet for private methods to use
     _worksheet = worksheet;
 
-    // Schema order list
-    SchemaNodeOrder = new[] { ExcelConditionalFormattingConstants.Nodes._cfvo };
-
     //Check if the parent does not exists
     if (itemElementNode == null) {
       // Get the parent node path by the rule type
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingOperatorType.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingOperatorType.cs
index cf0fae8..c513d8d 100644
--- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingOperatorType.cs
+++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingOperatorType.cs
@@ -29,8 +29,6 @@
  * Eyal Seagull    Conditional Formatting Adaption    2012-04-17
  *******************************************************************************/
 
-using System;
-
 namespace OfficeOpenXml.ConditionalFormatting;
 
 /// <summary>
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingRuleType.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingRuleType.cs
index b1c6b32..8168caa 100644
--- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingRuleType.cs
+++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingRuleType.cs
@@ -29,7 +29,6 @@
  * Eyal Seagull    Conditional Formatting Adaption    2012-04-03
  *******************************************************************************/
 
-using System;
 using System.Xml;
 
 namespace OfficeOpenXml.ConditionalFormatting;
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingTimePeriodType.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingTimePeriodType.cs
index 2051e7b..c93a82d 100644
--- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingTimePeriodType.cs
+++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingTimePeriodType.cs
@@ -29,8 +29,6 @@
  * Eyal Seagull    Conditional Formatting Adaption    2012-04-17
  *******************************************************************************/
 
-using System;
-
 namespace OfficeOpenXml.ConditionalFormatting;
 
 /// <summary>
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingValueObjectType.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingValueObjectType.cs
index 9d28645..22d9356 100644
--- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingValueObjectType.cs
+++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingValueObjectType.cs
@@ -29,7 +29,6 @@
  * Eyal Seagull    Conditional Formatting Adaption    2012-04-03
  *******************************************************************************/
 
-using System;
 using System.Xml;
 
 namespace OfficeOpenXml.ConditionalFormatting;
diff --git a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingDataBar.cs b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingDataBar.cs
index 8ca4be2..6c37773 100644
--- a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingDataBar.cs
+++ b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingDataBar.cs
@@ -29,7 +29,7 @@
  * Eyal Seagull        Added       		  2012-04-03
  *******************************************************************************/
 
-using System.Globalization;
+using System.Collections.Immutable;
 using System.Xml;
 using OfficeOpenXml.ConditionalFormatting.Contracts;
 
@@ -41,6 +41,8 @@
 public class ExcelConditionalFormattingDataBar
     : ExcelConditionalFormattingRule,
       IExcelConditionalFormattingDataBarGroup {
+  protected override ImmutableArray<string> SchemaNodeOrder { get; } = ["cfvo", "color"];
+
   /// <summary>
   ///
   /// </summary>
@@ -64,7 +66,6 @@
           worksheet,
           itemElementNode,
           namespaceManager ?? worksheet.NameSpaceManager) {
-    SchemaNodeOrder = new[] { "cfvo", "color" };
     //Create the <dataBar> node inside the <cfRule> node
     if (itemElementNode != null && itemElementNode.HasChildNodes) {
       bool high = false;
diff --git a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingRule.cs b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingRule.cs
index a85ebaa..082da63 100644
--- a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingRule.cs
+++ b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingRule.cs
@@ -30,6 +30,7 @@
  *******************************************************************************/
 
 using System;
+using System.Collections.Immutable;
 using System.Xml;
 using OfficeOpenXml.ConditionalFormatting.Contracts;
 using OfficeOpenXml.Style.Dxf;
@@ -50,6 +51,9 @@
   /// </summary>
   private static bool _changingPriority;
 
+  protected override ImmutableArray<string> SchemaNodeOrder =>
+    ExcelWorksheet.WorksheetSchemaNodeOrder;
+
   /// <summary>
   /// Initialize the <see cref="ExcelConditionalFormattingRule"/>
   /// </summary>
@@ -73,7 +77,6 @@
 
     _type = type;
     _worksheet = worksheet;
-    SchemaNodeOrder = _worksheet.SchemaNodeOrder;
 
     if (itemElementNode == null) {
       // Create/Get the <cfRule> inside <conditionalFormatting>
diff --git a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeColorScale.cs b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeColorScale.cs
index 5700e10..23c8d02 100644
--- a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeColorScale.cs
+++ b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeColorScale.cs
@@ -29,7 +29,6 @@
  * Eyal Seagull        Added       		  2012-04-03
  *******************************************************************************/
 
-using System.Drawing;
 using System.Xml;
 using OfficeOpenXml.ConditionalFormatting.Contracts;
 
diff --git a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingTwoColorScale.cs b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingTwoColorScale.cs
index ad7d79f..691f0dd 100644
--- a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingTwoColorScale.cs
+++ b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingTwoColorScale.cs
@@ -29,7 +29,6 @@
  * Eyal Seagull        Added       		  2012-04-03
  *******************************************************************************/
 
-using System.Drawing;
 using System.Xml;
 using OfficeOpenXml.ConditionalFormatting.Contracts;
 
diff --git a/EPPlus/DataValidation/ExcelDataValidation.cs b/EPPlus/DataValidation/ExcelDataValidation.cs
index 88a60de..1e5f445 100644
--- a/EPPlus/DataValidation/ExcelDataValidation.cs
+++ b/EPPlus/DataValidation/ExcelDataValidation.cs
@@ -31,6 +31,7 @@
  *******************************************************************************/
 
 using System;
+using System.Collections.Immutable;
 using System.Globalization;
 using System.Text.RegularExpressions;
 using System.Xml;
@@ -59,6 +60,22 @@
   protected readonly string _formula1Path = "d:formula1";
   protected readonly string _formula2Path = "d:formula2";
 
+  protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
+    "type",
+    "errorStyle",
+    "operator",
+    "allowBlank",
+    "showInputMessage",
+    "showErrorMessage",
+    "errorTitle",
+    "error",
+    "promptTitle",
+    "prompt",
+    "sqref",
+    "formula1",
+    "formula2",
+  ];
+
   internal ExcelDataValidation(
       ExcelWorksheet worksheet,
       string address,
@@ -112,26 +129,6 @@
     TopNode = itemElementNode;
     ValidationType = validationType;
     Address = new(address);
-    Init();
-  }
-
-  private void Init() {
-    // set schema node order
-    SchemaNodeOrder = new[] {
-      "type",
-      "errorStyle",
-      "operator",
-      "allowBlank",
-      "showInputMessage",
-      "showErrorMessage",
-      "errorTitle",
-      "error",
-      "promptTitle",
-      "prompt",
-      "sqref",
-      "formula1",
-      "formula2",
-    };
   }
 
   private string CheckAndFixRangeAddress(string address) {
diff --git a/EPPlus/DataValidation/ExcelDataValidationCollection.cs b/EPPlus/DataValidation/ExcelDataValidationCollection.cs
index 4518881..3c186a5 100644
--- a/EPPlus/DataValidation/ExcelDataValidationCollection.cs
+++ b/EPPlus/DataValidation/ExcelDataValidationCollection.cs
@@ -36,6 +36,7 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.Xml;
 using OfficeOpenXml.DataValidation.Contracts;
 using OfficeOpenXml.Utils;
@@ -75,6 +76,9 @@
       "{0}/d:dataValidation",
       _dataValidationPath);
 
+  protected override ImmutableArray<string> SchemaNodeOrder =>
+    ExcelWorksheet.WorksheetSchemaNodeOrder;
+
   /// <summary>
   /// Constructor
   /// </summary>
@@ -83,7 +87,6 @@
       : base(worksheet.NameSpaceManager, worksheet.WorksheetXml.DocumentElement) {
     Require.Argument(worksheet).IsNotNull("worksheet");
     _worksheet = worksheet;
-    SchemaNodeOrder = worksheet.SchemaNodeOrder;
 
     // check existing nodes and load them
     var dataValidationNodes = worksheet.WorksheetXml.SelectNodes(
diff --git a/EPPlus/Drawing/Vml/ExcelVmlDrawingBase.cs b/EPPlus/Drawing/Vml/ExcelVmlDrawingBase.cs
index df934c8..ade7974 100644
--- a/EPPlus/Drawing/Vml/ExcelVmlDrawingBase.cs
+++ b/EPPlus/Drawing/Vml/ExcelVmlDrawingBase.cs
@@ -30,6 +30,7 @@
  * Jan Källman		License changed GPL-->LGPL 2011-12-16
  *******************************************************************************/
 
+using System.Collections.Immutable;
 using System.Xml;
 
 namespace OfficeOpenXml.Drawing.Vml;
@@ -70,28 +71,28 @@
 /// Drawing object used for comments
 /// </summary>
 public class ExcelVmlDrawingBase : XmlHelper {
+  protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
+    "fill",
+    "stroke",
+    "shadow",
+    "path",
+    "textbox",
+    "ClientData",
+    "MoveWithCells",
+    "SizeWithCells",
+    "Anchor",
+    "Locked",
+    "AutoFill",
+    "LockText",
+    "TextHAlign",
+    "TextVAlign",
+    "Row",
+    "Column",
+    "Visible",
+  ];
+
   internal ExcelVmlDrawingBase(XmlNode topNode, XmlNamespaceManager ns)
-      : base(ns, topNode) {
-    SchemaNodeOrder = new[] {
-      "fill",
-      "stroke",
-      "shadow",
-      "path",
-      "textbox",
-      "ClientData",
-      "MoveWithCells",
-      "SizeWithCells",
-      "Anchor",
-      "Locked",
-      "AutoFill",
-      "LockText",
-      "TextHAlign",
-      "TextVAlign",
-      "Row",
-      "Column",
-      "Visible",
-    };
-  }
+      : base(ns, topNode) {}
 
   public string Id {
     get => GetXmlNodeString("@id");
diff --git a/EPPlus/Drawing/Vml/ExcelVmlDrawingBaseCollection.cs b/EPPlus/Drawing/Vml/ExcelVmlDrawingBaseCollection.cs
index 912214e..a0bd7e1 100644
--- a/EPPlus/Drawing/Vml/ExcelVmlDrawingBaseCollection.cs
+++ b/EPPlus/Drawing/Vml/ExcelVmlDrawingBaseCollection.cs
@@ -38,7 +38,6 @@
 
 public class ExcelVmlDrawingBaseCollection {
   internal ExcelVmlDrawingBaseCollection(ExcelPackage pck, ExcelWorksheet ws, Uri uri) {
-    VmlDrawingXml = new();
     VmlDrawingXml.PreserveWhitespace = false;
 
     NameTable nt = new NameTable();
@@ -55,7 +54,8 @@
     }
   }
 
-  internal XmlDocument VmlDrawingXml { get; set; }
+  internal XmlDocument VmlDrawingXml { get; set; } = new();
+
   internal Uri Uri { get; set; }
   internal string RelId { get; set; }
   internal ZipPackagePart Part { get; set; }
diff --git a/EPPlus/Drawing/Vml/ExcelVmlDrawingComment.cs b/EPPlus/Drawing/Vml/ExcelVmlDrawingComment.cs
index bd137cd..7eecb26 100644
--- a/EPPlus/Drawing/Vml/ExcelVmlDrawingComment.cs
+++ b/EPPlus/Drawing/Vml/ExcelVmlDrawingComment.cs
@@ -30,6 +30,7 @@
  * Jan Källman		License changed GPL-->LGPL 2011-12-16
  *******************************************************************************/
 
+using System.Collections.Immutable;
 using System.Xml;
 
 namespace OfficeOpenXml.Drawing.Vml;
@@ -38,28 +39,29 @@
 /// Drawing object used for comments
 /// </summary>
 public class ExcelVmlDrawingComment : ExcelVmlDrawingBase, IRangeId {
+  protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
+    "fill",
+    "stroke",
+    "shadow",
+    "path",
+    "textbox",
+    "ClientData",
+    "MoveWithCells",
+    "SizeWithCells",
+    "Anchor",
+    "Locked",
+    "AutoFill",
+    "LockText",
+    "TextHAlign",
+    "TextVAlign",
+    "Row",
+    "Column",
+    "Visible",
+  ];
+
   internal ExcelVmlDrawingComment(XmlNode topNode, ExcelRangeBase range, XmlNamespaceManager ns)
       : base(topNode, ns) {
     Range = range;
-    SchemaNodeOrder = new[] {
-      "fill",
-      "stroke",
-      "shadow",
-      "path",
-      "textbox",
-      "ClientData",
-      "MoveWithCells",
-      "SizeWithCells",
-      "Anchor",
-      "Locked",
-      "AutoFill",
-      "LockText",
-      "TextHAlign",
-      "TextVAlign",
-      "Row",
-      "Column",
-      "Visible",
-    };
   }
 
   internal ExcelRangeBase Range { get; set; }
diff --git a/EPPlus/Drawing/Vml/ExcelVmlDrawingPosition.cs b/EPPlus/Drawing/Vml/ExcelVmlDrawingPosition.cs
index 294f9b2..ec28659 100644
--- a/EPPlus/Drawing/Vml/ExcelVmlDrawingPosition.cs
+++ b/EPPlus/Drawing/Vml/ExcelVmlDrawingPosition.cs
@@ -30,7 +30,6 @@
  * Jan Källman		License changed GPL-->LGPL 2011-12-16
  *******************************************************************************/
 
-using System;
 using System.Xml;
 
 namespace OfficeOpenXml.Drawing.Vml;
diff --git a/EPPlus/EPPlusSDK.csproj b/EPPlus/EPPlusSDK.csproj
index 2cdc079..2a2e866 100644
--- a/EPPlus/EPPlusSDK.csproj
+++ b/EPPlus/EPPlusSDK.csproj
@@ -4,6 +4,6 @@
     <RootNamespace>OfficeOpenXml</RootNamespace>
     <AssemblyName>EPPlus</AssemblyName>
     <PackageId>Appsheet.EPPlus</PackageId>
-    <Version>1.0.5</Version>
+    <Version>1.0.6</Version>
   </PropertyGroup>
 </Project>
diff --git a/EPPlus/ExcelAddress.cs b/EPPlus/ExcelAddress.cs
index 9a5f20d..51ad999 100644
--- a/EPPlus/ExcelAddress.cs
+++ b/EPPlus/ExcelAddress.cs
@@ -33,7 +33,6 @@
 using System;
 using System.Collections.Generic;
 using System.Globalization;
-using System.Text.RegularExpressions;
 
 namespace OfficeOpenXml;
 
@@ -837,7 +836,6 @@
   }
 
   internal static AddressType IsValid(string address) {
-    double d;
     if (address == "#REF!") {
       return AddressType.Invalid;
     }
@@ -845,15 +843,14 @@
         address,
         NumberStyles.Any,
         CultureInfo.InvariantCulture,
-        out d)) //A double, no valid address
+        out _)) //A double, no valid address
     {
       return AddressType.Invalid;
     }
     if (IsFormula(address)) {
       return AddressType.Formula;
     }
-    string ws;
-    if (SplitAddress(address, out var wb, out ws, out var intAddress)) {
+    if (SplitAddress(address, out var wb, out _, out var intAddress)) {
       if (intAddress.Contains(
           "[")) //Table reference
       {
@@ -868,72 +865,6 @@
       return string.IsNullOrEmpty(wb) ? AddressType.InternalName : AddressType.ExternalName;
     }
     return AddressType.Invalid;
-
-    //if(string.IsNullOrEmpty(wb));
-    //ExcelAddress a = new ExcelAddress(Address);
-    //if (Address.IndexOf('!') > 0)
-    //{
-    //    string[] split = Address.Split('!');
-    //    if (split.Length == 2)
-    //    {
-    //        ws = split[0];
-    //        Address = split[1];
-    //    }
-    //    else if (split.Length == 3 && split[1] == "#REF" && split[2] == "")
-    //    {
-    //        ws = split[0];
-    //        Address = "#REF!";
-    //        if (ws.StartsWith("[") && ws.IndexOf("]") > 1)
-    //        {
-    //            return AddressType.ExternalAddress;
-    //        }
-    //        else
-    //        {
-    //            return AddressType.InternalAddress;
-    //        }
-    //    }
-    //    else
-    //    {
-    //        return AddressType.Invalid;
-    //    }
-    //}
-    //int _fromRow, column, _toRow, _toCol;
-    //if (ExcelAddressBase.GetRowColFromAddress(Address, out _fromRow, out column, out _toRow, out _toCol))
-    //{
-    //    if (_fromRow > 0 && column > 0 && _toRow <= ExcelPackage.MaxRows && _toCol <= ExcelPackage.MaxColumns)
-    //    {
-    //        if (ws.StartsWith("[") && ws.IndexOf("]") > 1)
-    //        {
-    //            return AddressType.ExternalAddress;
-    //        }
-    //        else
-    //        {
-    //            return AddressType.InternalAddress;
-    //        }
-    //    }
-    //    else
-    //    {
-    //        return AddressType.Invalid;
-    //    }
-    //}
-    //else
-    //{
-    //    if(IsValidName(Address))
-    //    {
-    //        if (ws.StartsWith("[") && ws.IndexOf("]") > 1)
-    //        {
-    //            return AddressType.ExternalName;
-    //        }
-    //        else
-    //        {
-    //            return AddressType.InternalName;
-    //        }
-    //    }
-    //    else
-    //    {
-    //        return AddressType.Invalid;
-    //    }
-    //}
   }
 
   private static bool IsAddress(string intAddress) {
@@ -1040,12 +971,8 @@
 
   public int Columns => _toCol - _fromCol + 1;
 
-  internal bool IsMultiCell() {
-    return (_fromRow < _fromCol || _fromCol < _toCol);
-  }
-
   internal static String GetWorkbookPart(string address) {
-    var ix = 0;
+    int ix;
     if (address[0] == '[') {
       ix = address.IndexOf(']') + 1;
       if (ix > 0) {
@@ -1055,11 +982,6 @@
     return "";
   }
 
-  internal static string GetWorksheetPart(string address, string defaultWorkSheet) {
-    int ix = 0;
-    return GetWorksheetPart(address, defaultWorkSheet, ref ix);
-  }
-
   internal static string GetWorksheetPart(string address, string defaultWorkSheet, ref int endIx) {
     if (address == "") {
       return defaultWorkSheet;
@@ -1081,18 +1003,6 @@
     return defaultWorkSheet;
   }
 
-  internal static string GetAddressPart(string address) {
-    var ix = 0;
-    GetWorksheetPart(address, "", ref ix);
-    if (ix < address.Length) {
-      if (address[ix] == '!') {
-        return address.Substring(ix + 1);
-      }
-      return "";
-    }
-    return "";
-  }
-
   internal static void SplitAddress(
       string fullAddress,
       out string wb,
@@ -1115,9 +1025,7 @@
 
   private static string GetString(string address, int ix, out int endIx) {
     var strIx = address.IndexOf("''");
-    var prevStrIx = ix;
     while (strIx > -1) {
-      prevStrIx = strIx;
       strIx = address.IndexOf("''");
     }
     endIx = address.IndexOf("'");
@@ -1189,8 +1097,6 @@
       _fromColFixed,
       _toColFixed;
 
-  internal ExcelFormulaAddress() {}
-
   public ExcelFormulaAddress(int fromRow, int fromCol, int toRow, int toColumn)
       : base(fromRow, fromCol, toRow, toColumn) {
     _ws = "";
@@ -1201,24 +1107,6 @@
     SetFixed();
   }
 
-  internal ExcelFormulaAddress(string ws, string address)
-      : base(address) {
-    if (string.IsNullOrEmpty(_ws)) {
-      _ws = ws;
-    }
-    SetFixed();
-  }
-
-  internal ExcelFormulaAddress(string ws, string address, bool isName)
-      : base(address, isName) {
-    if (string.IsNullOrEmpty(_ws)) {
-      _ws = ws;
-    }
-    if (!isName) {
-      SetFixed();
-    }
-  }
-
   private void SetFixed() {
     if (Address.IndexOf("[") >= 0) {
       return;
diff --git a/EPPlus/ExcelCellBase.cs b/EPPlus/ExcelCellBase.cs
index df48cb3..5a0b25e 100644
--- a/EPPlus/ExcelCellBase.cs
+++ b/EPPlus/ExcelCellBase.cs
@@ -153,7 +153,6 @@
         if (isText == false && part != "" && prevTq == c) {
           ret += addressTranslator(part, row, col, rowIncr, colIncr);
           part = "";
-          prevTq = (char)0;
         }
         prevTq = c;
         isText = !isText;
@@ -220,7 +219,6 @@
         if (isText == false && part != "" && prevTq == c) {
           ret += addressTranslator(part, row, col, rowIncr, colIncr);
           part = "";
-          prevTq = (char)0;
         }
         prevTq = c;
         isText = !isText;
diff --git a/EPPlus/ExcelColumn.cs b/EPPlus/ExcelColumn.cs
index 31bd864..889c682 100644
--- a/EPPlus/ExcelColumn.cs
+++ b/EPPlus/ExcelColumn.cs
@@ -30,7 +30,6 @@
  * Jan K�llman		License changed GPL-->LGPL 2011-12-27
  *******************************************************************************/
 
-using System;
 using System.Xml;
 using OfficeOpenXml.Style;
 
diff --git a/EPPlus/ExcelComment.cs b/EPPlus/ExcelComment.cs
index 3930bde..74df866 100644
--- a/EPPlus/ExcelComment.cs
+++ b/EPPlus/ExcelComment.cs
@@ -40,7 +40,7 @@
 /// An Excel Cell Comment
 /// </summary>
 public class ExcelComment : ExcelVmlDrawingComment {
-  internal XmlHelper _commentHelper;
+  private readonly XmlHelper _commentHelper;
   private readonly string _text;
 
   internal ExcelComment(XmlNamespaceManager ns, XmlNode commentTopNode, ExcelRangeBase cell)
@@ -83,10 +83,6 @@
               _commentHelper.NameSpaceManager)
           .InnerText;
     }
-    set {
-      int authorRef = GetAuthor(value);
-      _commentHelper.SetXmlNodeString("@authorId", authorRef.ToString());
-    }
   }
 
   private int GetAuthor(string value) {
@@ -141,5 +137,5 @@
   /// <summary>
   /// Richtext collection
   /// </summary>
-  public ExcelRichTextCollection RichText { get; set; }
+  internal ExcelRichTextCollection RichText { get; set; }
 }
diff --git a/EPPlus/ExcelCommentCollection.cs b/EPPlus/ExcelCommentCollection.cs
index 9146df7..70a4820 100644
--- a/EPPlus/ExcelCommentCollection.cs
+++ b/EPPlus/ExcelCommentCollection.cs
@@ -45,7 +45,6 @@
   internal RangeCollection _comments;
 
   internal ExcelCommentCollection(ExcelPackage pck, ExcelWorksheet ws, XmlNamespaceManager ns) {
-    CommentXml = new();
     CommentXml.PreserveWhitespace = false;
     NameSpaceManager = ns;
     Worksheet = ws;
@@ -89,7 +88,7 @@
   /// <summary>
   /// Access to the comment xml document
   /// </summary>
-  public XmlDocument CommentXml { get; set; }
+  public XmlDocument CommentXml { get; set; } = new();
 
   internal Uri Uri { get; set; }
 
diff --git a/EPPlus/ExcelHeaderFooter.cs b/EPPlus/ExcelHeaderFooter.cs
deleted file mode 100644
index c4a3eeb..0000000
--- a/EPPlus/ExcelHeaderFooter.cs
+++ /dev/null
@@ -1,349 +0,0 @@
-/*******************************************************************************
- * You may amend and distribute as you like, but don't remove this header!
- *
- * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
- * See http://www.codeplex.com/EPPlus for details.
- *
- * Copyright (C) 2011  Jan Källman
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
-
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU Lesser General Public License for more details.
- *
- * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
- * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
- *
- * All code and executables are provided "as is" with no warranty either express or implied.
- * The author accepts no liability for any damage or loss of business that this product may cause.
- *
- * Code change notes:
- *
- * Author							Change						Date
- *******************************************************************************
- * Jan Källman		                Initial Release		        2009-10-01
- * Jan Källman                      Total rewrite               2010-03-01
- * Jan Källman		License changed GPL-->LGPL 2011-12-27
- * *******************************************************************************/
-
-using System.Xml;
-
-namespace OfficeOpenXml;
-
-/// <summary>
-/// Print header and footer
-/// </summary>
-public class ExcelHeaderFooterText {
-  private ExcelWorksheet _ws;
-  private string _hf;
-
-  internal ExcelHeaderFooterText(XmlNode textNode, ExcelWorksheet ws, string hf) {
-    _ws = ws;
-    _hf = hf;
-    if (textNode == null || string.IsNullOrEmpty(textNode.InnerText)) {
-      return;
-    }
-    string text = textNode.InnerText;
-    string code = text.Substring(0, 2);
-    int startPos = 2;
-    for (int pos = startPos; pos < text.Length - 2; pos++) {
-      string newCode = text.Substring(pos, 2);
-      if (newCode == "&C" || newCode == "&R") {
-        SetText(code, text.Substring(startPos, pos - startPos));
-        startPos = pos + 2;
-        pos = startPos;
-        code = newCode;
-      }
-    }
-    SetText(code, text.Substring(startPos, text.Length - startPos));
-  }
-
-  private void SetText(string code, string text) {
-    switch (code) {
-      case "&L":
-        LeftAlignedText = text;
-        break;
-      case "&C":
-        CenteredText = text;
-        break;
-      default:
-        RightAlignedText = text;
-        break;
-    }
-  }
-
-  /// <summary>
-  /// Get/set the text to appear on the left hand side of the header (or footer) on the worksheet.
-  /// </summary>
-  public string LeftAlignedText;
-
-  /// <summary>
-  /// Get/set the text to appear in the center of the header (or footer) on the worksheet.
-  /// </summary>
-  public string CenteredText;
-
-  /// <summary>
-  /// Get/set the text to appear on the right hand side of the header (or footer) on the worksheet.
-  /// </summary>
-  public string RightAlignedText;
-}
-
-/// <summary>
-/// Represents the Header and Footer on an Excel Worksheet
-/// </summary>
-public sealed class ExcelHeaderFooter : XmlHelper {
-  /// <summary>
-  /// The code for "current page #"
-  /// </summary>
-  public const string PageNumber = "&P";
-
-  /// <summary>
-  /// The code for "total pages"
-  /// </summary>
-  public const string NumberOfPages = "&N";
-
-  /// <summary>
-  /// The code for "text font color"
-  /// RGB Color is specified as RRGGBB
-  /// Theme Color is specified as TTSNN where TT is the theme color Id, S is either "+" or "-" of the tint/shade value, NN is the tint/shade value.
-  /// </summary>
-  public const string FontColor = "&K";
-
-  /// <summary>
-  /// The code for "sheet tab name"
-  /// </summary>
-  public const string SheetName = "&A";
-
-  /// <summary>
-  /// The code for "this workbook's file path"
-  /// </summary>
-  public const string FilePath = "&Z";
-
-  /// <summary>
-  /// The code for "this workbook's file name"
-  /// </summary>
-  public const string FileName = "&F";
-
-  /// <summary>
-  /// The code for "date"
-  /// </summary>
-  public const string CurrentDate = "&D";
-
-  /// <summary>
-  /// The code for "time"
-  /// </summary>
-  public const string CurrentTime = "&T";
-
-  /// <summary>
-  /// The code for "picture as background"
-  /// </summary>
-  public const string Image = "&G";
-
-  /// <summary>
-  /// The code for "outline style"
-  /// </summary>
-  public const string OutlineStyle = "&O";
-
-  /// <summary>
-  /// The code for "shadow style"
-  /// </summary>
-  public const string ShadowStyle = "&H";
-
-  internal ExcelHeaderFooterText _oddHeader;
-  internal ExcelHeaderFooterText _oddFooter;
-  internal ExcelHeaderFooterText _evenHeader;
-  internal ExcelHeaderFooterText _evenFooter;
-  internal ExcelHeaderFooterText _firstHeader;
-  internal ExcelHeaderFooterText _firstFooter;
-  private readonly ExcelWorksheet _ws;
-
-  /// <summary>
-  /// ExcelHeaderFooter Constructor
-  /// </summary>
-  /// <param name="nameSpaceManager"></param>
-  /// <param name="topNode"></param>
-  /// <param name="ws">The worksheet</param>
-  internal ExcelHeaderFooter(
-      XmlNamespaceManager nameSpaceManager,
-      XmlNode topNode,
-      ExcelWorksheet ws)
-      : base(nameSpaceManager, topNode) {
-    _ws = ws;
-    SchemaNodeOrder = new[] {
-      "headerFooter",
-      "oddHeader",
-      "oddFooter",
-      "evenHeader",
-      "evenFooter",
-      "firstHeader",
-      "firstFooter",
-    };
-  }
-
-  private const string _alignWithMarginsPath = "@alignWithMargins";
-
-  /// <summary>
-  /// Gets/sets the alignWithMargins attribute
-  /// </summary>
-  public bool AlignWithMargins {
-    get => GetXmlNodeBool(_alignWithMarginsPath);
-    set => SetXmlNodeString(_alignWithMarginsPath, value ? "1" : "0");
-  }
-
-  private const string _differentOddEvenPath = "@differentOddEven";
-
-  /// <summary>
-  /// Gets/sets the flag that tells Excel to display different headers and footers on odd and even pages.
-  /// </summary>
-  public bool differentOddEven {
-    get => GetXmlNodeBool(_differentOddEvenPath);
-    set => SetXmlNodeString(_differentOddEvenPath, value ? "1" : "0");
-  }
-
-  private const string _differentFirstPath = "@differentFirst";
-
-  /// <summary>
-  /// Gets/sets the flag that tells Excel to display different headers and footers on the first page of the worksheet.
-  /// </summary>
-  public bool differentFirst {
-    get => GetXmlNodeBool(_differentFirstPath);
-    set => SetXmlNodeString(_differentFirstPath, value ? "1" : "0");
-  }
-
-  /// <summary>
-  /// Provides access to the header on odd numbered pages of the document.
-  /// If you want the same header on both odd and even pages, then only set values in this ExcelHeaderFooterText class.
-  /// </summary>
-  public ExcelHeaderFooterText OddHeader {
-    get {
-      if (_oddHeader == null) {
-        _oddHeader = new(TopNode.SelectSingleNode("d:oddHeader", NameSpaceManager), _ws, "H");
-      }
-      return _oddHeader;
-    }
-  }
-
-  /// <summary>
-  /// Provides access to the footer on odd numbered pages of the document.
-  /// If you want the same footer on both odd and even pages, then only set values in this ExcelHeaderFooterText class.
-  /// </summary>
-  public ExcelHeaderFooterText OddFooter {
-    get {
-      if (_oddFooter == null) {
-        _oddFooter = new(TopNode.SelectSingleNode("d:oddFooter", NameSpaceManager), _ws, "F");
-        ;
-      }
-      return _oddFooter;
-    }
-  }
-
-  // evenHeader and evenFooter set differentOddEven = true
-  /// <summary>
-  /// Provides access to the header on even numbered pages of the document.
-  /// </summary>
-  public ExcelHeaderFooterText EvenHeader {
-    get {
-      if (_evenHeader == null) {
-        _evenHeader = new(TopNode.SelectSingleNode("d:evenHeader", NameSpaceManager), _ws, "HEVEN");
-        differentOddEven = true;
-      }
-      return _evenHeader;
-    }
-  }
-
-  /// <summary>
-  /// Provides access to the footer on even numbered pages of the document.
-  /// </summary>
-  public ExcelHeaderFooterText EvenFooter {
-    get {
-      if (_evenFooter == null) {
-        _evenFooter = new(TopNode.SelectSingleNode("d:evenFooter", NameSpaceManager), _ws, "FEVEN");
-        differentOddEven = true;
-      }
-      return _evenFooter;
-    }
-  }
-
-  /// <summary>
-  /// Provides access to the header on the first page of the document.
-  /// </summary>
-  public ExcelHeaderFooterText FirstHeader {
-    get {
-      if (_firstHeader == null) {
-        _firstHeader = new(
-            TopNode.SelectSingleNode("d:firstHeader", NameSpaceManager),
-            _ws,
-            "HFIRST");
-        differentFirst = true;
-      }
-      return _firstHeader;
-    }
-  }
-
-  /// <summary>
-  /// Provides access to the footer on the first page of the document.
-  /// </summary>
-  public ExcelHeaderFooterText FirstFooter {
-    get {
-      if (_firstFooter == null) {
-        _firstFooter = new(
-            TopNode.SelectSingleNode("d:firstFooter", NameSpaceManager),
-            _ws,
-            "FFIRST");
-        differentFirst = true;
-      }
-      return _firstFooter;
-    }
-  }
-
-  /// <summary>
-  /// Saves the header and footer information to the worksheet XML
-  /// </summary>
-  internal void Save() {
-    if (_oddHeader != null) {
-      SetXmlNodeString("d:oddHeader", GetText(OddHeader));
-    }
-    if (_oddFooter != null) {
-      SetXmlNodeString("d:oddFooter", GetText(OddFooter));
-    }
-
-    // only set evenHeader and evenFooter
-    if (differentOddEven) {
-      if (_evenHeader != null) {
-        SetXmlNodeString("d:evenHeader", GetText(EvenHeader));
-      }
-      if (_evenFooter != null) {
-        SetXmlNodeString("d:evenFooter", GetText(EvenFooter));
-      }
-    }
-
-    // only set firstHeader and firstFooter
-    if (differentFirst) {
-      if (_firstHeader != null) {
-        SetXmlNodeString("d:firstHeader", GetText(FirstHeader));
-      }
-      if (_firstFooter != null) {
-        SetXmlNodeString("d:firstFooter", GetText(FirstFooter));
-      }
-    }
-  }
-
-  private string GetText(ExcelHeaderFooterText headerFooter) {
-    string ret = "";
-    if (headerFooter.LeftAlignedText != null) {
-      ret += "&L" + headerFooter.LeftAlignedText;
-    }
-    if (headerFooter.CenteredText != null) {
-      ret += "&C" + headerFooter.CenteredText;
-    }
-    if (headerFooter.RightAlignedText != null) {
-      ret += "&R" + headerFooter.RightAlignedText;
-    }
-    return ret;
-  }
-}
diff --git a/EPPlus/ExcelPackage.cs b/EPPlus/ExcelPackage.cs
index be8f56e..7d12a0e 100644
--- a/EPPlus/ExcelPackage.cs
+++ b/EPPlus/ExcelPackage.cs
@@ -42,32 +42,10 @@
 namespace OfficeOpenXml;
 
 /// <summary>
-/// Maps to DotNetZips CompressionLevel enum
-/// </summary>
-public enum CompressionLevel {
-  Level0 = 0,
-  None = 0,
-  Level1 = 1,
-  BestSpeed = 1,
-  Level2 = 2,
-  Level3 = 3,
-  Level4 = 4,
-  Level5 = 5,
-  Level6 = 6,
-  Default = 6,
-  Level7 = 7,
-  Level8 = 8,
-  BestCompression = 9,
-  Level9 = 9,
-}
-
-/// <summary>
 /// Represents an Excel XLSX file package.
-/// This is the top-level object to access all parts of the document.\
+/// This is the top-level object to access all parts of the document.
 /// </summary>
 public sealed class ExcelPackage {
-  internal const bool _preserveWhitespace = false;
-
   /// <summary>
   /// Extention Schema types
   /// </summary>
@@ -86,8 +64,6 @@
   internal const string _schemaRelationships =
       "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
 
-  internal const string _schemaDrawings = "http://schemas.openxmlformats.org/drawingml/2006/main";
-
   internal const string _schemaMicrosoftVml = "urn:schemas-microsoft-com:vml";
   internal const string _schemaMicrosoftOffice = "urn:schemas-microsoft-com:office:office";
   internal const string _schemaMicrosoftExcel = "urn:schemas-microsoft-com:office:excel";
@@ -112,18 +88,6 @@
   internal const string _schemaVt =
       "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes";
 
-  //Pivottables
-  internal const string _schemaPivotTable =
-      "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml";
-  internal const string _schemaPivotCacheDefinition =
-      "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml";
-  internal const string _schemaPivotCacheRecords =
-      "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml";
-
-  internal const string _contentTypeWorkbookDefault =
-      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
-  internal const string _contentTypeWorkbookMacroEnabled =
-      "application/vnd.ms-excel.sheet.macroEnabled.main+xml";
   internal const string _contentTypeSharedString =
       "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml";
 
@@ -142,13 +106,7 @@
   /// </summary>
   public ExcelPackage() {
     Package = new();
-    Workbook = CreateWorkbook();
-    _ = Workbook.WorkbookXml;
-    // create the relationship to the main part
-    Package.CreateRelationship(
-        UriHelper.GetRelativeUri(new("/xl", UriKind.Relative), ExcelWorkbook.WorkbookUri),
-        TargetMode.Internal,
-        _schemaRelationships + "/officeDocument");
+    Workbook = new(this, CreateDefaultNsm());
   }
 
   /// <summary>
@@ -157,7 +115,7 @@
   public ExcelPackage(FileInfo newFile) {
     using var inputStream = newFile.OpenRead();
     Package = new(inputStream);
-    Workbook = CreateWorkbook();
+    Workbook = new(this, CreateDefaultNsm());
   }
 
   /// <summary>
@@ -165,14 +123,7 @@
   /// </summary>
   public ExcelPackage(Stream newStream) {
     Package = new(newStream);
-    Workbook = CreateWorkbook();
-  }
-
-  private ExcelWorkbook CreateWorkbook() {
-    ExcelWorkbook workbook = new(this, CreateDefaultNsm());
-    workbook.GetExternalReferences();
-    workbook.GetDefinedNames();
-    return workbook;
+    Workbook = new(this, CreateDefaultNsm());
   }
 
   internal ZipPackage Package { get; }
@@ -207,52 +158,54 @@
     return ns;
   }
 
-  /// <summary>
-  /// Saves the XmlDocument into the package at the specified Uri.
-  /// </summary>
-  /// <param name="uri">The Uri of the component</param>
-  /// <param name="xmlDoc">The XmlDocument to save</param>
-  internal void SavePart(Uri uri, XmlDocument xmlDoc) {
-    ZipPackagePart part = Package.GetPart(uri);
-    xmlDoc.Save(part.GetStream(FileMode.Create, FileAccess.Write));
+  internal XmlDocument GetXmlDocument(Uri uri) {
+    var xmlDocument = new XmlDocument();
+    var part = Package.GetPart(uri);
+    XmlHelper.LoadXmlSafe(xmlDocument, part.GetStream());
+    part.SaveHandler = writer => xmlDocument.Save(writer);
+    return xmlDocument;
   }
 
-  /// <summary>
-  /// Compression option for the package
-  /// </summary>
-  public CompressionLevel Compression {
-    get => Package.Compression;
-    set => Package.Compression = value;
+  internal ZipPackageRelationship CreateXmlDocument(
+      Uri uri,
+      string contentType,
+      string relationship,
+      XmlDocument newDoc) {
+    Package.CreatePart(uri, contentType, newDoc.Save);
+    return Package.CreateRelationship(
+        UriHelper.GetRelativeUri(new("/xl", UriKind.Relative), uri),
+        TargetMode.Internal,
+        relationship);
   }
 
-  /// <summary>
-  /// Get the XmlDocument from an URI
-  /// </summary>
-  /// <param name="uri">The Uri to the part</param>
-  /// <returns>The XmlDocument</returns>
-  internal XmlDocument GetXmlFromUri(Uri uri) {
-    XmlDocument xml = new XmlDocument();
-    ZipPackagePart part = Package.GetPart(uri);
-    XmlHelper.LoadXmlSafe(xml, part.GetStream());
-    return (xml);
+  internal XmlDocument GetOrCreateXmlDocument(
+      Uri uri,
+      string contentType,
+      string relationship,
+      Func<XmlDocument> createDocument) {
+    if (Package.PartExists(uri)) {
+      return GetXmlDocument(uri);
+    }
+    var newDoc = createDocument();
+    CreateXmlDocument(uri, contentType, relationship, newDoc);
+    return newDoc;
   }
 
-  /// <summary>
-  /// Saves and returns the Excel files as a bytearray.
-  /// Note that the package is closed upon save
-  /// </summary>
-  /// <example>
-  /// Example how to return a document from a Webserver...
-  /// <code>
-  ///  ExcelPackage package=new ExcelPackage();
-  ///  /**** ... Create the document ****/
-  ///  Byte[] bin = package.GetAsByteArray();
-  ///  Response.ContentType = "Application/vnd.ms-Excel";
-  ///  Response.AddHeader("content-disposition", "attachment;  filename=TheFile.xlsx");
-  ///  Response.BinaryWrite(bin);
-  /// </code>
-  /// </example>
-  /// <returns></returns>
+  internal XmlDocument GetOrCreateXmlDocument(
+      Uri uri,
+      string contentType,
+      string relationship,
+      string initialDocumentXml) =>
+    GetOrCreateXmlDocument(
+        uri,
+        contentType,
+        relationship,
+        () => {
+          var emptyDocument = new XmlDocument();
+          emptyDocument.LoadXml(initialDocumentXml);
+          return emptyDocument;
+        });
+
   public byte[] GetAsByteArray() {
     var result = new MemoryStream();
     Workbook.Save();
diff --git a/EPPlus/ExcelProtectedRange.cs b/EPPlus/ExcelProtectedRange.cs
index ef0da39..f89c544 100644
--- a/EPPlus/ExcelProtectedRange.cs
+++ b/EPPlus/ExcelProtectedRange.cs
@@ -1,35 +1,14 @@
 using System.Xml;
-using OfficeOpenXml.Utils;
 
 namespace OfficeOpenXml;
 
 public class ExcelProtectedRange : XmlHelper {
-  public string Name {
-    get => GetXmlNodeString("@name");
-    set => SetXmlNodeString("@name", value);
-  }
+  public string Name => GetXmlNodeString("@name");
 
   private ExcelAddress _address;
-  public ExcelAddress Address {
-    get {
-      if (_address == null) {
-        _address = new(GetXmlNodeString("@sqref"));
-      }
-      return _address;
-    }
-    set {
-      SetXmlNodeString("@sqref", SqRefUtility.ToSqRefAddress(value.Address));
-      _address = value;
-    }
-  }
 
-  internal ExcelProtectedRange(
-      string name,
-      ExcelAddress address,
-      XmlNamespaceManager ns,
-      XmlNode topNode)
-      : base(ns, topNode) {
-    Name = name;
-    Address = address;
-  }
+  public ExcelAddress Address => _address ??= new(GetXmlNodeString("@sqref"));
+
+  internal ExcelProtectedRange(XmlNamespaceManager ns, XmlNode topNode)
+      : base(ns, topNode) {}
 }
diff --git a/EPPlus/ExcelProtectedRangeCollection.cs b/EPPlus/ExcelProtectedRangeCollection.cs
index fdc2053..0b95ccf 100644
--- a/EPPlus/ExcelProtectedRangeCollection.cs
+++ b/EPPlus/ExcelProtectedRangeCollection.cs
@@ -1,15 +1,11 @@
 using System.Collections;
 using System.Collections.Generic;
 using System.Xml;
-using OfficeOpenXml.Utils;
 
 namespace OfficeOpenXml;
 
 public class ExcelProtectedRangeCollection : XmlHelper, IEnumerable<ExcelProtectedRange> {
-  internal ExcelProtectedRangeCollection(
-      XmlNamespaceManager nsm,
-      XmlNode topNode,
-      ExcelWorksheet ws)
+  internal ExcelProtectedRangeCollection(XmlNamespaceManager nsm, XmlNode topNode)
       : base(nsm, topNode) {
     foreach (XmlNode protectedRangeNode in topNode.SelectNodes(
         "d:protectedRanges/d:protectedRange",
@@ -17,64 +13,12 @@
       if (!(protectedRangeNode is XmlElement)) {
         continue;
       }
-      _baseList.Add(
-          new(
-              protectedRangeNode.Attributes["name"].Value,
-              new(SqRefUtility.FromSqRefAddress(protectedRangeNode.Attributes["sqref"].Value)),
-              nsm,
-              topNode));
+      _baseList.Add(new(nsm, topNode));
     }
   }
 
   private readonly List<ExcelProtectedRange> _baseList = new();
 
-  public ExcelProtectedRange Add(string name, ExcelAddress address) {
-    if (!ExistNode("d:protectedRanges")) {
-      CreateNode("d:protectedRanges");
-    }
-
-    var newNode = CreateNode("d:protectedRanges/d:protectedRange");
-    var item = new ExcelProtectedRange(name, address, NameSpaceManager, newNode);
-    _baseList.Add(item);
-    return item;
-  }
-
-  public void Clear() {
-    DeleteNode("d:protectedRanges");
-    _baseList.Clear();
-  }
-
-  public bool Contains(ExcelProtectedRange item) {
-    return _baseList.Contains(item);
-  }
-
-  public void CopyTo(ExcelProtectedRange[] array, int arrayIndex) {
-    _baseList.CopyTo(array, arrayIndex);
-  }
-
-  public int Count => _baseList.Count;
-
-  public bool Remove(ExcelProtectedRange item) {
-    DeleteAllNode(
-        "d:protectedRanges/d:protectedRange[@name='"
-            + item.Name
-            + "' and @sqref='"
-            + item.Address.Address
-            + "']");
-    if (_baseList.Count == 0) {
-      DeleteNode("d:protectedRanges");
-    }
-    return _baseList.Remove(item);
-  }
-
-  public int IndexOf(ExcelProtectedRange item) {
-    return _baseList.IndexOf(item);
-  }
-
-  public void RemoveAt(int index) {
-    _baseList.RemoveAt(index);
-  }
-
   public ExcelProtectedRange this[int index] => _baseList[index];
 
   IEnumerator<ExcelProtectedRange> IEnumerable<ExcelProtectedRange>.GetEnumerator() {
diff --git a/EPPlus/ExcelProtection.cs b/EPPlus/ExcelProtection.cs
index bf10612..5d0c943 100644
--- a/EPPlus/ExcelProtection.cs
+++ b/EPPlus/ExcelProtection.cs
@@ -30,6 +30,7 @@
  * Jan Källman		    License changed GPL-->LGPL 2011-12-27
  *******************************************************************************/
 
+using System.Collections.Immutable;
 using System.Xml;
 
 namespace OfficeOpenXml;
@@ -40,10 +41,11 @@
 ///<seealso cref="ExcelSheetProtection"/>
 /// </summary>
 public class ExcelProtection : XmlHelper {
-  internal ExcelProtection(XmlNamespaceManager ns, XmlNode topNode, ExcelWorkbook wb)
-      : base(ns, topNode) {
-    SchemaNodeOrder = wb.SchemaNodeOrder;
-  }
+  protected override ImmutableArray<string> SchemaNodeOrder =>
+    ExcelWorkbook.WorkbookSchemaNodeOrder;
+
+  internal ExcelProtection(XmlNamespaceManager ns, XmlNode topNode)
+      : base(ns, topNode) {}
 
   private const string _lockStructurePath = "d:workbookProtection/@lockStructure";
 
diff --git a/EPPlus/ExcelRangeBase.cs b/EPPlus/ExcelRangeBase.cs
index 686eb78..9c82531 100644
--- a/EPPlus/ExcelRangeBase.cs
+++ b/EPPlus/ExcelRangeBase.cs
@@ -34,23 +34,15 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
 using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Reflection;
 using System.Security;
 using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading;
 using System.Xml;
 using OfficeOpenXml.ConditionalFormatting;
 using OfficeOpenXml.DataValidation;
 using OfficeOpenXml.FormulaParsing.LexicalAnalysis;
 using OfficeOpenXml.Style;
 using OfficeOpenXml.Style.XmlAccess;
-using OfficeOpenXml.Table;
 
 namespace OfficeOpenXml;
 
@@ -1310,460 +1302,6 @@
   public IRangeDataValidation DataValidation => new RangeDataValidation(_worksheet, Address);
 
   /// <summary>
-  /// Load the data from the datareader starting from the top left cell of the range
-  /// </summary>
-  /// <param name="reader">The datareader to loadfrom</param>
-  /// <param name="printHeaders">Print the column caption property (if set) or the columnname property if not, on first row</param>
-  /// <param name="tableName">The name of the table</param>
-  /// <param name="tableStyle">The table style to apply to the data</param>
-  /// <returns>The filled range</returns>
-  public ExcelRangeBase LoadFromDataReader(
-      IDataReader reader,
-      bool printHeaders,
-      string tableName,
-      TableStyles tableStyle = TableStyles.None) {
-    var r = LoadFromDataReader(reader, printHeaders);
-
-    int rows = r.Rows - 1;
-    if (rows >= 0 && r.Columns > 0) {
-      var tbl = _worksheet.Tables.Add(
-          new(_fromRow, _fromCol, _fromRow + (rows <= 0 ? 1 : rows), _fromCol + r.Columns - 1),
-          tableName);
-      tbl.ShowHeader = printHeaders;
-      tbl.TableStyle = tableStyle;
-    }
-    return r;
-  }
-
-  /// <summary>
-  /// Load the data from the datareader starting from the top left cell of the range
-  /// </summary>
-  /// <param name="reader">The datareader to load from</param>
-  /// <param name="printHeaders">Print the caption property (if set) or the columnname property if not, on first row</param>
-  /// <returns>The filled range</returns>
-  public ExcelRangeBase LoadFromDataReader(IDataReader reader, bool printHeaders) {
-    if (reader == null) {
-      throw (new ArgumentNullException("reader", "Reader can't be null"));
-    }
-    int fieldCount = reader.FieldCount;
-
-    int col = _fromCol,
-        row = _fromRow;
-    if (printHeaders) {
-      for (int i = 0; i < fieldCount; i++) {
-        // If no caption is set, the ColumnName property is called implicitly.
-        _worksheet._values.SetValue(row, col++, reader.GetName(i));
-      }
-      row++;
-      col = _fromCol;
-    }
-    while (reader.Read()) {
-      for (int i = 0; i < fieldCount; i++) {
-        _worksheet._values.SetValue(row, col++, reader.GetValue(i));
-      }
-      row++;
-      col = _fromCol;
-    }
-    return _worksheet.Cells[_fromRow, _fromCol, row - 1, _fromCol + fieldCount - 1];
-  }
-
-  /// <summary>
-  /// Load the data from the datatable starting from the top left cell of the range
-  /// </summary>
-  /// <param name="table">The datatable to load</param>
-  /// <param name="printHeaders">Print the column caption property (if set) or the columnname property if not, on first row</param>
-  /// <param name="tableStyle">The table style to apply to the data</param>
-  /// <returns>The filled range</returns>
-  public ExcelRangeBase LoadFromDataTable(
-      DataTable table,
-      bool printHeaders,
-      TableStyles tableStyle) {
-    var r = LoadFromDataTable(table, printHeaders);
-
-    int rows = (table.Rows.Count == 0 ? 1 : table.Rows.Count) + (printHeaders ? 1 : 0);
-    if (rows >= 0 && table.Columns.Count > 0) {
-      var tbl = _worksheet.Tables.Add(
-          new(_fromRow, _fromCol, _fromRow + rows - 1, _fromCol + table.Columns.Count - 1),
-          table.TableName);
-      tbl.ShowHeader = printHeaders;
-      tbl.TableStyle = tableStyle;
-    }
-    return r;
-  }
-
-  /// <summary>
-  /// Load the data from the datatable starting from the top left cell of the range
-  /// </summary>
-  /// <param name="table">The datatable to load</param>
-  /// <param name="printHeaders">Print the caption property (if set) or the columnname property if not, on first row</param>
-  /// <returns>The filled range</returns>
-  public ExcelRangeBase LoadFromDataTable(DataTable table, bool printHeaders) {
-    if (table == null) {
-      throw (new ArgumentNullException("Table can't be null"));
-    }
-
-    int col = _fromCol,
-        row = _fromRow;
-    if (printHeaders) {
-      foreach (DataColumn dc in table.Columns) {
-        // If no caption is set, the ColumnName property is called implicitly.
-        _worksheet._values.SetValue(row, col++, dc.Caption);
-      }
-      row++;
-      col = _fromCol;
-    } else if (table.Rows.Count == 0) {
-      return null;
-    }
-    foreach (DataRow dr in table.Rows) {
-      foreach (object value in dr.ItemArray) {
-        if (value != null && value != DBNull.Value && !string.IsNullOrEmpty(value.ToString())) {
-          _worksheet._values.SetValue(row, col++, value);
-        } else {
-          col++;
-        }
-      }
-      row++;
-      col = _fromCol;
-    }
-    return _worksheet.Cells[_fromRow,
-      _fromCol,
-      (row == _fromRow ? _fromRow : row - 1),
-      _fromCol + table.Columns.Count - 1];
-  }
-
-  /// <summary>
-  /// Loads data from the collection of arrays of objects into the range, starting from
-  /// the top-left cell.
-  /// </summary>
-  /// <param name="data">The data.</param>
-  public ExcelRangeBase LoadFromArrays(IEnumerable<object[]> data) {
-    //thanx to Abdullin for the code contribution
-    if (data == null) {
-      throw new ArgumentNullException("data");
-    }
-
-    int column = _fromCol,
-        row = _fromRow;
-
-    foreach (var rowData in data) {
-      column = _fromCol;
-      foreach (var cellData in rowData) {
-        _worksheet._values.SetValue(row, column, cellData);
-        column += 1;
-      }
-      row += 1;
-    }
-    return _worksheet.Cells[_fromRow, _fromCol, row - 1, column - 1];
-  }
-
-  /// <summary>
-  /// Load a collection into a the worksheet starting from the top left row of the range.
-  /// </summary>
-  /// <typeparam name="T">The datatype in the collection</typeparam>
-  /// <param name="collection">The collection to load</param>
-  /// <returns>The filled range</returns>
-  public ExcelRangeBase LoadFromCollection<T>(IEnumerable<T> collection) {
-    return LoadFromCollection(
-        collection,
-        false,
-        TableStyles.None,
-        BindingFlags.Public | BindingFlags.Instance,
-        null);
-  }
-
-  /// <summary>
-  /// Load a collection of T into the worksheet starting from the top left row of the range.
-  /// Default option will load all public instance properties of T
-  /// </summary>
-  /// <typeparam name="T">The datatype in the collection</typeparam>
-  /// <param name="collection">The collection to load</param>
-  /// <param name="printHeaders">Print the property names on the first row. If the property is decorated with a <see cref="DisplayNameAttribute"/> or a <see cref="DescriptionAttribute"/> that attribute will be used instead of the reflected member name.</param>
-  /// <returns>The filled range</returns>
-  public ExcelRangeBase LoadFromCollection<T>(IEnumerable<T> collection, bool printHeaders) {
-    return LoadFromCollection(
-        collection,
-        printHeaders,
-        TableStyles.None,
-        BindingFlags.Public | BindingFlags.Instance,
-        null);
-  }
-
-  /// <summary>
-  /// Load a collection of T into the worksheet starting from the top left row of the range.
-  /// Default option will load all public instance properties of T
-  /// </summary>
-  /// <typeparam name="T">The datatype in the collection</typeparam>
-  /// <param name="collection">The collection to load</param>
-  /// <param name="printHeaders">Print the property names on the first row. If the property is decorated with a <see cref="DisplayNameAttribute"/> or a <see cref="DescriptionAttribute"/> that attribute will be used instead of the reflected member name.</param>
-  /// <param name="tableStyle">Will create a table with this style. If set to TableStyles.None no table will be created</param>
-  /// <returns>The filled range</returns>
-  public ExcelRangeBase LoadFromCollection<T>(
-      IEnumerable<T> collection,
-      bool printHeaders,
-      TableStyles tableStyle) {
-    return LoadFromCollection(
-        collection,
-        printHeaders,
-        tableStyle,
-        BindingFlags.Public | BindingFlags.Instance,
-        null);
-  }
-
-  /// <summary>
-  /// Load a collection into the worksheet starting from the top left row of the range.
-  /// </summary>
-  /// <typeparam name="T">The datatype in the collection</typeparam>
-  /// <param name="collection">The collection to load</param>
-  /// <param name="printHeaders">Print the property names on the first row. Any underscore in the property name will be converted to a space. If the property is decorated with a <see cref="DisplayNameAttribute"/> or a <see cref="DescriptionAttribute"/> that attribute will be used instead of the reflected member name.</param>
-  /// <param name="tableStyle">Will create a table with this style. If set to TableStyles.None no table will be created</param>
-  /// <param name="memberFlags">Property flags to use</param>
-  /// <param name="members">The properties to output. Must be of type T</param>
-  /// <returns>The filled range</returns>
-  public ExcelRangeBase LoadFromCollection<T>(
-      IEnumerable<T> collection,
-      bool printHeaders,
-      TableStyles tableStyle,
-      BindingFlags memberFlags,
-      MemberInfo[] members) {
-    var type = typeof(T);
-    if (members == null) {
-      members = type.GetProperties(memberFlags);
-    } else {
-      foreach (var t in members) {
-        if (t.DeclaringType != null
-            && t.DeclaringType != type
-            && !t.DeclaringType.IsSubclassOf(type)) {
-          throw new InvalidCastException(
-              "Supplied properties in parameter Properties must be of the same type as T (or an assignable type from T");
-        }
-      }
-    }
-
-    int col = _fromCol,
-        row = _fromRow;
-    if (members.Length > 0 && printHeaders) {
-      foreach (var t in members) {
-        var descriptionAttribute =
-            t.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault()
-                as DescriptionAttribute;
-        var header = string.Empty;
-        if (descriptionAttribute != null) {
-          header = descriptionAttribute.Description;
-        } else {
-          var displayNameAttribute =
-              t.GetCustomAttributes(typeof(DisplayNameAttribute), false).FirstOrDefault()
-                  as DisplayNameAttribute;
-          if (displayNameAttribute != null) {
-            header = displayNameAttribute.DisplayName;
-          } else {
-            header = t.Name.Replace('_', ' ');
-          }
-        }
-        _worksheet._values.SetValue(row, col++, header);
-      }
-      row++;
-    }
-
-    if (!collection.Any() && (members.Length == 0 || printHeaders == false)) {
-      return null;
-    }
-
-    if (members.Length == 0) {
-      foreach (var item in collection) {
-        _worksheet.Cells[row++, col].Value = item;
-      }
-    } else {
-      foreach (var item in collection) {
-        col = _fromCol;
-        if (item is string || item is decimal || item is DateTime || item.GetType().IsPrimitive) {
-          _worksheet.Cells[row, col++].Value = item;
-        } else {
-          foreach (var t in members) {
-            if (t is PropertyInfo info) {
-              _worksheet.Cells[row, col++].Value = info.GetValue(item, null);
-            } else if (t is FieldInfo fieldInfo) {
-              _worksheet.Cells[row, col++].Value = fieldInfo.GetValue(item);
-            } else if (t is MethodInfo methodInfo) {
-              _worksheet.Cells[row, col++].Value = methodInfo.Invoke(item, null);
-            }
-          }
-        }
-        row++;
-      }
-    }
-
-    if (_fromRow == row - 1 && printHeaders) {
-      row++;
-    }
-
-    var r = _worksheet.Cells[_fromRow, _fromCol, row - 1, members.Length == 0 ? col : col - 1];
-
-    if (tableStyle != TableStyles.None) {
-      var tbl = _worksheet.Tables.Add(r, "");
-      tbl.ShowHeader = printHeaders;
-      tbl.TableStyle = tableStyle;
-    }
-    return r;
-  }
-
-  /// <summary>
-  /// Loads a CSV text into a range starting from the top left cell.
-  /// Default settings is Comma separation
-  /// </summary>
-  /// <param name="text">The Text</param>
-  /// <returns>The range containing the data</returns>
-  public ExcelRangeBase LoadFromText(string text) {
-    return LoadFromText(text, new());
-  }
-
-  /// <summary>
-  /// Loads a CSV text into a range starting from the top left cell.
-  /// </summary>
-  /// <param name="text">The Text</param>
-  /// <param name="format">Information how to load the text</param>
-  /// <returns>The range containing the data</returns>
-  public ExcelRangeBase LoadFromText(string text, ExcelTextFormat format) {
-    if (string.IsNullOrEmpty(text)) {
-      var r = _worksheet.Cells[_fromRow, _fromCol];
-      r.Value = "";
-      return r;
-    }
-
-    if (format == null) {
-      format = new();
-    }
-
-    string splitRegex = String.Format(
-        "{0}(?=(?:[^{1}]*{1}[^{1}]*{1})*[^{1}]*$)",
-        format.EOL,
-        format.TextQualifier);
-    string[] lines = Regex.Split(text, splitRegex);
-    int row = _fromRow;
-    int col = _fromCol;
-    int maxCol = col;
-    int lineNo = 1;
-    foreach (string line in lines) {
-      if (lineNo > format.SkipLinesBeginning && lineNo <= lines.Length - format.SkipLinesEnd) {
-        col = _fromCol;
-        string v = "";
-        bool isText = false,
-            isQualifier = false;
-        int qCount = 0;
-        int lineQCount = 0;
-        foreach (char c in line) {
-          if (format.TextQualifier != 0 && c == format.TextQualifier) {
-            if (!isText && v != "") {
-              throw (new(string.Format("Invalid Text Qualifier in line : {0}", line)));
-            }
-            isQualifier = !isQualifier;
-            qCount += 1;
-            lineQCount++;
-            isText = true;
-          } else {
-            if (qCount > 1 && !string.IsNullOrEmpty(v)) {
-              v += new string(format.TextQualifier, qCount / 2);
-            } else if (qCount > 2 && string.IsNullOrEmpty(v)) {
-              v += new string(format.TextQualifier, (qCount - 1) / 2);
-            }
-
-            if (isQualifier) {
-              v += c;
-            } else {
-              if (c == format.Delimiter) {
-                _worksheet.SetValue(row, col, ConvertData(format, v, col - _fromCol, isText));
-                v = "";
-                isText = false;
-                col++;
-              } else {
-                if (qCount % 2 == 1) {
-                  throw (new(string.Format("Text delimiter is not closed in line : {0}", line)));
-                }
-                v += c;
-              }
-            }
-            qCount = 0;
-          }
-        }
-        if (qCount > 1) {
-          v += new string(format.TextQualifier, qCount / 2);
-        }
-        if (lineQCount % 2 == 1) {
-          throw (new(string.Format("Text delimiter is not closed in line : {0}", line)));
-        }
-
-        _worksheet._values.SetValue(row, col, ConvertData(format, v, col - _fromCol, isText));
-        if (col > maxCol) {
-          maxCol = col;
-        }
-        row++;
-      }
-      lineNo++;
-    }
-    return _worksheet.Cells[_fromRow, _fromCol, row - 1, maxCol];
-  }
-
-  /// <summary>
-  /// Loads a CSV text into a range starting from the top left cell.
-  /// </summary>
-  /// <param name="text">The Text</param>
-  /// <param name="format">Information how to load the text</param>
-  /// <param name="tableStyle">Create a table with this style</param>
-  /// <param name="firstRowIsHeader">Use the first row as header</param>
-  /// <returns></returns>
-  public ExcelRangeBase LoadFromText(
-      string text,
-      ExcelTextFormat format,
-      TableStyles tableStyle,
-      bool firstRowIsHeader) {
-    var r = LoadFromText(text, format);
-
-    var tbl = _worksheet.Tables.Add(r, "");
-    tbl.ShowHeader = firstRowIsHeader;
-    tbl.TableStyle = tableStyle;
-
-    return r;
-  }
-
-  /// <summary>
-  /// Loads a CSV file into a range starting from the top left cell.
-  /// </summary>
-  /// <param name="textFile">The Textfile</param>
-  /// <returns></returns>
-  public ExcelRangeBase LoadFromText(FileInfo textFile) {
-    return LoadFromText(File.ReadAllText(textFile.FullName, Encoding.ASCII));
-  }
-
-  /// <summary>
-  /// Loads a CSV file into a range starting from the top left cell.
-  /// </summary>
-  /// <param name="textFile">The Textfile</param>
-  /// <param name="format">Information how to load the text</param>
-  /// <returns></returns>
-  public ExcelRangeBase LoadFromText(FileInfo textFile, ExcelTextFormat format) {
-    return LoadFromText(File.ReadAllText(textFile.FullName, format.Encoding), format);
-  }
-
-  /// <summary>
-  /// Loads a CSV file into a range starting from the top left cell.
-  /// </summary>
-  /// <param name="textFile">The Textfile</param>
-  /// <param name="format">Information how to load the text</param>
-  /// <param name="tableStyle">Create a table with this style</param>
-  /// <param name="firstRowIsHeader">Use the first row as header</param>
-  /// <returns></returns>
-  public ExcelRangeBase LoadFromText(
-      FileInfo textFile,
-      ExcelTextFormat format,
-      TableStyles tableStyle,
-      bool firstRowIsHeader) {
-    return LoadFromText(
-        File.ReadAllText(textFile.FullName, format.Encoding),
-        format,
-        tableStyle,
-        firstRowIsHeader);
-  }
-
-  /// <summary>
   /// Get the strongly typed value of the cell.
   /// </summary>
   /// <typeparam name="T">The type</typeparam>
diff --git a/EPPlus/ExcelRow.cs b/EPPlus/ExcelRow.cs
index bb94e2b..f4336c7 100644
--- a/EPPlus/ExcelRow.cs
+++ b/EPPlus/ExcelRow.cs
@@ -44,20 +44,6 @@
   internal bool PageBreak;
   internal bool Phonetic;
   internal bool CustomHeight;
-  internal int MergeID;
-
-  internal RowInternal Clone() {
-    return new() {
-      Height = Height,
-      Hidden = Hidden,
-      Collapsed = Collapsed,
-      OutlineLevel = OutlineLevel,
-      PageBreak = PageBreak,
-      Phonetic = Phonetic,
-      CustomHeight = CustomHeight,
-      MergeID = MergeID,
-    };
-  }
 }
 
 /// <summary>
@@ -269,20 +255,4 @@
     get => RowID;
     set => Row = ((int)(value >> 29));
   }
-
-  /// <summary>
-  /// Copies the current row to a new worksheet
-  /// </summary>
-  /// <param name="added">The worksheet where the copy will be created</param>
-  internal void Clone(ExcelWorksheet added) {
-    ExcelRow newRow = added.Row(Row);
-    newRow.Collapsed = Collapsed;
-    newRow.Height = Height;
-    newRow.Hidden = Hidden;
-    newRow.OutlineLevel = OutlineLevel;
-    newRow.PageBreak = PageBreak;
-    newRow.Phonetic = Phonetic;
-    newRow._styleName = _styleName;
-    newRow.StyleID = StyleID;
-  }
 }
diff --git a/EPPlus/ExcelSheetProtection.cs b/EPPlus/ExcelSheetProtection.cs
index bc7996d..0b2cb09 100644
--- a/EPPlus/ExcelSheetProtection.cs
+++ b/EPPlus/ExcelSheetProtection.cs
@@ -30,6 +30,7 @@
  * Jan Källman		    License changed GPL-->LGPL 2011-12-27
  *******************************************************************************/
 
+using System.Collections.Immutable;
 using System.Xml;
 
 namespace OfficeOpenXml;
@@ -40,10 +41,11 @@
 ///<seealso cref="ExcelProtection"/>
 /// </summary>
 public sealed class ExcelSheetProtection : XmlHelper {
-  internal ExcelSheetProtection(XmlNamespaceManager nsm, XmlNode topNode, ExcelWorksheet ws)
-      : base(nsm, topNode) {
-    SchemaNodeOrder = ws.SchemaNodeOrder;
-  }
+  protected override ImmutableArray<string> SchemaNodeOrder =>
+    ExcelWorksheet.WorksheetSchemaNodeOrder;
+
+  internal ExcelSheetProtection(XmlNamespaceManager nsm, XmlNode topNode)
+      : base(nsm, topNode) {}
 
   private const string _isProtectedPath = "d:sheetProtection/@sheet";
 
diff --git a/EPPlus/ExcelStyles.cs b/EPPlus/ExcelStyles.cs
index 9f1b283..6bb75ff 100644
--- a/EPPlus/ExcelStyles.cs
+++ b/EPPlus/ExcelStyles.cs
@@ -32,6 +32,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.Linq;
 using System.Xml;
 using OfficeOpenXml.ConditionalFormatting;
@@ -60,21 +61,22 @@
   private readonly XmlNamespaceManager _nameSpaceManager;
   internal int _nextDfxNumFmtID = 164;
 
+  protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
+    "numFmts",
+    "fonts",
+    "fills",
+    "borders",
+    "cellStyleXfs",
+    "cellXfs",
+    "cellStyles",
+    "dxfs",
+  ];
+
   internal ExcelStyles(XmlNamespaceManager nameSpaceManager, XmlDocument xml, ExcelWorkbook wb)
       : base(nameSpaceManager, xml) {
     _styleXml = xml;
     _wb = wb;
     _nameSpaceManager = nameSpaceManager;
-    SchemaNodeOrder = new[] {
-      "numFmts",
-      "fonts",
-      "fills",
-      "borders",
-      "cellStyleXfs",
-      "cellXfs",
-      "cellStyles",
-      "dxfs",
-    };
     LoadFromDocument();
   }
 
@@ -177,13 +179,11 @@
     var ws = _wb.Worksheets[e.PositionID];
     Dictionary<int, int> styleCashe = new Dictionary<int, int>();
     //Set single address
-    lock (ws._styles) {
-      SetStyleAddress(sender, e, address, ws, ref styleCashe);
-      if (address.Addresses != null) {
-        //Handle multiaddresses
-        foreach (var innerAddress in address.Addresses) {
-          SetStyleAddress(sender, e, innerAddress, ws, ref styleCashe);
-        }
+    SetStyleAddress(sender, e, address, ws, ref styleCashe);
+    if (address.Addresses != null) {
+      //Handle multiaddresses
+      foreach (var innerAddress in address.Addresses) {
+        SetStyleAddress(sender, e, innerAddress, ws, ref styleCashe);
       }
     }
     return 0;
@@ -769,113 +769,106 @@
   }
 
   internal int CloneStyle(ExcelStyles style, int styleId, bool isNamedStyle, bool allwaysAdd) {
-    ExcelXfs xfs;
-    lock (style) {
-      if (isNamedStyle) {
-        xfs = style.CellStyleXfs[styleId];
-      } else {
-        xfs = style.CellXfs[styleId];
-      }
-      ExcelXfs newXfs = xfs.Copy(this);
-      //Numberformat
-      if (xfs.NumberFormatId > 0) {
-        //rake36: Two problems here...
-        //rake36:  1. the first time through when format stays equal to String.Empty, it adds a string.empty to the list of Number Formats
-        //rake36:  2. when adding a second sheet, if the numberformatid == 164, it finds the 164 added by previous sheets but was using the array index
-        //rake36:      for the numberformatid
+    var xfs = isNamedStyle ? style.CellStyleXfs[styleId] : style.CellXfs[styleId];
+    ExcelXfs newXfs = xfs.Copy(this);
+    //Numberformat
+    if (xfs.NumberFormatId > 0) {
+      //rake36: Two problems here...
+      //rake36:  1. the first time through when format stays equal to String.Empty, it adds a string.empty to the list of Number Formats
+      //rake36:  2. when adding a second sheet, if the numberformatid == 164, it finds the 164 added by previous sheets but was using the array index
+      //rake36:      for the numberformatid
 
-        string format = string.Empty;
-        foreach (var fmt in style.NumberFormats) {
-          if (fmt.NumFmtId == xfs.NumberFormatId) {
-            format = fmt.Format;
-            break;
-          }
-        }
-        //rake36: Don't add another format if it's blank
-        if (!String.IsNullOrEmpty(format)) {
-          int ix = NumberFormats.FindIndexById(format);
-          if (ix < 0) {
-            var item = new ExcelNumberFormatXml(NameSpaceManager) {
-              Format = format,
-              NumFmtId = NumberFormats.NextId++,
-            };
-            NumberFormats.Add(format, item);
-            //rake36: Use the just added format id
-            newXfs.NumberFormatId = item.NumFmtId;
-          } else {
-            //rake36: Use the format id defined by the index... not the index itself
-            newXfs.NumberFormatId = NumberFormats[ix].NumFmtId;
-          }
+      string format = string.Empty;
+      foreach (var fmt in style.NumberFormats) {
+        if (fmt.NumFmtId == xfs.NumberFormatId) {
+          format = fmt.Format;
+          break;
         }
       }
-
-      //Font
-      if (xfs.FontId > -1) {
-        int ix = Fonts.FindIndexById(xfs.Font.Id);
+      //rake36: Don't add another format if it's blank
+      if (!String.IsNullOrEmpty(format)) {
+        int ix = NumberFormats.FindIndexById(format);
         if (ix < 0) {
-          ExcelFontXml item = style.Fonts[xfs.FontId].Copy();
-          ix = Fonts.Add(xfs.Font.Id, item);
-        }
-        newXfs.FontId = ix;
-      }
-
-      //Border
-      if (xfs.BorderId > -1) {
-        int ix = Borders.FindIndexById(xfs.Border.Id);
-        if (ix < 0) {
-          ExcelBorderXml item = style.Borders[xfs.BorderId].Copy();
-          ix = Borders.Add(xfs.Border.Id, item);
-        }
-        newXfs.BorderId = ix;
-      }
-
-      //Fill
-      if (xfs.FillId > -1) {
-        int ix = Fills.FindIndexById(xfs.Fill.Id);
-        if (ix < 0) {
-          var item = style.Fills[xfs.FillId].Copy();
-          ix = Fills.Add(xfs.Fill.Id, item);
-        }
-        newXfs.FillId = ix;
-      }
-
-      //Named style reference
-      if (xfs.XfId > 0) {
-        var id = style.CellStyleXfs[xfs.XfId].Id;
-        var newId = CellStyleXfs.FindIndexById(id);
-        if (newId >= 0) {
-          newXfs.XfId = newId;
-        } else if (style._wb != _wb
-            && allwaysAdd
-                == false) //Not the same workbook, copy the namedstyle to the workbook or match the id
-        {
-          var nsFind = style.NamedStyles.ToDictionary(d => (d.StyleXfId));
-          if (nsFind.ContainsKey(xfs.XfId)) {
-            var st = nsFind[xfs.XfId];
-            if (NamedStyles.ExistsKey(st.Name)) {
-              newXfs.XfId = NamedStyles.FindIndexById(st.Name);
-            } else {
-              var ns = CreateNamedStyle(st.Name, st.Style);
-              newXfs.XfId = NamedStyles.Count - 1;
-            }
-          }
-        }
-      }
-
-      int index;
-      if (isNamedStyle) {
-        index = CellStyleXfs.Add(newXfs.Id, newXfs);
-      } else {
-        if (allwaysAdd) {
-          index = CellXfs.Add(newXfs.Id, newXfs);
+          var item = new ExcelNumberFormatXml(NameSpaceManager) {
+            Format = format,
+            NumFmtId = NumberFormats.NextId++,
+          };
+          NumberFormats.Add(format, item);
+          //rake36: Use the just added format id
+          newXfs.NumberFormatId = item.NumFmtId;
         } else {
-          index = CellXfs.FindIndexById(newXfs.Id);
-          if (index < 0) {
-            index = CellXfs.Add(newXfs.Id, newXfs);
+          //rake36: Use the format id defined by the index... not the index itself
+          newXfs.NumberFormatId = NumberFormats[ix].NumFmtId;
+        }
+      }
+    }
+
+    //Font
+    if (xfs.FontId > -1) {
+      int ix = Fonts.FindIndexById(xfs.Font.Id);
+      if (ix < 0) {
+        ExcelFontXml item = style.Fonts[xfs.FontId].Copy();
+        ix = Fonts.Add(xfs.Font.Id, item);
+      }
+      newXfs.FontId = ix;
+    }
+
+    //Border
+    if (xfs.BorderId > -1) {
+      int ix = Borders.FindIndexById(xfs.Border.Id);
+      if (ix < 0) {
+        ExcelBorderXml item = style.Borders[xfs.BorderId].Copy();
+        ix = Borders.Add(xfs.Border.Id, item);
+      }
+      newXfs.BorderId = ix;
+    }
+
+    //Fill
+    if (xfs.FillId > -1) {
+      int ix = Fills.FindIndexById(xfs.Fill.Id);
+      if (ix < 0) {
+        var item = style.Fills[xfs.FillId].Copy();
+        ix = Fills.Add(xfs.Fill.Id, item);
+      }
+      newXfs.FillId = ix;
+    }
+
+    //Named style reference
+    if (xfs.XfId > 0) {
+      var id = style.CellStyleXfs[xfs.XfId].Id;
+      var newId = CellStyleXfs.FindIndexById(id);
+      if (newId >= 0) {
+        newXfs.XfId = newId;
+      } else if (style._wb != _wb
+          && allwaysAdd
+              == false) //Not the same workbook, copy the namedstyle to the workbook or match the id
+      {
+        var nsFind = style.NamedStyles.ToDictionary(d => (d.StyleXfId));
+        if (nsFind.ContainsKey(xfs.XfId)) {
+          var st = nsFind[xfs.XfId];
+          if (NamedStyles.ExistsKey(st.Name)) {
+            newXfs.XfId = NamedStyles.FindIndexById(st.Name);
+          } else {
+            var ns = CreateNamedStyle(st.Name, st.Style);
+            newXfs.XfId = NamedStyles.Count - 1;
           }
         }
       }
-      return index;
     }
+
+    int index;
+    if (isNamedStyle) {
+      index = CellStyleXfs.Add(newXfs.Id, newXfs);
+    } else {
+      if (allwaysAdd) {
+        index = CellXfs.Add(newXfs.Id, newXfs);
+      } else {
+        index = CellXfs.FindIndexById(newXfs.Id);
+        if (index < 0) {
+          index = CellXfs.Add(newXfs.Id, newXfs);
+        }
+      }
+    }
+    return index;
   }
 }
diff --git a/EPPlus/ExcelTextFormat.cs b/EPPlus/ExcelTextFormat.cs
index 83b0c59..8029447 100644
--- a/EPPlus/ExcelTextFormat.cs
+++ b/EPPlus/ExcelTextFormat.cs
@@ -86,54 +86,45 @@
   /// <item><term>Encoding</term><description>Encoding.ASCII</description></item>
   /// </list>
   /// </summary>
-  public ExcelTextFormat() {
-    Delimiter = ',';
-    TextQualifier = '\0';
-    EOL = "\r\n";
-    Culture = CultureInfo.InvariantCulture;
-    DataTypes = null;
-    SkipLinesBeginning = 0;
-    SkipLinesEnd = 0;
-    Encoding = Encoding.ASCII;
-  }
+  public ExcelTextFormat() {}
 
   /// <summary>
   /// Delimiter character
   /// </summary>
-  public char Delimiter { get; set; }
+  public char Delimiter { get; set; } = ',';
 
   /// <summary>
   /// Text qualifier character
   /// </summary>
-  public char TextQualifier { get; set; }
+  public char TextQualifier { get; set; } = '\0';
 
   /// <summary>
   /// End of line characters. Default CRLF
   /// </summary>
-  public string EOL { get; set; }
+  public string EOL { get; set; } = "\r\n";
 
   /// <summary>
   /// Datatypes list for each column (if column is not present Unknown is assumed)
   /// </summary>
-  public eDataTypes[] DataTypes { get; set; }
+  public eDataTypes[] DataTypes { get; set; } = null;
 
   /// <summary>
   /// Culture used when parsing. Default CultureInfo.InvariantCulture
   /// </summary>
-  public CultureInfo Culture { get; set; }
+  public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture;
 
   /// <summary>
   /// Number of lines skiped in the begining of the file. Default 0.
   /// </summary>
-  public int SkipLinesBeginning { get; set; }
+  public int SkipLinesBeginning { get; set; } = 0;
 
   /// <summary>
   /// Number of lines skiped at the end of the file. Default 0.
   /// </summary>
-  public int SkipLinesEnd { get; set; }
+  public int SkipLinesEnd { get; set; } = 0;
 
   /// <summary>
   /// Only used when reading files from disk using a FileInfo object. Default AscII
   /// </summary>
-  public Encoding Encoding { get; set; }
+  public Encoding Encoding { get; set; } = Encoding.ASCII;
 }
diff --git a/EPPlus/ExcelWorkbook.cs b/EPPlus/ExcelWorkbook.cs
index 3331644..c581319 100644
--- a/EPPlus/ExcelWorkbook.cs
+++ b/EPPlus/ExcelWorkbook.cs
@@ -33,6 +33,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.Globalization;
 using System.IO;
 using System.Text;
@@ -77,57 +78,105 @@
     internal bool isRichText;
   }
 
-  internal ExcelPackage _package;
+  private readonly ExcelPackage _package;
   private ExcelWorksheets _worksheets;
   private OfficeProperties _properties;
 
   private ExcelStyles _styles;
 
-  /// <summary>
-  /// Creates a new instance of the ExcelWorkbook class.
-  /// </summary>
-  /// <param name="package">The parent package</param>
-  /// <param name="namespaceManager">NamespaceManager</param>
+  internal static ImmutableArray<string> WorkbookSchemaNodeOrder = [
+    "fileVersion",
+    "fileSharing",
+    "workbookPr",
+    "workbookProtection",
+    "bookViews",
+    "sheets",
+    "functionGroups",
+    "functionPrototypes",
+    "externalReferences",
+    "definedNames",
+    "calcPr",
+    "oleSize",
+    "customWorkbookViews",
+    "pivotCaches",
+    "smartTagPr",
+    "smartTagTypes",
+    "webPublishing",
+    "fileRecoveryPr",
+  ];
+
+  protected override ImmutableArray<string> SchemaNodeOrder => WorkbookSchemaNodeOrder;
+
   internal ExcelWorkbook(ExcelPackage package, XmlNamespaceManager namespaceManager)
       : base(namespaceManager) {
     _package = package;
-
     _names = new(this);
     _namespaceManager = namespaceManager;
+
+    WorkbookXml = package.GetOrCreateXmlDocument(
+        WorkbookUri,
+        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
+        ExcelPackage._schemaRelationships + "/officeDocument",
+        () => CreateEmptyWorkbookXml(namespaceManager));
+    _stylesXml = package.GetOrCreateXmlDocument(
+        StylesUri,
+        "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
+        ExcelPackage._schemaRelationships + "/styles",
+        CreateEmptyStylesXml);
+
     TopNode = WorkbookXml.DocumentElement;
-    SchemaNodeOrder = new[] {
-      "fileVersion",
-      "fileSharing",
-      "workbookPr",
-      "workbookProtection",
-      "bookViews",
-      "sheets",
-      "functionGroups",
-      "functionPrototypes",
-      "externalReferences",
-      "definedNames",
-      "calcPr",
-      "oleSize",
-      "customWorkbookViews",
-      "pivotCaches",
-      "smartTagPr",
-      "smartTagTypes",
-      "webPublishing",
-      "fileRecoveryPr",
-    };
     FullCalcOnLoad = true; //Full calculation on load by default, for both new workbooks and templates.
+
     GetSharedStrings();
+    GetExternalReferences();
+    GetDefinedNames();
   }
 
-  internal Dictionary<string, SharedStringItem> _sharedStrings = new(); //Used when reading cells.
+  private static XmlDocument CreateEmptyWorkbookXml(XmlNamespaceManager namespaceManager) {
+    var result = new XmlDocument(namespaceManager.NameTable);
+    var wbElem = result.CreateElement("workbook", ExcelPackage._schemaMain);
+
+    // Add the relationships namespace
+    wbElem.SetAttribute("xmlns:r", ExcelPackage._schemaRelationships);
+    result.AppendChild(wbElem);
+
+    // Create the bookViews and workbooks element
+    var bookViews = result.CreateElement("bookViews", ExcelPackage._schemaMain);
+    wbElem.AppendChild(bookViews);
+    var workbookView = result.CreateElement("workbookView", ExcelPackage._schemaMain);
+    bookViews.AppendChild(workbookView);
+
+    return result;
+  }
+
+  private static XmlDocument CreateEmptyStylesXml() {
+    StringBuilder xml = new StringBuilder(
+        "<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">");
+    xml.Append("<numFmts />");
+    xml.Append("<fonts count=\"1\"><font><sz val=\"11\" /><name val=\"Calibri\" /></font></fonts>");
+    xml.Append(
+        "<fills><fill><patternFill patternType=\"none\" /></fill><fill><patternFill patternType=\"gray125\" /></fill></fills>");
+    xml.Append(
+        "<borders><border><left /><right /><top /><bottom /><diagonal /></border></borders>");
+    xml.Append("<cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" /></cellStyleXfs>");
+    xml.Append("<cellXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" xfId=\"0\" /></cellXfs>");
+    xml.Append("<cellStyles><cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\" /></cellStyles>");
+    xml.Append("<dxfs count=\"0\" />");
+    xml.Append("</styleSheet>");
+
+    var result = new XmlDocument();
+    result.LoadXml(xml.ToString());
+    return result;
+  }
+
+  internal readonly Dictionary<string, SharedStringItem> _sharedStrings = new(); //Used when reading cells.
   internal List<SharedStringItem> _sharedStringsList = new(); //Used when reading cells.
   internal ExcelNamedRangeCollection _names;
-  internal int _nextDrawingID = 0;
   internal int _nextTableID = int.MinValue;
   internal int _nextPivotTableID = int.MinValue;
-  internal XmlNamespaceManager _namespaceManager;
-  internal FormulaParser _formulaParser;
-  internal FormulaParserManager _parserManager;
+  private readonly XmlNamespaceManager _namespaceManager;
+  private FormulaParser _formulaParser;
+  private FormulaParserManager _parserManager;
   internal CellStore<List<Token>> _formulaTokens;
 
   /// <summary>
@@ -135,7 +184,7 @@
   /// </summary>
   private void GetSharedStrings() {
     if (_package.Package.PartExists(SharedStringsUri)) {
-      var xml = _package.GetXmlFromUri(SharedStringsUri);
+      var xml = _package.GetXmlDocument(SharedStringsUri);
       XmlNodeList nl = xml.SelectNodes("//d:sst/d:si", NameSpaceManager);
       _sharedStringsList = new();
       if (nl != null) {
@@ -273,7 +322,7 @@
   public ExcelWorksheets Worksheets {
     get {
       if (_worksheets == null) {
-        var sheetsNode = _workbookXml.DocumentElement.SelectSingleNode(
+        var sheetsNode = WorkbookXml.DocumentElement.SelectSingleNode(
             "d:sheets",
             _namespaceManager);
         if (sheetsNode == null) {
@@ -314,15 +363,7 @@
   /// <summary>
   /// Access properties to protect or unprotect a workbook
   /// </summary>
-  public ExcelProtection Protection {
-    get {
-      if (_protection == null) {
-        _protection = new(NameSpaceManager, TopNode, this);
-        _protection.SchemaNodeOrder = SchemaNodeOrder;
-      }
-      return _protection;
-    }
-  }
+  public ExcelProtection Protection => _protection ??= new(NameSpaceManager, TopNode);
 
   private ExcelWorkbookView _view;
 
@@ -346,31 +387,22 @@
   /// <summary>
   /// URI to the styles inside the package
   /// </summary>
-  internal static Uri StylesUri { get; } = new("/xl/styles.xml", UriKind.Relative);
+  private static Uri StylesUri { get; } = new("/xl/styles.xml", UriKind.Relative);
 
   /// <summary>
   /// URI to the shared strings inside the package
   /// </summary>
-  internal static Uri SharedStringsUri { get; } = new("/xl/sharedStrings.xml", UriKind.Relative);
+  private static Uri SharedStringsUri { get; } = new("/xl/sharedStrings.xml", UriKind.Relative);
 
   /// <summary>
   /// Returns a reference to the workbook's part within the package
   /// </summary>
   internal ZipPackagePart Part => (_package.Package.GetPart(WorkbookUri));
 
-  private XmlDocument _workbookXml;
-
   /// <summary>
   /// Provides access to the XML data representing the workbook in the package.
   /// </summary>
-  public XmlDocument WorkbookXml {
-    get {
-      if (_workbookXml == null) {
-        CreateWorkbookXml(_namespaceManager);
-      }
-      return (_workbookXml);
-    }
-  }
+  internal XmlDocument WorkbookXml { get; }
 
   private const string _codeModuleNamePath = "d:workbookPr/@codeName";
 
@@ -405,101 +437,7 @@
     }
   }
 
-  /// <summary>
-  /// Create or read the XML for the workbook.
-  /// </summary>
-  private void CreateWorkbookXml(XmlNamespaceManager namespaceManager) {
-    if (_package.Package.PartExists(WorkbookUri)) {
-      _workbookXml = _package.GetXmlFromUri(WorkbookUri);
-    } else {
-      // create a new workbook part and add to the package
-      ZipPackagePart partWorkbook = _package.Package.CreatePart(
-          WorkbookUri,
-          "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
-          _package.Compression);
-
-      // create the workbook
-      _workbookXml = new(namespaceManager.NameTable);
-
-      _workbookXml.PreserveWhitespace = ExcelPackage._preserveWhitespace;
-      // create the workbook element
-      XmlElement wbElem = _workbookXml.CreateElement("workbook", ExcelPackage._schemaMain);
-
-      // Add the relationships namespace
-      wbElem.SetAttribute("xmlns:r", ExcelPackage._schemaRelationships);
-
-      _workbookXml.AppendChild(wbElem);
-
-      // create the bookViews and workbooks element
-      XmlElement bookViews = _workbookXml.CreateElement("bookViews", ExcelPackage._schemaMain);
-      wbElem.AppendChild(bookViews);
-      XmlElement workbookView = _workbookXml.CreateElement(
-          "workbookView",
-          ExcelPackage._schemaMain);
-      bookViews.AppendChild(workbookView);
-
-      // save it to the package
-      StreamWriter stream = new StreamWriter(
-          partWorkbook.GetStream(FileMode.Create, FileAccess.Write));
-      _workbookXml.Save(stream);
-    }
-  }
-
-  private XmlDocument _stylesXml;
-
-  /// <summary>
-  /// Provides access to the XML data representing the styles in the package.
-  /// </summary>
-  public XmlDocument StylesXml {
-    get {
-      if (_stylesXml == null) {
-        if (_package.Package.PartExists(StylesUri)) {
-          _stylesXml = _package.GetXmlFromUri(StylesUri);
-        } else {
-          // create a new styles part and add to the package
-          ZipPackagePart part = _package.Package.CreatePart(
-              StylesUri,
-              "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
-              _package.Compression);
-          // create the style sheet
-
-          StringBuilder xml = new StringBuilder(
-              "<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">");
-          xml.Append("<numFmts />");
-          xml.Append(
-              "<fonts count=\"1\"><font><sz val=\"11\" /><name val=\"Calibri\" /></font></fonts>");
-          xml.Append(
-              "<fills><fill><patternFill patternType=\"none\" /></fill><fill><patternFill patternType=\"gray125\" /></fill></fills>");
-          xml.Append(
-              "<borders><border><left /><right /><top /><bottom /><diagonal /></border></borders>");
-          xml.Append("<cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" /></cellStyleXfs>");
-          xml.Append(
-              "<cellXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" xfId=\"0\" /></cellXfs>");
-          xml.Append(
-              "<cellStyles><cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\" /></cellStyles>");
-          xml.Append("<dxfs count=\"0\" />");
-          xml.Append("</styleSheet>");
-
-          _stylesXml = new();
-          _stylesXml.LoadXml(xml.ToString());
-
-          //Save it to the package
-          StreamWriter stream = new StreamWriter(part.GetStream(FileMode.Create, FileAccess.Write));
-
-          _stylesXml.Save(stream);
-          //stream.Close();
-
-          // create the relationship between the workbook and the new shared strings part
-          Part.CreateRelationship(
-              UriHelper.GetRelativeUri(WorkbookUri, StylesUri),
-              TargetMode.Internal,
-              ExcelPackage._schemaRelationships + "/styles");
-        }
-      }
-      return (_stylesXml);
-    }
-    set => _stylesXml = value;
-  }
+  private readonly XmlDocument _stylesXml;
 
   /// <summary>
   /// Package styles collection. Used internally to access style data.
@@ -507,7 +445,7 @@
   public ExcelStyles Styles {
     get {
       if (_styles == null) {
-        _styles = new(NameSpaceManager, StylesXml, this);
+        _styles = new(NameSpaceManager, _stylesXml, this);
       }
       return _styles;
     }
@@ -527,14 +465,14 @@
     }
   }
 
-  private readonly string CALC_MODE_PATH = "d:calcPr/@calcMode";
+  private readonly string _calcModePath = "d:calcPr/@calcMode";
 
   /// <summary>
   /// Calculation mode for the workbook.
   /// </summary>
   public ExcelCalcMode CalcMode {
     get {
-      string calcMode = GetXmlNodeString(CALC_MODE_PATH);
+      string calcMode = GetXmlNodeString(_calcModePath);
       switch (calcMode) {
         case "autoNoTable":
           return ExcelCalcMode.AutomaticNoTable;
@@ -547,13 +485,13 @@
     set {
       switch (value) {
         case ExcelCalcMode.AutomaticNoTable:
-          SetXmlNodeString(CALC_MODE_PATH, "autoNoTable");
+          SetXmlNodeString(_calcModePath, "autoNoTable");
           break;
         case ExcelCalcMode.Manual:
-          SetXmlNodeString(CALC_MODE_PATH, "manual");
+          SetXmlNodeString(_calcModePath, "manual");
           break;
         default:
-          SetXmlNodeString(CALC_MODE_PATH, "auto");
+          SetXmlNodeString(_calcModePath, "auto");
           break;
       }
     }
@@ -570,44 +508,16 @@
     set => SetXmlNodeBool(_fullCalcOnLoadPath, value);
   }
 
-  /// <summary>
-  /// Saves the workbook and all its components to the package.
-  /// For internal use only!
-  /// </summary>
-  internal void Save() // Workbook Save
-  {
+  internal void Save() {
     if (Worksheets.Count == 0) {
       throw new InvalidOperationException("The workbook must contain at least one worksheet");
     }
 
     DeleteCalcChain();
-
-    const string vbaPartUri = "/xl/vbaProject.bin";
-    if (!_package.Package.PartExists(new(vbaPartUri, UriKind.Relative))) {
-      if (Part.ContentType != ExcelPackage._contentTypeWorkbookDefault) {
-        Part.ContentType = ExcelPackage._contentTypeWorkbookDefault;
-      }
-    } else {
-      if (Part.ContentType != ExcelPackage._contentTypeWorkbookMacroEnabled) {
-        Part.ContentType = ExcelPackage._contentTypeWorkbookMacroEnabled;
-      }
-    }
-
     UpdateDefinedNamesXml();
 
-    // save the workbook
-    if (_workbookXml != null) {
-      _package.SavePart(WorkbookUri, _workbookXml);
-    }
-
-    // save the properties of the workbook
-    if (_properties != null) {
-      _properties.Save();
-    }
-
     // save the style sheet
     Styles.UpdateXml();
-    _package.SavePart(StylesUri, _stylesXml);
 
     // save all the open worksheets
     var isProtected = Protection.LockWindows || Protection.LockStructure;
@@ -616,19 +526,16 @@
         worksheet.View.WindowProtection = true;
       }
       worksheet.Save();
-      worksheet.Part.SaveHandler = worksheet.SaveHandler;
     }
 
-    var part = _package.Package.CreatePart(
+    _package.Package.CreatePart(
         SharedStringsUri,
         ExcelPackage._contentTypeSharedString,
-        _package.Compression);
-    part.SaveHandler = SaveSharedStringHandler;
+        SaveSharedStringHandler);
     Part.CreateRelationship(
         UriHelper.GetRelativeUri(WorkbookUri, SharedStringsUri),
         TargetMode.Internal,
         ExcelPackage._schemaRelationships + "/sharedStrings");
-    //UpdateSharedStringsXml();
 
     // Data validation
     ValidateDataValidations();
diff --git a/EPPlus/ExcelWorkbookView.cs b/EPPlus/ExcelWorkbookView.cs
index 6772936..26a2430 100644
--- a/EPPlus/ExcelWorkbookView.cs
+++ b/EPPlus/ExcelWorkbookView.cs
@@ -30,6 +30,7 @@
  * Jan Källman		    License changed GPL-->LGPL 2011-12-27
  *******************************************************************************/
 
+using System.Collections.Immutable;
 using System.Globalization;
 using System.Xml;
 
@@ -39,6 +40,9 @@
 /// Access to workbook view properties
 /// </summary>
 public class ExcelWorkbookView : XmlHelper {
+  protected override ImmutableArray<string> SchemaNodeOrder =>
+    ExcelWorkbook.WorkbookSchemaNodeOrder;
+
   /// <summary>
   /// Creates a new ExcelWorkbookView which provides access to all the
   /// view states of the worksheet.
@@ -47,9 +51,7 @@
   /// <param name="node"></param>
   /// <param name="wb"></param>
   internal ExcelWorkbookView(XmlNamespaceManager ns, XmlNode node, ExcelWorkbook wb)
-      : base(ns, node) {
-    SchemaNodeOrder = wb.SchemaNodeOrder;
-  }
+      : base(ns, node) {}
 
   private const string _leftPath = "d:bookViews/d:workbookView/@xWindow";
 
diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs
index 33d2dd8..02fe010 100644
--- a/EPPlus/ExcelWorksheet.cs
+++ b/EPPlus/ExcelWorksheet.cs
@@ -34,6 +34,7 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.ComponentModel;
 using System.Globalization;
 using System.IO;
@@ -89,13 +90,12 @@
       XmlNamespaceManager ns,
       ExcelPackage pck,
       ExcelWorkbook workbook,
-      string relId,
-      Uri uriWorksheet,
-      string sheetName,
+      Uri worksheetUri,
+      string name,
       int sheetId,
       int positionId,
       eWorkSheetHidden hidden)
-      : base(ns, pck, workbook, relId, uriWorksheet, sheetName, sheetId, positionId, hidden) {}
+      : base(ns, pck, workbook, worksheetUri, name, sheetId, positionId, hidden) {}
 }
 
 /// <summary>
@@ -190,7 +190,7 @@
   public class MergeCellsCollection : IEnumerable<string> {
     internal MergeCellsCollection() {}
 
-    internal CellStore<int> _cells = new();
+    internal readonly CellStore<int> _cells = new();
     private readonly List<string> _list = new();
 
     internal List<string> List => _list;
@@ -212,17 +212,13 @@
     public string this[int index] => _list[index];
 
     internal void Add(ExcelAddressBase address, bool doValidate) {
-      int ix = 0;
-
       //Validate
       if (doValidate && Validate(address) == false) {
         throw (new ArgumentException("Can't merge and already merged range"));
       }
-      lock (this) {
-        ix = _list.Count;
-        _list.Add(address.Address);
-        SetIndex(address, ix);
-      }
+      var ix = _list.Count;
+      _list.Add(address.Address);
+      SetIndex(address, ix);
     }
 
     private bool Validate(ExcelAddressBase address) {
@@ -329,143 +325,136 @@
     }
   }
 
-  internal CellStore<object> _values;
-  internal CellStore<string> _types;
-  internal CellStore<int> _styles;
-  internal CellStore<object> _formulas;
-  internal FlagCellStore _flags;
+  internal readonly CellStore<object> _values = new();
+  internal readonly CellStore<string> _types = new();
+  internal readonly CellStore<int> _styles = new();
+  internal readonly CellStore<object> _formulas = new();
+  internal readonly FlagCellStore _flags = new();
   internal CellStore<List<Token>> _formulaTokens;
 
-  internal CellStore<Uri> _hyperLinks;
-  internal CellStore<ExcelComment> _commentsStore;
+  internal readonly CellStore<Uri> _hyperLinks = new();
+  internal readonly CellStore<ExcelComment> _commentsStore = new();
 
-  internal Dictionary<int, Formulas> _sharedFormulas = new();
-  internal int _minCol = ExcelPackage.MaxColumns;
-  internal int _maxCol = 0;
+  internal readonly Dictionary<int, Formulas> _sharedFormulas = new();
 
   internal readonly ExcelPackage _package;
   private readonly ExcelWorkbook _workbook;
-  private readonly Uri _worksheetUri;
-  private string _name;
-  private readonly int _sheetID;
-  private int _positionID;
-  private readonly string _relationshipID;
-  private XmlDocument _worksheetXml;
   private ExcelWorksheetView _sheetView;
-  private ExcelHeaderFooter _headerFooter;
+
+  internal static ImmutableArray<string> WorksheetSchemaNodeOrder = [
+    "sheetPr",
+    "tabColor",
+    "outlinePr",
+    "pageSetUpPr",
+    "dimension",
+    "sheetViews",
+    "sheetFormatPr",
+    "cols",
+    "sheetData",
+    "sheetProtection",
+    "protectedRanges",
+    "scenarios",
+    "autoFilter",
+    "sortState",
+    "dataConsolidate",
+    "customSheetViews",
+    "customSheetViews",
+    "mergeCells",
+    "phoneticPr",
+    "conditionalFormatting",
+    "dataValidations",
+    "hyperlinks",
+    "printOptions",
+    "pageMargins",
+    "pageSetup",
+    "headerFooter",
+    "linePrint",
+    "rowBreaks",
+    "colBreaks",
+    "customProperties",
+    "cellWatches",
+    "ignoredErrors",
+    "smartTags",
+    "drawing",
+    "legacyDrawing",
+    "legacyDrawingHF",
+    "picture",
+    "oleObjects",
+    "activeXControls",
+    "webPublishItems",
+    "tableParts",
+    "extLst",
+  ];
+
+  protected override ImmutableArray<string> SchemaNodeOrder => WorksheetSchemaNodeOrder;
 
   internal ExcelWorksheet(
       XmlNamespaceManager ns,
       ExcelPackage excelPackage,
       ExcelWorkbook workbook,
-      string relId,
-      Uri uriWorksheet,
-      string sheetName,
+      Uri worksheetUri,
+      string name,
       int sheetId,
       int positionId,
-      eWorkSheetHidden hide)
+      eWorkSheetHidden hidden)
       : base(ns, null) {
     _workbook = workbook;
-    SchemaNodeOrder = new[] {
-      "sheetPr",
-      "tabColor",
-      "outlinePr",
-      "pageSetUpPr",
-      "dimension",
-      "sheetViews",
-      "sheetFormatPr",
-      "cols",
-      "sheetData",
-      "sheetProtection",
-      "protectedRanges",
-      "scenarios",
-      "autoFilter",
-      "sortState",
-      "dataConsolidate",
-      "customSheetViews",
-      "customSheetViews",
-      "mergeCells",
-      "phoneticPr",
-      "conditionalFormatting",
-      "dataValidations",
-      "hyperlinks",
-      "printOptions",
-      "pageMargins",
-      "pageSetup",
-      "headerFooter",
-      "linePrint",
-      "rowBreaks",
-      "colBreaks",
-      "customProperties",
-      "cellWatches",
-      "ignoredErrors",
-      "smartTags",
-      "drawing",
-      "legacyDrawing",
-      "legacyDrawingHF",
-      "picture",
-      "oleObjects",
-      "activeXControls",
-      "webPublishItems",
-      "tableParts",
-      "extLst",
-    };
     _package = excelPackage;
-    _workbook = workbook;
-    _relationshipID = relId;
-    _worksheetUri = uriWorksheet;
-    _name = sheetName;
-    _sheetID = sheetId;
-    _positionID = positionId;
-    Hidden = hide;
-
-    /**** Cellstore ****/
-    _values = new();
-    _types = new();
-    _styles = new();
-    _formulas = new();
-    _flags = new();
-    _commentsStore = new();
-    _hyperLinks = new();
-
     _names = new(Workbook, this);
 
-    CreateXml();
-    TopNode = _worksheetXml.DocumentElement;
-  }
+    Hidden = hidden;
+    Name = name;
+    PositionID = positionId;
+    SheetID = sheetId;
 
-  /// <summary>
-  /// The Uri to the worksheet within the package
-  /// </summary>
-  internal Uri WorksheetUri => (_worksheetUri);
+    Part = _package.Package.GetPart(worksheetUri);
+    Part.SaveHandler = SaveHandler;
+
+    // First Columns, rows, cells, mergecells, hyperlinks and pagebreakes are loaded from an
+    // XmlTextReader to optimize speed.
+    using var stream = Part.GetStream();
+    var xr = new XmlTextReader(stream);
+    xr.ProhibitDtd = true;
+    xr.WhitespaceHandling = WhitespaceHandling.None;
+    LoadColumns(xr); //columnXml
+    long start = stream.Position;
+    LoadCells(xr);
+    var nextElementLength = GetAttributeLength(xr);
+    long end = stream.Position - nextElementLength;
+    LoadMergeCells(xr);
+    LoadHyperLinks(xr);
+    LoadRowPageBreakes(xr);
+    LoadColPageBreakes(xr);
+
+    // Then the rest of the XML is extracted and loaded into the WorksheetXml document.
+    stream.Seek(0, SeekOrigin.Begin);
+    var xml = GetWorkSheetXml(stream, start, end, out var encoding);
+
+    //first char is invalid sometimes??
+    if (xml[0] != '<') {
+      LoadXmlSafe(WorksheetXml, xml.Substring(1, xml.Length - 1), encoding);
+    } else {
+      LoadXmlSafe(WorksheetXml, xml, encoding);
+    }
+    ClearNodes();
+
+    TopNode = WorksheetXml.DocumentElement;
+  }
 
   /// <summary>
   /// The Zip.ZipPackagePart for the worksheet within the package
   /// </summary>
-  internal ZipPackagePart Part => (_package.Package.GetPart(WorksheetUri));
-
-  /// <summary>
-  /// The ID for the worksheet's relationship with the workbook in the package
-  /// </summary>
-  internal string RelationshipID => (_relationshipID);
+  internal ZipPackagePart Part { get; }
 
   /// <summary>
   /// The unique identifier for the worksheet.
   /// </summary>
-  internal int SheetID => (_sheetID);
+  internal int SheetID { get; }
 
   /// <summary>
   /// The position of the worksheet.
   /// </summary>
-  internal int PositionID {
-    get => (_positionID);
-    set => _positionID = value;
-  }
-
-  /// <summary>
-  /// The index in the worksheets collection
-  /// </summary>
-  public int Index => (_positionID);
+  internal int PositionID { get; }
 
   /// <summary>
   /// Address for autofilter
@@ -513,47 +502,9 @@
   /// <summary>
   /// The worksheet's display name as it appears on the tab
   /// </summary>
-  public string Name {
-    get => (_name);
-    set {
-      if (value == _name) {
-        return;
-      }
-      value = _workbook.Worksheets.ValidateFixSheetName(value);
-      foreach (var ws in Workbook.Worksheets) {
-        if (ws.PositionID != PositionID
-            && ws.Name.Equals(value, StringComparison.InvariantCultureIgnoreCase)) {
-          throw (new ArgumentException("Worksheet name must be unique"));
-        }
-      }
-      _workbook.SetXmlNodeString(
-          string.Format("d:sheets/d:sheet[@sheetId={0}]/@name", _sheetID),
-          value);
-      ChangeNames(value);
+  public string Name { get; }
 
-      _name = value;
-    }
-  }
-
-  private void ChangeNames(string value) {
-    //Renames name in this Worksheet;
-    foreach (var n in Workbook.Names) {
-      if (string.IsNullOrEmpty(n.NameFormula) && n.NameValue == null) {
-        n.ChangeWorksheet(_name, value);
-      }
-    }
-    foreach (var ws in Workbook.Worksheets) {
-      if (!(ws is ExcelChartsheet)) {
-        foreach (var n in ws.Names) {
-          if (string.IsNullOrEmpty(n.NameFormula) && n.NameValue == null) {
-            n.ChangeWorksheet(_name, value);
-          }
-        }
-      }
-    }
-  }
-
-  internal ExcelNamedRangeCollection _names;
+  private readonly ExcelNamedRangeCollection _names;
 
   /// <summary>
   /// Provides access to named ranges
@@ -571,7 +522,7 @@
   public eWorkSheetHidden Hidden {
     get {
       string state = _workbook.GetXmlNodeString(
-          string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", _sheetID));
+          string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", SheetID));
       if (state == "hidden") {
         return eWorkSheetHidden.Hidden;
       }
@@ -582,13 +533,13 @@
     }
     set {
       if (value == eWorkSheetHidden.Visible) {
-        _workbook.DeleteNode(string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", _sheetID));
+        _workbook.DeleteNode(string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", SheetID));
       } else {
         string v;
         v = value.ToString();
         v = v.Substring(0, 1).ToLower(CultureInfo.InvariantCulture) + v.Substring(1);
         _workbook.SetXmlNodeString(
-            string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", _sheetID),
+            string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", SheetID),
             v);
       }
     }
@@ -704,15 +655,11 @@
     set => SetXmlNodeString(_codeModuleNamePath, value);
   }
 
-  internal void CodeNameChange(string value) {
-    CodeModuleName = value;
-  }
-
   /// <summary>
   /// The XML document holding the worksheet data.
   /// All column, row, cell, pagebreak, merged cell and hyperlink-data are loaded into memory and removed from the document when loading the document.
   /// </summary>
-  public XmlDocument WorksheetXml => (_worksheetXml);
+  internal XmlDocument WorksheetXml { get; } = new();
 
   internal ExcelVmlDrawingCommentCollection _vmlDrawings;
 
@@ -745,7 +692,7 @@
   }
 
   private void CreateVmlCollection() {
-    var vmlNode = _worksheetXml.DocumentElement.SelectSingleNode(
+    var vmlNode = WorksheetXml.DocumentElement.SelectSingleNode(
         "d:legacyDrawing/@r:id",
         NameSpaceManager);
     if (vmlNode == null) {
@@ -761,41 +708,6 @@
     }
   }
 
-  private void CreateXml() {
-    _worksheetXml = new();
-    _worksheetXml.PreserveWhitespace = ExcelPackage._preserveWhitespace;
-    ZipPackagePart packPart = _package.Package.GetPart(WorksheetUri);
-    string xml = "";
-
-    // First Columns, rows, cells, mergecells, hyperlinks and pagebreakes are loaded from a xmlstream to optimize speed...
-    Stream stream = packPart.GetStream();
-
-    XmlTextReader xr = new XmlTextReader(stream);
-    xr.ProhibitDtd = true;
-    xr.WhitespaceHandling = WhitespaceHandling.None;
-    LoadColumns(xr); //columnXml
-    long start = stream.Position;
-    LoadCells(xr);
-    var nextElementLength = GetAttributeLength(xr);
-    long end = stream.Position - nextElementLength;
-    LoadMergeCells(xr);
-    LoadHyperLinks(xr);
-    LoadRowPageBreakes(xr);
-    LoadColPageBreakes(xr);
-    //...then the rest of the Xml is extracted and loaded into the WorksheetXml document.
-    stream.Seek(0, SeekOrigin.Begin);
-    xml = GetWorkSheetXml(stream, start, end, out var encoding);
-
-    //first char is invalid sometimes??
-    if (xml[0] != '<') {
-      LoadXmlSafe(_worksheetXml, xml.Substring(1, xml.Length - 1), encoding);
-    } else {
-      LoadXmlSafe(_worksheetXml, xml, encoding);
-    }
-
-    ClearNodes();
-  }
-
   /// <summary>
   /// Get the lenth of the attributes
   /// Conditional formatting attributes can be extremly long som get length of the attributes to finetune position.
@@ -850,20 +762,20 @@
   }
 
   private void ClearNodes() {
-    if (_worksheetXml.SelectSingleNode("//d:cols", NameSpaceManager) != null) {
-      _worksheetXml.SelectSingleNode("//d:cols", NameSpaceManager).RemoveAll();
+    if (WorksheetXml.SelectSingleNode("//d:cols", NameSpaceManager) != null) {
+      WorksheetXml.SelectSingleNode("//d:cols", NameSpaceManager).RemoveAll();
     }
-    if (_worksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager) != null) {
-      _worksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager).RemoveAll();
+    if (WorksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager) != null) {
+      WorksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager).RemoveAll();
     }
-    if (_worksheetXml.SelectSingleNode("//d:hyperlinks", NameSpaceManager) != null) {
-      _worksheetXml.SelectSingleNode("//d:hyperlinks", NameSpaceManager).RemoveAll();
+    if (WorksheetXml.SelectSingleNode("//d:hyperlinks", NameSpaceManager) != null) {
+      WorksheetXml.SelectSingleNode("//d:hyperlinks", NameSpaceManager).RemoveAll();
     }
-    if (_worksheetXml.SelectSingleNode("//d:rowBreaks", NameSpaceManager) != null) {
-      _worksheetXml.SelectSingleNode("//d:rowBreaks", NameSpaceManager).RemoveAll();
+    if (WorksheetXml.SelectSingleNode("//d:rowBreaks", NameSpaceManager) != null) {
+      WorksheetXml.SelectSingleNode("//d:rowBreaks", NameSpaceManager).RemoveAll();
     }
-    if (_worksheetXml.SelectSingleNode("//d:colBreaks", NameSpaceManager) != null) {
-      _worksheetXml.SelectSingleNode("//d:colBreaks", NameSpaceManager).RemoveAll();
+    if (WorksheetXml.SelectSingleNode("//d:colBreaks", NameSpaceManager) != null) {
+      WorksheetXml.SelectSingleNode("//d:colBreaks", NameSpaceManager).RemoveAll();
     }
   }
 
@@ -1250,11 +1162,6 @@
         break;
       }
     }
-    //if (cell != null) cellList.Add(cell);
-
-    //_cells = new RangeCollection(cellList);
-    //_rows = new RangeCollection(rowList);
-    //_formulaCells = new RangeCollection(formulaList);
   }
 
   private bool DoAddRow(XmlTextReader xr) {
@@ -1277,16 +1184,6 @@
         }
         if (xr.NodeType == XmlNodeType.Element) {
           string address = xr.GetAttribute("ref");
-          //int fromRow, fromCol, toRow, toCol;
-          //ExcelCellBase.GetRowColFromAddress(address, out fromRow, out fromCol, out toRow, out toCol);
-          //for (int row = fromRow; row <= toRow; row++)
-          //{
-          //    for (int col = fromCol; col <= toCol; col++)
-          //    {
-          //        _flags.SetFlagValue(row, col, true,CellFlags.Merged);
-          //    }
-          //}
-          //_mergedCells.List.Add(address);
           _mergedCells.Add(new ExcelAddress(address), false);
         }
       }
@@ -1475,45 +1372,6 @@
     //}
   }
 
-  //private string GetSharedString(int stringID)
-  //{
-  //    string retValue = null;
-  //    XmlNodeList stringNodes = xlPackage.Workbook.SharedStringsXml.SelectNodes(string.Format("//d:si", stringID), NameSpaceManager);
-  //    XmlNode stringNode = stringNodes[stringID];
-  //    if (stringNode != null)
-  //        retValue = stringNode.InnerText;
-  //    return (retValue);
-  //}
-
-  /// <summary>
-  /// A reference to the header and footer class which allows you to
-  /// set the header and footer for all odd, even and first pages of the worksheet
-  /// </summary>
-  /// <remarks>
-  /// To format the text you can use the following format
-  /// <list type="table">
-  /// <listheader><term>Prefix</term><description>Description</description></listheader>
-  /// <item><term>&amp;U</term><description>Underlined</description></item>
-  /// <item><term>&amp;E</term><description>Double Underline</description></item>
-  /// <item><term>&amp;K:xxxxxx</term><description>Color. ex &amp;K:FF0000 for red</description></item>
-  /// <item><term>&amp;"Font,Regular Bold Italic"</term><description>Changes the font. Regular or Bold or Italic or Bold Italic can be used. ex &amp;"Arial,Bold Italic"</description></item>
-  /// <item><term>&amp;nn</term><description>Change font size. nn is an integer. ex &amp;24</description></item>
-  /// <item><term>&amp;G</term><description>Placeholder for images. Images can not be added by the library, but its possible to use in a template.</description></item>
-  /// </list>
-  /// </remarks>
-  public ExcelHeaderFooter HeaderFooter {
-    get {
-      if (_headerFooter == null) {
-        XmlNode headerFooterNode = TopNode.SelectSingleNode("d:headerFooter", NameSpaceManager);
-        if (headerFooterNode == null) {
-          headerFooterNode = CreateNode("d:headerFooter");
-        }
-        _headerFooter = new(NameSpaceManager, headerFooterNode, this);
-      }
-      return (_headerFooter);
-    }
-  }
-
   ///// <summary>
   ///// Provides access to an individual cell within the worksheet.
   ///// </summary>
@@ -1772,58 +1630,56 @@
               "Can't insert. Rows will be shifted outside the boundries of the worksheet."));
     }
 
-    lock (this) {
-      _values.Insert(rowFrom, 0, rows, 0);
-      _formulas.Insert(rowFrom, 0, rows, 0);
-      _styles.Insert(rowFrom, 0, rows, 0);
-      _types.Insert(rowFrom, 0, rows, 0);
-      _commentsStore.Insert(rowFrom, 0, rows, 0);
-      _hyperLinks.Insert(rowFrom, 0, rows, 0);
-      _flags.Insert(rowFrom, 0, rows, 0);
+    _values.Insert(rowFrom, 0, rows, 0);
+    _formulas.Insert(rowFrom, 0, rows, 0);
+    _styles.Insert(rowFrom, 0, rows, 0);
+    _types.Insert(rowFrom, 0, rows, 0);
+    _commentsStore.Insert(rowFrom, 0, rows, 0);
+    _hyperLinks.Insert(rowFrom, 0, rows, 0);
+    _flags.Insert(rowFrom, 0, rows, 0);
 
-      foreach (var f in _sharedFormulas.Values) {
-        if (f.StartRow >= rowFrom) {
-          f.StartRow += rows;
-        }
-        var a = new ExcelAddressBase(f.Address);
-        if (a._fromRow >= rowFrom) {
-          a._fromRow += rows;
-          a._toRow += rows;
-        } else if (a._toRow >= rowFrom) {
-          a._toRow += rows;
-        }
-        f.Address = ExcelCellBase.GetAddress(a._fromRow, a._fromCol, a._toRow, a._toCol);
-        f.Formula = ExcelCellBase.UpdateFormulaReferences(f.Formula, rows, 0, rowFrom, 0);
+    foreach (var f in _sharedFormulas.Values) {
+      if (f.StartRow >= rowFrom) {
+        f.StartRow += rows;
       }
-      var cse = new CellsStoreEnumerator<object>(_formulas);
-      while (cse.Next()) {
-        if (cse.Value is string) {
-          cse.Value = ExcelCellBase.UpdateFormulaReferences(
-              cse.Value.ToString(),
-              rows,
-              0,
-              rowFrom,
-              0);
-        }
+      var a = new ExcelAddressBase(f.Address);
+      if (a._fromRow >= rowFrom) {
+        a._fromRow += rows;
+        a._toRow += rows;
+      } else if (a._toRow >= rowFrom) {
+        a._toRow += rows;
       }
-
-      FixMergedCellsRow(rowFrom, rows, false);
-      if (copyStylesFromRow > 0) {
-        var cseS = new CellsStoreEnumerator<int>(
-            _styles,
-            copyStylesFromRow,
+      f.Address = ExcelCellBase.GetAddress(a._fromRow, a._fromCol, a._toRow, a._toCol);
+      f.Formula = ExcelCellBase.UpdateFormulaReferences(f.Formula, rows, 0, rowFrom, 0);
+    }
+    var cse = new CellsStoreEnumerator<object>(_formulas);
+    while (cse.Next()) {
+      if (cse.Value is string) {
+        cse.Value = ExcelCellBase.UpdateFormulaReferences(
+            cse.Value.ToString(),
+            rows,
             0,
-            copyStylesFromRow,
-            ExcelPackage.MaxColumns); //Fixes issue 15068 , 15090
-        while (cseS.Next()) {
-          for (var r = 0; r < rows; r++) {
-            _styles.SetValue(rowFrom + r, cseS.Column, cseS.Value);
-          }
+            rowFrom,
+            0);
+      }
+    }
+
+    FixMergedCellsRow(rowFrom, rows, false);
+    if (copyStylesFromRow > 0) {
+      var cseS = new CellsStoreEnumerator<int>(
+          _styles,
+          copyStylesFromRow,
+          0,
+          copyStylesFromRow,
+          ExcelPackage.MaxColumns); //Fixes issue 15068 , 15090
+      while (cseS.Next()) {
+        for (var r = 0; r < rows; r++) {
+          _styles.SetValue(rowFrom + r, cseS.Column, cseS.Value);
         }
       }
-      foreach (var tbl in Tables) {
-        tbl.Address = tbl.Address.AddRow(rowFrom, rows);
-      }
+    }
+    foreach (var tbl in Tables) {
+      tbl.Address = tbl.Address.AddRow(rowFrom, rows);
     }
   }
 
@@ -1859,87 +1715,85 @@
               "Can't insert. Columns will be shifted outside the boundries of the worksheet."));
     }
 
-    lock (this) {
-      _values.Insert(0, columnFrom, 0, columns);
-      _formulas.Insert(0, columnFrom, 0, columns);
-      _styles.Insert(0, columnFrom, 0, columns);
-      _types.Insert(0, columnFrom, 0, columns);
-      _commentsStore.Insert(0, columnFrom, 0, columns);
-      _hyperLinks.Insert(0, columnFrom, 0, columns);
-      _flags.Insert(0, columnFrom, 0, columns);
+    _values.Insert(0, columnFrom, 0, columns);
+    _formulas.Insert(0, columnFrom, 0, columns);
+    _styles.Insert(0, columnFrom, 0, columns);
+    _types.Insert(0, columnFrom, 0, columns);
+    _commentsStore.Insert(0, columnFrom, 0, columns);
+    _hyperLinks.Insert(0, columnFrom, 0, columns);
+    _flags.Insert(0, columnFrom, 0, columns);
 
-      foreach (var f in _sharedFormulas.Values) {
-        if (f.StartCol >= columnFrom) {
-          f.StartCol += columns;
+    foreach (var f in _sharedFormulas.Values) {
+      if (f.StartCol >= columnFrom) {
+        f.StartCol += columns;
+      }
+      var a = new ExcelAddressBase(f.Address);
+      if (a._fromCol >= columnFrom) {
+        a._fromCol += columns;
+        a._toCol += columns;
+      } else if (a._toCol >= columnFrom) {
+        a._toCol += columns;
+      }
+      f.Address = ExcelCellBase.GetAddress(a._fromRow, a._fromCol, a._toRow, a._toCol);
+      f.Formula = ExcelCellBase.UpdateFormulaReferences(f.Formula, 0, columns, 0, columnFrom);
+    }
+
+    var cse = new CellsStoreEnumerator<object>(_formulas);
+    while (cse.Next()) {
+      if (cse.Value is string) {
+        cse.Value = ExcelCellBase.UpdateFormulaReferences(
+            cse.Value.ToString(),
+            0,
+            columns,
+            0,
+            columnFrom);
+      }
+    }
+
+    FixMergedCellsColumn(columnFrom, columns, false);
+
+    var csec = new CellsStoreEnumerator<object>(_values, 0, 1, 0, ExcelPackage.MaxColumns);
+    var lst = new List<ExcelColumn>();
+    foreach (var col in csec) {
+      if (col is ExcelColumn column) {
+        lst.Add(column);
+      }
+    }
+
+    for (int i = lst.Count - 1; i >= 0; i--) {
+      var c = lst[i];
+      if (c._columnMin >= columnFrom) {
+        if (c._columnMin + columns <= ExcelPackage.MaxColumns) {
+          c._columnMin += columns;
+        } else {
+          c._columnMin = ExcelPackage.MaxColumns;
         }
-        var a = new ExcelAddressBase(f.Address);
-        if (a._fromCol >= columnFrom) {
-          a._fromCol += columns;
-          a._toCol += columns;
-        } else if (a._toCol >= columnFrom) {
-          a._toCol += columns;
+
+        if (c._columnMax + columns <= ExcelPackage.MaxColumns) {
+          c._columnMax += columns;
+        } else {
+          c._columnMax = ExcelPackage.MaxColumns;
         }
-        f.Address = ExcelCellBase.GetAddress(a._fromRow, a._fromCol, a._toRow, a._toCol);
-        f.Formula = ExcelCellBase.UpdateFormulaReferences(f.Formula, 0, columns, 0, columnFrom);
+      } else if (c._columnMax >= columnFrom) {
+        var cc = c._columnMax - columnFrom;
+        c._columnMax = columnFrom - 1;
+        CopyColumn(c, columnFrom + columns, columnFrom + columns + cc);
+      }
+    }
+
+    if (copyStylesFromColumn > 0) {
+      for (var c = 0; c < columns; c++) {
+        var col = Column(columnFrom + c);
+        col.StyleID = Column(copyStylesFromColumn).StyleID;
+      }
+    }
+    //Adjust tables
+    foreach (var tbl in Tables) {
+      if (columnFrom > tbl.Address.Start.Column && columnFrom <= tbl.Address.End.Column) {
+        InsertTableColumns(columnFrom, columns, tbl);
       }
 
-      var cse = new CellsStoreEnumerator<object>(_formulas);
-      while (cse.Next()) {
-        if (cse.Value is string) {
-          cse.Value = ExcelCellBase.UpdateFormulaReferences(
-              cse.Value.ToString(),
-              0,
-              columns,
-              0,
-              columnFrom);
-        }
-      }
-
-      FixMergedCellsColumn(columnFrom, columns, false);
-
-      var csec = new CellsStoreEnumerator<object>(_values, 0, 1, 0, ExcelPackage.MaxColumns);
-      var lst = new List<ExcelColumn>();
-      foreach (var col in csec) {
-        if (col is ExcelColumn column) {
-          lst.Add(column);
-        }
-      }
-
-      for (int i = lst.Count - 1; i >= 0; i--) {
-        var c = lst[i];
-        if (c._columnMin >= columnFrom) {
-          if (c._columnMin + columns <= ExcelPackage.MaxColumns) {
-            c._columnMin += columns;
-          } else {
-            c._columnMin = ExcelPackage.MaxColumns;
-          }
-
-          if (c._columnMax + columns <= ExcelPackage.MaxColumns) {
-            c._columnMax += columns;
-          } else {
-            c._columnMax = ExcelPackage.MaxColumns;
-          }
-        } else if (c._columnMax >= columnFrom) {
-          var cc = c._columnMax - columnFrom;
-          c._columnMax = columnFrom - 1;
-          CopyColumn(c, columnFrom + columns, columnFrom + columns + cc);
-        }
-      }
-
-      if (copyStylesFromColumn > 0) {
-        for (var c = 0; c < columns; c++) {
-          var col = Column(columnFrom + c);
-          col.StyleID = Column(copyStylesFromColumn).StyleID;
-        }
-      }
-      //Adjust tables
-      foreach (var tbl in Tables) {
-        if (columnFrom > tbl.Address.Start.Column && columnFrom <= tbl.Address.End.Column) {
-          InsertTableColumns(columnFrom, columns, tbl);
-        }
-
-        tbl.Address = tbl.Address.AddColumn(columnFrom, columns);
-      }
+      tbl.Address = tbl.Address.AddColumn(columnFrom, columns);
     }
   }
 
@@ -2064,21 +1918,19 @@
               "Row out of range. Spans from 1 to "
                   + ExcelPackage.MaxRows.ToString(CultureInfo.InvariantCulture)));
     }
-    lock (this) {
-      _values.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
-      _types.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
-      _formulas.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
-      _styles.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
-      _flags.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
-      _commentsStore.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
-      _hyperLinks.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
+    _values.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
+    _types.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
+    _formulas.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
+    _styles.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
+    _flags.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
+    _commentsStore.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
+    _hyperLinks.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns);
 
-      AdjustFormulasRow(rowFrom, rows);
-      FixMergedCellsRow(rowFrom, rows, true);
+    AdjustFormulasRow(rowFrom, rows);
+    FixMergedCellsRow(rowFrom, rows, true);
 
-      foreach (var tbl in Tables) {
-        tbl.Address = tbl.Address.DeleteRow(rowFrom, rows);
-      }
+    foreach (var tbl in Tables) {
+      tbl.Address = tbl.Address.DeleteRow(rowFrom, rows);
     }
   }
 
@@ -2101,59 +1953,52 @@
               "Column out of range. Spans from 1 to "
                   + ExcelPackage.MaxColumns.ToString(CultureInfo.InvariantCulture)));
     }
-    lock (this) {
-      var col = _values.GetValue(0, columnFrom) as ExcelColumn;
-      if (col == null) {
-        var r = 0;
-        var c = columnFrom;
-        if (_values.PrevCell(ref r, ref c)) {
-          col = _values.GetValue(0, c) as ExcelColumn;
-          if (col._columnMax >= columnFrom) {
-            col.ColumnMax = columnFrom - 1;
-          }
+    var col = _values.GetValue(0, columnFrom) as ExcelColumn;
+    if (col == null) {
+      var r = 0;
+      var c = columnFrom;
+      if (_values.PrevCell(ref r, ref c)) {
+        col = _values.GetValue(0, c) as ExcelColumn;
+        if (col._columnMax >= columnFrom) {
+          col.ColumnMax = columnFrom - 1;
         }
       }
+    }
 
-      _values.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
-      _types.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
-      _formulas.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
-      _styles.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
-      _flags.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
-      _commentsStore.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
-      _hyperLinks.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
+    _values.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
+    _types.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
+    _formulas.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
+    _styles.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
+    _flags.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
+    _commentsStore.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
+    _hyperLinks.Delete(0, columnFrom, ExcelPackage.MaxRows, columns);
 
-      AdjustFormulasColumn(columnFrom, columns);
-      FixMergedCellsColumn(columnFrom, columns, true);
+    AdjustFormulasColumn(columnFrom, columns);
+    FixMergedCellsColumn(columnFrom, columns, true);
 
-      var csec = new CellsStoreEnumerator<object>(
-          _values,
-          0,
-          columnFrom,
-          0,
-          ExcelPackage.MaxColumns);
-      foreach (var column in csec) {
-        if (column is ExcelColumn excelColumn) {
-          if (excelColumn._columnMin >= columnFrom) {
-            excelColumn._columnMin -= columns;
-            excelColumn._columnMax -= columns;
-          }
+    var csec = new CellsStoreEnumerator<object>(_values, 0, columnFrom, 0, ExcelPackage.MaxColumns);
+    foreach (var column in csec) {
+      if (column is ExcelColumn excelColumn) {
+        if (excelColumn._columnMin >= columnFrom) {
+          excelColumn._columnMin -= columns;
+          excelColumn._columnMax -= columns;
         }
       }
+    }
 
-      foreach (var tbl in Tables) {
-        if (columnFrom >= tbl.Address.Start.Column && columnFrom <= tbl.Address.End.Column) {
-          var node = tbl.Columns[0].TopNode.ParentNode;
-          var ix = columnFrom - tbl.Address.Start.Column;
-          for (int i = 0; i < columns; i++) {
-            if (node.ChildNodes.Count > ix) {
-              node.RemoveChild(node.ChildNodes[ix]);
-            }
+    foreach (var tbl in Tables) {
+      if (columnFrom >= tbl.Address.Start.Column && columnFrom <= tbl.Address.End.Column) {
+        var node = tbl.Columns[0].TopNode.ParentNode;
+        var ix = columnFrom - tbl.Address.Start.Column;
+        for (int i = 0; i < columns; i++) {
+          if (node.ChildNodes.Count > ix) {
+            node.RemoveChild(node.ChildNodes[ix]);
           }
-          tbl._cols = new(tbl);
         }
-
-        tbl.Address = tbl.Address.DeleteColumn(columnFrom, columns);
+        tbl._cols = new(tbl);
       }
+
+      tbl.Address = tbl.Address.DeleteColumn(columnFrom, columns);
     }
   }
 
@@ -2175,7 +2020,6 @@
     foreach (var ix in delSf) {
       _sharedFormulas.Remove(ix);
     }
-    delSf = null;
     var cse = new CellsStoreEnumerator<object>(
         _formulas,
         1,
@@ -2212,7 +2056,6 @@
     foreach (var ix in delSf) {
       _sharedFormulas.Remove(ix);
     }
-    delSf = null;
     var cse = new CellsStoreEnumerator<object>(
         _formulas,
         1,
@@ -2406,32 +2249,22 @@
 
   internal void Save() {
     DeletePrinterSettings();
-
-    if (_worksheetXml != null) {
-      if (!(this is ExcelChartsheet)) {
-        // save the header & footer (if defined)
-        if (_headerFooter != null) {
-          HeaderFooter.Save();
-        }
-
-        var d = Dimension;
-        if (d == null) {
-          DeleteAllNode("d:dimension/@ref");
-        } else {
-          SetXmlNodeString("d:dimension/@ref", d.Address);
-        }
-
-        SaveComments();
-        SaveTables();
-        SavePivotTables();
+    if (!(this is ExcelChartsheet)) {
+      var d = Dimension;
+      if (d == null) {
+        DeleteAllNode("d:dimension/@ref");
+      } else {
+        SetXmlNodeString("d:dimension/@ref", d.Address);
       }
+      SaveTables();
+      SavePivotTables();
     }
   }
 
   internal void SaveHandler(StreamWriter streamWriter) {
     //Create the nodes if they do not exist.
     if (this is ExcelChartsheet) {
-      streamWriter.Write(_worksheetXml.OuterXml);
+      streamWriter.Write(WorksheetXml.OuterXml);
     } else {
       CreateNode("d:cols");
       CreateNode("d:sheetData");
@@ -2441,7 +2274,7 @@
       CreateNode("d:colBreaks");
 
       //StreamWriter sw=new StreamWriter(Part.GetStream(FileMode.Create, FileAccess.Write));
-      var xml = _worksheetXml.OuterXml;
+      var xml = WorksheetXml.OuterXml;
       int colStart = 0,
           colEnd = 0;
       GetBlockPos(xml, "cols", ref colStart, ref colEnd);
@@ -2512,59 +2345,6 @@
     }
   }
 
-  private void SaveComments() {
-    if (_comments != null) {
-      if (_comments.Count == 0) {
-        if (_comments.Uri != null) {
-          Part.DeleteRelationship(_comments.RelId);
-          _package.Package.DeletePart(_comments.Uri);
-        }
-        RemoveLegacyDrawingRel(VmlDrawingsComments.RelId);
-      } else {
-        if (_comments.Uri == null) {
-          _comments.Uri = new(string.Format("/xl/comments{0}.xml", SheetID), UriKind.Relative);
-        }
-        if (_comments.Part == null) {
-          _comments.Part = _package.Package.CreatePart(
-              _comments.Uri,
-              "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
-              _package.Compression);
-          var rel = Part.CreateRelationship(
-              UriHelper.GetRelativeUri(WorksheetUri, _comments.Uri),
-              TargetMode.Internal,
-              ExcelPackage._schemaRelationships + "/comments");
-        }
-        _comments.CommentXml.Save(_comments.Part.GetStream(FileMode.Create));
-      }
-    }
-
-    if (_vmlDrawings != null) {
-      if (_vmlDrawings.Count == 0) {
-        if (_vmlDrawings.Uri != null) {
-          Part.DeleteRelationship(_vmlDrawings.RelId);
-          _package.Package.DeletePart(_vmlDrawings.Uri);
-        }
-      } else {
-        if (_vmlDrawings.Uri == null) {
-          _vmlDrawings.Uri = GetNewUri(_package.Package, "/xl/drawings/vmlDrawing{0}.vml");
-        }
-        if (_vmlDrawings.Part == null) {
-          _vmlDrawings.Part = _package.Package.CreatePart(
-              _vmlDrawings.Uri,
-              "application/vnd.openxmlformats-officedocument.vmlDrawing",
-              _package.Compression);
-          var rel = Part.CreateRelationship(
-              UriHelper.GetRelativeUri(WorksheetUri, _vmlDrawings.Uri),
-              TargetMode.Internal,
-              ExcelPackage._schemaRelationships + "/vmlDrawing");
-          SetXmlNodeString("d:legacyDrawing/@r:id", rel.Id);
-          _vmlDrawings.RelId = rel.Id;
-        }
-        _vmlDrawings.VmlDrawingXml.Save(_vmlDrawings.Part.GetStream());
-      }
-    }
-  }
-
   /// <summary>
   /// Save all table data
   /// </summary>
@@ -2614,30 +2394,6 @@
           colNum++;
         }
       }
-      if (tbl.Part == null) {
-        tbl.TableUri = GetNewUri(_package.Package, "/xl/tables/table{0}.xml", tbl.Id);
-        tbl.Part = _package.Package.CreatePart(
-            tbl.TableUri,
-            "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml",
-            Workbook._package.Compression);
-        var stream = tbl.Part.GetStream(FileMode.Create);
-        tbl.TableXml.Save(stream);
-        var rel = Part.CreateRelationship(
-            UriHelper.GetRelativeUri(WorksheetUri, tbl.TableUri),
-            TargetMode.Internal,
-            ExcelPackage._schemaRelationships + "/table");
-        tbl.RelationshipID = rel.Id;
-
-        CreateNode("d:tableParts");
-        XmlNode tbls = TopNode.SelectSingleNode("d:tableParts", NameSpaceManager);
-
-        var tblNode = tbls.OwnerDocument.CreateElement("tablePart", ExcelPackage._schemaMain);
-        tbls.AppendChild(tblNode);
-        tblNode.SetAttribute("id", ExcelPackage._schemaRelationships, rel.Id);
-      } else {
-        var stream = tbl.Part.GetStream(FileMode.Create);
-        tbl.TableXml.Save(stream);
-      }
     }
   }
 
@@ -2775,9 +2531,6 @@
           }
         }
       }
-      pt.PivotTableXml.Save(pt.Part.GetStream(FileMode.Create));
-      pt.CacheDefinition.CacheDefinitionXml.Save(
-          pt.CacheDefinition.Part.GetStream(FileMode.Create));
     }
   }
 
@@ -2896,7 +2649,6 @@
 
     int row = -1;
 
-    StringBuilder sbXml = new StringBuilder();
     var ss = _workbook._sharedStrings;
     var styles = _workbook.Styles;
     var cache = new StringBuilder();
@@ -3190,16 +2942,13 @@
                 ? ""
                 : "tooltip=\"" + SecurityElement.Escape(link.ToolTip) + "\" ");
       } else if (uri != null) {
-        string id;
         Uri hyp;
         if (uri is ExcelHyperLink hyperLink) {
           hyp = hyperLink.OriginalUri;
         } else {
           hyp = uri;
         }
-        if (hyps.ContainsKey(hyp.OriginalString)) {
-          id = hyps[hyp.OriginalString];
-        } else {
+        if (!hyps.ContainsKey(hyp.OriginalString)) {
           var relationship = Part.CreateRelationship(
               hyp,
               TargetMode.External,
@@ -3221,9 +2970,7 @@
                 ExcelCellBase.GetAddress(cse.Row, cse.Column),
                 relationship.Id);
           }
-          id = relationship.Id;
         }
-        //cell.HyperLinkRId = id;
       }
     }
     if (!first) {
@@ -3232,24 +2979,6 @@
   }
 
   /// <summary>
-  /// Create the hyperlinks node in the XML
-  /// </summary>
-  /// <returns></returns>
-  private XmlNode CreateHyperLinkCollection() {
-    XmlElement hl = _worksheetXml.CreateElement("hyperlinks", ExcelPackage._schemaMain);
-    XmlNode prevNode = _worksheetXml.SelectSingleNode(
-        "//d:conditionalFormatting",
-        NameSpaceManager);
-    if (prevNode == null) {
-      prevNode = _worksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager);
-      if (prevNode == null) {
-        prevNode = _worksheetXml.SelectSingleNode("//d:sheetData", NameSpaceManager);
-      }
-    }
-    return _worksheetXml.DocumentElement.InsertAfter(hl, prevNode);
-  }
-
-  /// <summary>
   /// Dimension address for the worksheet.
   /// Top left cell to Bottom right.
   /// If the worksheet has no cells, null is returned
@@ -3274,7 +3003,7 @@
   public ExcelSheetProtection Protection {
     get {
       if (_protection == null) {
-        _protection = new(NameSpaceManager, TopNode, this);
+        _protection = new(NameSpaceManager, TopNode);
       }
       return _protection;
     }
@@ -3285,7 +3014,7 @@
   public ExcelProtectedRangeCollection ProtectedRanges {
     get {
       if (_protectedRanges == null) {
-        _protectedRanges = new(NameSpaceManager, TopNode, this);
+        _protectedRanges = new(NameSpaceManager, TopNode);
       }
       return _protectedRanges;
     }
@@ -3399,19 +3128,6 @@
     return i;
   }
 
-  internal void SetHfLegacyDrawingRel(string relId) {
-    SetXmlNodeString("d:legacyDrawingHF/@r:id", relId);
-  }
-
-  internal void RemoveLegacyDrawingRel(string relId) {
-    var n = WorksheetXml.DocumentElement.SelectSingleNode(
-        string.Format("d:legacyDrawing[@r:id=\"{0}\"]", relId),
-        NameSpaceManager);
-    if (n != null) {
-      n.ParentNode.RemoveChild(n);
-    }
-  }
-
   internal void UpdateCellsWithDate1904Setting() {
     var cse = new CellsStoreEnumerator<object>(_values);
     var offset = Workbook.Date1904 ? -ExcelWorkbook._date1904Offset : ExcelWorkbook._date1904Offset;
diff --git a/EPPlus/ExcelWorksheetView.cs b/EPPlus/ExcelWorksheetView.cs
index bd132e5..758ec6e 100644
--- a/EPPlus/ExcelWorksheetView.cs
+++ b/EPPlus/ExcelWorksheetView.cs
@@ -31,6 +31,7 @@
  *******************************************************************************/
 
 using System;
+using System.Collections.Immutable;
 using System.Xml;
 
 namespace OfficeOpenXml;
@@ -127,6 +128,13 @@
 
   private readonly ExcelWorksheet _worksheet;
 
+  protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
+    "sheetViews",
+    "sheetView",
+    "pane",
+    "selection",
+  ];
+
   /// <summary>
   /// Creates a new ExcelWorksheetView which provides access to all the view states of the worksheet.
   /// </summary>
@@ -136,7 +144,6 @@
   internal ExcelWorksheetView(XmlNamespaceManager ns, XmlNode node, ExcelWorksheet xlWorksheet)
       : base(ns, node) {
     _worksheet = xlWorksheet;
-    SchemaNodeOrder = new[] { "sheetViews", "sheetView", "pane", "selection" };
     Panes = LoadPanes();
   }
 
diff --git a/EPPlus/ExcelWorksheets.cs b/EPPlus/ExcelWorksheets.cs
index 1ed69a2..a0e4ebb 100644
--- a/EPPlus/ExcelWorksheets.cs
+++ b/EPPlus/ExcelWorksheets.cs
@@ -33,10 +33,8 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
-using System.IO;
 using System.Text.RegularExpressions;
 using System.Xml;
-using OfficeOpenXml.Packaging;
 using OfficeOpenXml.Utils;
 
 namespace OfficeOpenXml;
@@ -47,7 +45,7 @@
 public class ExcelWorksheets : XmlHelper, IEnumerable<ExcelWorksheet> {
   private readonly ExcelPackage _pck;
   private readonly ExcelWorkbook _workbook;
-  private Dictionary<int, ExcelWorksheet> _worksheets;
+  private Dictionary<int, ExcelWorksheet> _worksheets = new();
   private readonly XmlNamespaceManager _namespaceManager;
 
   internal ExcelWorksheets(
@@ -59,7 +57,6 @@
     _pck = pck;
     _workbook = workbook;
     _namespaceManager = nsm;
-    _worksheets = new();
     int positionId = 1;
 
     foreach (XmlNode sheetNode in topNode.ChildNodes) {
@@ -89,7 +86,6 @@
                   _namespaceManager,
                   pck,
                   _workbook,
-                  relId,
                   uriWorksheet,
                   name,
                   sheetId,
@@ -102,7 +98,6 @@
                   _namespaceManager,
                   pck,
                   _workbook,
-                  relId,
                   uriWorksheet,
                   name,
                   sheetId,
@@ -152,65 +147,42 @@
   /// </summary>
   /// <param name="name">The name of the workbook</param>
   public ExcelWorksheet Add(string name) {
-    int sheetId;
-    Uri uriWorksheet;
-    lock (_worksheets) {
-      name = ValidateFixSheetName(name);
-      if (GetByName(name) != null) {
-        throw (new InvalidOperationException(_errDupWorksheet + " : " + name));
-      }
-      GetSheetUri(ref name, out sheetId, out uriWorksheet, false);
-      ZipPackagePart worksheetPart = _pck.Package.CreatePart(
-          uriWorksheet,
-          _worksheetContentType,
-          _pck.Compression);
-
-      //Create the new, empty worksheet and save it to the package
-      StreamWriter streamWorksheet = new StreamWriter(
-          worksheetPart.GetStream(FileMode.Create, FileAccess.Write));
-      XmlDocument worksheetXml = CreateNewWorksheet(false);
-      worksheetXml.Save(streamWorksheet);
-
-      string rel = CreateWorkbookRel(name, sheetId, uriWorksheet, false);
-
-      int positionId = _worksheets.Count + 1;
-      ExcelWorksheet worksheet;
-
-      {
-        worksheet = new(
-            _namespaceManager,
-            _pck,
-            _workbook,
-            rel,
-            uriWorksheet,
-            name,
-            sheetId,
-            positionId,
-            eWorkSheetHidden.Visible);
-      }
-
-      _worksheets.Add(positionId, worksheet);
-      return worksheet;
+    name = ValidateFixSheetName(name);
+    if (GetByName(name) != null) {
+      throw (new InvalidOperationException(_errDupWorksheet + " : " + name));
     }
-  }
+    GetSheetUri(ref name, out var sheetId, out var uriWorksheet, false);
 
-  private string CreateWorkbookRel(string name, int sheetId, Uri uriWorksheet, bool isChart) {
-    //Create the relationship between the workbook and the new worksheet
-    var rel = _workbook.Part.CreateRelationship(
-        UriHelper.GetRelativeUri(ExcelWorkbook.WorkbookUri, uriWorksheet),
-        TargetMode.Internal,
-        ExcelPackage._schemaRelationships + "/" + (isChart ? "chartsheet" : "worksheet"));
+    // Create the new worksheet
+    var rel = _pck.CreateXmlDocument(
+        uriWorksheet,
+        _worksheetContentType,
+        ExcelPackage._schemaRelationships + "/worksheet",
+        CreateNewWorksheet(false));
 
-    //Create the new sheet node
+    // Add worksheet to the workbook
     XmlElement worksheetNode = _workbook.WorkbookXml.CreateElement(
         "sheet",
         ExcelPackage._schemaMain);
     worksheetNode.SetAttribute("name", name);
     worksheetNode.SetAttribute("sheetId", sheetId.ToString());
     worksheetNode.SetAttribute("id", ExcelPackage._schemaRelationships, rel.Id);
-
     TopNode.AppendChild(worksheetNode);
-    return rel.Id;
+
+    int positionId = _worksheets.Count + 1;
+
+    ExcelWorksheet worksheet = new(
+        _namespaceManager,
+        _pck,
+        _workbook,
+        uriWorksheet,
+        name,
+        sheetId,
+        positionId,
+        eWorkSheetHidden.Visible);
+
+    _worksheets.Add(positionId, worksheet);
+    return worksheet;
   }
 
   private void GetSheetUri(ref string name, out int sheetId, out Uri uriWorksheet, bool isChart) {
@@ -361,181 +333,4 @@
     }
     return (xlWorksheet);
   }
-
-  /// <summary>
-  /// Moves the source worksheet to the position before the target worksheet
-  /// </summary>
-  /// <param name="sourceName">The name of the source worksheet</param>
-  /// <param name="targetName">The name of the target worksheet</param>
-  public void MoveBefore(string sourceName, string targetName) {
-    Move(sourceName, targetName, false);
-  }
-
-  /// <summary>
-  /// Moves the source worksheet to the position before the target worksheet
-  /// </summary>
-  /// <param name="sourcePositionId">The id of the source worksheet</param>
-  /// <param name="targetPositionId">The id of the target worksheet</param>
-  public void MoveBefore(int sourcePositionId, int targetPositionId) {
-    Move(sourcePositionId, targetPositionId, false);
-  }
-
-  /// <summary>
-  /// Moves the source worksheet to the position after the target worksheet
-  /// </summary>
-  /// <param name="sourceName">The name of the source worksheet</param>
-  /// <param name="targetName">The name of the target worksheet</param>
-  public void MoveAfter(string sourceName, string targetName) {
-    Move(sourceName, targetName, true);
-  }
-
-  /// <summary>
-  /// Moves the source worksheet to the position after the target worksheet
-  /// </summary>
-  /// <param name="sourcePositionId">The id of the source worksheet</param>
-  /// <param name="targetPositionId">The id of the target worksheet</param>
-  public void MoveAfter(int sourcePositionId, int targetPositionId) {
-    Move(sourcePositionId, targetPositionId, true);
-  }
-
-  /// <summary>
-  ///
-  /// </summary>
-  /// <param name="sourceName"></param>
-  public void MoveToStart(string sourceName) {
-    var sourceSheet = this[sourceName];
-    if (sourceSheet == null) {
-      throw new(
-          string.Format(
-              "Move worksheet error: Could not find worksheet to move '{0}'",
-              sourceName));
-    }
-    Move(sourceSheet.PositionID, 1, false);
-  }
-
-  /// <summary>
-  ///
-  /// </summary>
-  /// <param name="sourcePositionId"></param>
-  public void MoveToStart(int sourcePositionId) {
-    Move(sourcePositionId, 1, false);
-  }
-
-  /// <summary>
-  ///
-  /// </summary>
-  /// <param name="sourceName"></param>
-  public void MoveToEnd(string sourceName) {
-    var sourceSheet = this[sourceName];
-    if (sourceSheet == null) {
-      throw new(
-          string.Format(
-              "Move worksheet error: Could not find worksheet to move '{0}'",
-              sourceName));
-    }
-    Move(sourceSheet.PositionID, _worksheets.Count, true);
-  }
-
-  /// <summary>
-  ///
-  /// </summary>
-  /// <param name="sourcePositionId"></param>
-  public void MoveToEnd(int sourcePositionId) {
-    Move(sourcePositionId, _worksheets.Count, true);
-  }
-
-  private void Move(string sourceName, string targetName, bool placeAfter) {
-    var sourceSheet = this[sourceName];
-    if (sourceSheet == null) {
-      throw new(
-          string.Format(
-              "Move worksheet error: Could not find worksheet to move '{0}'",
-              sourceName));
-    }
-    var targetSheet = this[targetName];
-    if (targetSheet == null) {
-      throw new(
-          string.Format(
-              "Move worksheet error: Could not find worksheet to move '{0}'",
-              targetName));
-    }
-    Move(sourceSheet.PositionID, targetSheet.PositionID, placeAfter);
-  }
-
-  private void Move(int sourcePositionId, int targetPositionId, bool placeAfter) {
-    // Bugfix: if source and target are the same worksheet the following code will create a duplicate
-    //         which will cause a corrupt workbook. /swmal 2014-05-10
-    if (sourcePositionId == targetPositionId) {
-      return;
-    }
-
-    lock (_worksheets) {
-      var sourceSheet = this[sourcePositionId];
-      if (sourceSheet == null) {
-        throw new(
-            string.Format(
-                "Move worksheet error: Could not find worksheet at position '{0}'",
-                sourcePositionId));
-      }
-      var targetSheet = this[targetPositionId];
-      if (targetSheet == null) {
-        throw new(
-            string.Format(
-                "Move worksheet error: Could not find worksheet at position '{0}'",
-                targetPositionId));
-      }
-      if (sourcePositionId == targetPositionId && _worksheets.Count < 2) {
-        return; //--- no reason to attempt to re-arrange a single item with itself
-      }
-
-      var index = 1;
-      var newOrder = new Dictionary<int, ExcelWorksheet>();
-      foreach (var entry in _worksheets) {
-        if (entry.Key == targetPositionId) {
-          if (!placeAfter) {
-            sourceSheet.PositionID = index;
-            newOrder.Add(index++, sourceSheet);
-          }
-
-          entry.Value.PositionID = index;
-          newOrder.Add(index++, entry.Value);
-
-          if (placeAfter) {
-            sourceSheet.PositionID = index;
-            newOrder.Add(index++, sourceSheet);
-          }
-        } else if (entry.Key == sourcePositionId) {
-          //--- do nothing
-        } else {
-          entry.Value.PositionID = index;
-          newOrder.Add(index++, entry.Value);
-        }
-      }
-      _worksheets = newOrder;
-
-      MoveSheetXmlNode(sourceSheet, targetSheet, placeAfter);
-    }
-  }
-
-  private void MoveSheetXmlNode(
-      ExcelWorksheet sourceSheet,
-      ExcelWorksheet targetSheet,
-      bool placeAfter) {
-    lock (TopNode.OwnerDocument) {
-      var sourceNode = TopNode.SelectSingleNode(
-          string.Format("d:sheet[@sheetId = '{0}']", sourceSheet.SheetID),
-          _namespaceManager);
-      var targetNode = TopNode.SelectSingleNode(
-          string.Format("d:sheet[@sheetId = '{0}']", targetSheet.SheetID),
-          _namespaceManager);
-      if (sourceNode == null || targetNode == null) {
-        throw new("Source SheetId and Target SheetId must be valid");
-      }
-      if (placeAfter) {
-        TopNode.InsertAfter(sourceNode, targetNode);
-      } else {
-        TopNode.InsertBefore(sourceNode, targetNode);
-      }
-    }
-  }
 } // end class Worksheets
diff --git a/EPPlus/FormulaParsing/CalculateExtentions.cs b/EPPlus/FormulaParsing/CalculateExtentions.cs
index 8d8792b..afeb3ff 100644
--- a/EPPlus/FormulaParsing/CalculateExtentions.cs
+++ b/EPPlus/FormulaParsing/CalculateExtentions.cs
@@ -30,13 +30,9 @@
  *******************************************************************************/
 
 using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
 using OfficeOpenXml.FormulaParsing;
 using OfficeOpenXml.FormulaParsing.Excel.Functions;
 using OfficeOpenXml.FormulaParsing.Exceptions;
-using OfficeOpenXml.FormulaParsing.LexicalAnalysis;
 
 namespace OfficeOpenXml;
 
diff --git a/EPPlus/FormulaParsing/Excel/Functions/ArgumentParsers.cs b/EPPlus/FormulaParsing/Excel/Functions/ArgumentParsers.cs
index 86f9d5c..0a518f9 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/ArgumentParsers.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/ArgumentParsers.cs
@@ -1,4 +1,4 @@
-/* Copyright (C) 2011  Jan Källman
+/* Copyright (C) 2011  Jan Källman
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -30,7 +30,6 @@
 namespace OfficeOpenXml.FormulaParsing.Excel.Functions;
 
 public class ArgumentParsers {
-  private static readonly object _syncRoot = new();
   private readonly Dictionary<DataType, ArgumentParser> _parsers = new();
   private readonly ArgumentParserFactory _parserFactory;
 
@@ -44,10 +43,8 @@
 
   public ArgumentParser GetParser(DataType dataType) {
     if (!_parsers.ContainsKey(dataType)) {
-      lock (_syncRoot) {
-        if (!_parsers.ContainsKey(dataType)) {
-          _parsers.Add(dataType, _parserFactory.CreateArgumentParser(dataType));
-        }
+      if (!_parsers.ContainsKey(dataType)) {
+        _parsers.Add(dataType, _parserFactory.CreateArgumentParser(dataType));
       }
     }
     return _parsers[dataType];
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Database/DcountA.cs b/EPPlus/FormulaParsing/Excel/Functions/Database/DcountA.cs
index 0316950..3af4c77 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/Database/DcountA.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/Database/DcountA.cs
@@ -43,7 +43,7 @@
     ValidateArguments(arguments, 2);
     var dbAddress = arguments.ElementAt(0).ValueAsRangeInfo.Address.Address;
     string field = null;
-    string criteriaRange = null;
+    string criteriaRange;
     if (arguments.Count() == 2) {
       criteriaRange = arguments.ElementAt(1).ValueAsRangeInfo.Address.Address;
     } else {
diff --git a/EPPlus/FormulaParsing/Excel/Functions/DateTime/DateParsingFunction.cs b/EPPlus/FormulaParsing/Excel/Functions/DateTime/DateParsingFunction.cs
index fbac43c..47143ef 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/DateTime/DateParsingFunction.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/DateTime/DateParsingFunction.cs
@@ -5,7 +5,7 @@
 
 public abstract class DateParsingFunction : ExcelFunction {
   protected System.DateTime ParseDate(IEnumerable<FunctionArgument> arguments, object dateObj) {
-    System.DateTime date = System.DateTime.MinValue;
+    System.DateTime date;
     if (dateObj is string) {
       date = System.DateTime.Parse(dateObj.ToString(), CultureInfo.InvariantCulture);
     } else {
diff --git a/EPPlus/FormulaParsing/Excel/Functions/DateTime/Minute.cs b/EPPlus/FormulaParsing/Excel/Functions/DateTime/Minute.cs
index 57426d0..a93b354 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/DateTime/Minute.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/DateTime/Minute.cs
@@ -35,7 +35,7 @@
       ParsingContext context) {
     ValidateArguments(arguments, 1);
     var dateObj = arguments.ElementAt(0).Value;
-    System.DateTime date = System.DateTime.MinValue;
+    System.DateTime date;
     if (dateObj is string) {
       date = System.DateTime.Parse(dateObj.ToString());
     } else {
diff --git a/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeBaseFunction.cs b/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeBaseFunction.cs
index 879fc64..4b2ef4a 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeBaseFunction.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeBaseFunction.cs
@@ -28,11 +28,7 @@
 namespace OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;
 
 public abstract class TimeBaseFunction : ExcelFunction {
-  public TimeBaseFunction() {
-    TimeStringParser = new();
-  }
-
-  protected TimeStringParser TimeStringParser { get; private set; }
+  protected TimeStringParser TimeStringParser { get; private set; } = new();
 
   protected double SerialNumber { get; private set; }
 
diff --git a/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeStringParser.cs b/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeStringParser.cs
index 533528d..0c403b6 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeStringParser.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeStringParser.cs
@@ -71,8 +71,7 @@
   }
 
   private double Parse12HourTimeString(string input) {
-    string dayPart = string.Empty;
-    dayPart = input.Substring(input.Length - 2, 2);
+    var dayPart = input.Substring(input.Length - 2, 2);
     GetValuesFromString(input, out var hour, out var minute, out var second);
     if (dayPart == "PM") {
       hour += 12;
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Math/AverageIf.cs b/EPPlus/FormulaParsing/Excel/Functions/Math/AverageIf.cs
index 0423f98..5568a25 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/Math/AverageIf.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/Math/AverageIf.cs
@@ -76,7 +76,7 @@
     ThrowExcelErrorValueExceptionIf(
         () => criteria == null || criteria.ToString().Length > 255,
         eErrorType.Value);
-    var retVal = 0d;
+    double retVal;
     if (arguments.Count() > 2) {
       var secondArg = arguments.ElementAt(2);
       var lookupRange = secondArg.Value as IEnumerable<FunctionArgument>;
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Math/Subtotal.cs b/EPPlus/FormulaParsing/Excel/Functions/Math/Subtotal.cs
index cc82beb..17d8586 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/Math/Subtotal.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/Math/Subtotal.cs
@@ -81,8 +81,7 @@
       return CreateResult(0d, DataType.Decimal);
     }
     var actualArgs = arguments.Skip(1);
-    ExcelFunction function = null;
-    function = GetFunctionByCalcType(funcNum);
+    var function = GetFunctionByCalcType(funcNum);
     var compileResult = function.Execute(actualArgs, context);
     compileResult.IsResultOfSubtotal = true;
     return compileResult;
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Math/SumIf.cs b/EPPlus/FormulaParsing/Excel/Functions/Math/SumIf.cs
index 232d460..8d8d61b 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/Math/SumIf.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/Math/SumIf.cs
@@ -53,7 +53,7 @@
     ThrowExcelErrorValueExceptionIf(
         () => criteria == null || criteria.ToString().Length > 255,
         eErrorType.Value);
-    var retVal = 0d;
+    double retVal;
     if (arguments.Count() > 2) {
       var sumRange = arguments.ElementAt(2).Value as ExcelDataProvider.IRangeInfo; //IEnumerable<FunctionArgument>;
       retVal = CalculateWithSumRange(args, criteria.ToString(), sumRange, context);
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Math/VarMethods.cs b/EPPlus/FormulaParsing/Excel/Functions/Math/VarMethods.cs
index d12ddf8..6061b74 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/Math/VarMethods.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/Math/VarMethods.cs
@@ -39,13 +39,13 @@
 
   public static double Var(IEnumerable<double> args) {
     double avg = args.Average();
-    double d = args.Aggregate(0.0, (total, next) => total += System.Math.Pow(next - avg, 2));
+    double d = args.Aggregate(0.0, (total, next) => total + System.Math.Pow(next - avg, 2));
     return Divide(d, (args.Count() - 1));
   }
 
   public static double VarP(IEnumerable<double> args) {
     double avg = args.Average();
-    double d = args.Aggregate(0.0, (total, next) => total += System.Math.Pow(next - avg, 2));
+    double d = args.Aggregate(0.0, (total, next) => total + System.Math.Pow(next - avg, 2));
     return Divide(d, args.Count());
   }
 }
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs b/EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs
index 2a05519..e5ee106 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs
@@ -24,7 +24,7 @@
       ParsingContext context) {
     ValidateArguments(arguments, 1);
     var val = ArgToString(arguments, 0).TrimEnd(' ');
-    double result = 0d;
+    double result;
     if (Regex.IsMatch(
         val,
         $"^[\\d]*({Regex.Escape(_groupSeparator)}?[\\d]*)?({Regex.Escape(_decimalSeparator)
diff --git a/EPPlus/FormulaParsing/ExcelCalculationOption.cs b/EPPlus/FormulaParsing/ExcelCalculationOption.cs
index a94d5ef..79d03bc 100644
--- a/EPPlus/FormulaParsing/ExcelCalculationOption.cs
+++ b/EPPlus/FormulaParsing/ExcelCalculationOption.cs
@@ -1,9 +1,5 @@
 namespace OfficeOpenXml.FormulaParsing;
 
 public class ExcelCalculationOption {
-  public ExcelCalculationOption() {
-    AllowCirculareReferences = false;
-  }
-
-  public bool AllowCirculareReferences { get; set; }
+  public bool AllowCirculareReferences { get; set; } = false;
 }
diff --git a/EPPlus/FormulaParsing/ExcelUtilities/ExcelAddressInfo.cs b/EPPlus/FormulaParsing/ExcelUtilities/ExcelAddressInfo.cs
index afba2c1..c7dd453 100644
--- a/EPPlus/FormulaParsing/ExcelUtilities/ExcelAddressInfo.cs
+++ b/EPPlus/FormulaParsing/ExcelUtilities/ExcelAddressInfo.cs
@@ -36,7 +36,6 @@
 public class ExcelAddressInfo {
   private ExcelAddressInfo(string address) {
     var addressOnSheet = address;
-    Worksheet = string.Empty;
     if (address.Contains("!")) {
       var worksheetArr = address.Split('!');
       Worksheet = worksheetArr[0];
@@ -57,7 +56,7 @@
     return new(address);
   }
 
-  public string Worksheet { get; private set; }
+  public string Worksheet { get; private set; } = string.Empty;
 
   public bool WorksheetIsSpecified => !string.IsNullOrEmpty(Worksheet);
 
diff --git a/EPPlus/FormulaParsing/ExcelUtilities/RangeAddress.cs b/EPPlus/FormulaParsing/ExcelUtilities/RangeAddress.cs
index cf62744..b46145c 100644
--- a/EPPlus/FormulaParsing/ExcelUtilities/RangeAddress.cs
+++ b/EPPlus/FormulaParsing/ExcelUtilities/RangeAddress.cs
@@ -32,11 +32,7 @@
 namespace OfficeOpenXml.FormulaParsing.ExcelUtilities;
 
 public class RangeAddress {
-  public RangeAddress() {
-    Address = string.Empty;
-  }
-
-  internal string Address { get; set; }
+  internal string Address { get; set; } = string.Empty;
 
   public string Worksheet { get; internal set; }
 
diff --git a/EPPlus/FormulaParsing/ExpressionGraph/Expression.cs b/EPPlus/FormulaParsing/ExpressionGraph/Expression.cs
index e32e3f0..bdab90a 100644
--- a/EPPlus/FormulaParsing/ExpressionGraph/Expression.cs
+++ b/EPPlus/FormulaParsing/ExpressionGraph/Expression.cs
@@ -77,7 +77,7 @@
   }
 
   public virtual Expression MergeWithNext() {
-    var expression = this;
+    Expression expression;
     if (Next != null && Operator != null) {
       var result = Operator.Apply(Compile(), Next.Compile());
       expression = ExpressionConverter.Instance.FromCompileResult(result);
diff --git a/EPPlus/FormulaParsing/LexicalAnalysis/TokenFactory.cs b/EPPlus/FormulaParsing/LexicalAnalysis/TokenFactory.cs
index 6ba0d9b..4639753 100644
--- a/EPPlus/FormulaParsing/LexicalAnalysis/TokenFactory.cs
+++ b/EPPlus/FormulaParsing/LexicalAnalysis/TokenFactory.cs
@@ -70,9 +70,9 @@
     var tokenList = (IList<Token>)tokens;
     //Address with worksheet-string before  /JK
     if (token.StartsWith("!") && tokenList[tokenList.Count - 1].TokenType == TokenType.String) {
-      string addr = "";
       var i = tokenList.Count - 2;
       if (i > 0) {
+        string addr;
         if (tokenList[i].TokenType == TokenType.StringContent) {
           addr = "'" + tokenList[i].Value.Replace("'", "''") + "'";
         } else {
diff --git a/EPPlus/FormulaParsing/LexicalAnalysis/TokenSeparatorProvider.cs b/EPPlus/FormulaParsing/LexicalAnalysis/TokenSeparatorProvider.cs
index 337f2a2..d56dc18 100644
--- a/EPPlus/FormulaParsing/LexicalAnalysis/TokenSeparatorProvider.cs
+++ b/EPPlus/FormulaParsing/LexicalAnalysis/TokenSeparatorProvider.cs
@@ -34,10 +34,9 @@
 namespace OfficeOpenXml.FormulaParsing.LexicalAnalysis;
 
 public class TokenSeparatorProvider : ITokenSeparatorProvider {
-  private static readonly Dictionary<string, Token> _tokens;
+  private static readonly Dictionary<string, Token> _tokens = new();
 
   static TokenSeparatorProvider() {
-    _tokens = new();
     _tokens.Add("+", new("+", TokenType.Operator));
     _tokens.Add("-", new("-", TokenType.Operator));
     _tokens.Add("*", new("*", TokenType.Operator));
diff --git a/EPPlus/FormulaParsing/LexicalAnalysis/TokenizerContext.cs b/EPPlus/FormulaParsing/LexicalAnalysis/TokenizerContext.cs
index ef9c31a..130324d 100644
--- a/EPPlus/FormulaParsing/LexicalAnalysis/TokenizerContext.cs
+++ b/EPPlus/FormulaParsing/LexicalAnalysis/TokenizerContext.cs
@@ -40,13 +40,11 @@
     if (!string.IsNullOrEmpty(formula)) {
       _chars = formula.ToArray();
     }
-    _result = new();
-    _currentToken = new();
   }
 
   private readonly char[] _chars;
-  private readonly List<Token> _result;
-  private StringBuilder _currentToken;
+  private readonly List<Token> _result = new();
+  private StringBuilder _currentToken = new();
 
   public char[] FormulaChars => _chars;
 
diff --git a/EPPlus/FormulaParsing/ParsingConfiguration.cs b/EPPlus/FormulaParsing/ParsingConfiguration.cs
index 71440b3..90792b4 100644
--- a/EPPlus/FormulaParsing/ParsingConfiguration.cs
+++ b/EPPlus/FormulaParsing/ParsingConfiguration.cs
@@ -15,11 +15,9 @@
 
   public IExpressionCompiler ExpressionCompiler { get; private set; }
 
-  public FunctionRepository FunctionRepository { get; private set; }
+  public FunctionRepository FunctionRepository { get; private set; } = FunctionRepository.Create();
 
-  private ParsingConfiguration() {
-    FunctionRepository = FunctionRepository.Create();
-  }
+  private ParsingConfiguration() {}
 
   internal static ParsingConfiguration Create() {
     return new();
diff --git a/EPPlus/FormulaParsing/ParsingScope.cs b/EPPlus/FormulaParsing/ParsingScope.cs
index 1e861c5..f901f1e 100644
--- a/EPPlus/FormulaParsing/ParsingScope.cs
+++ b/EPPlus/FormulaParsing/ParsingScope.cs
@@ -16,13 +16,12 @@
     _parsingScopes = parsingScopes;
     Parent = parent;
     Address = address;
-    ScopeId = Guid.NewGuid();
   }
 
   /// <summary>
   /// Id of the scope.
   /// </summary>
-  public Guid ScopeId { get; private set; }
+  public Guid ScopeId { get; private set; } = Guid.NewGuid();
 
   /// <summary>
   /// The calling scope.
diff --git a/EPPlus/OfficeProperties.cs b/EPPlus/OfficeProperties.cs
index 1b0a6be..e41d226 100644
--- a/EPPlus/OfficeProperties.cs
+++ b/EPPlus/OfficeProperties.cs
@@ -34,10 +34,7 @@
 
 using System;
 using System.Globalization;
-using System.IO;
 using System.Xml;
-using OfficeOpenXml.Packaging;
-using OfficeOpenXml.Utils;
 
 namespace OfficeOpenXml;
 
@@ -45,18 +42,14 @@
 /// Provides access to the properties bag of the package
 /// </summary>
 public sealed class OfficeProperties : XmlHelper {
-  private XmlDocument _xmlPropertiesCore;
-  private XmlDocument _xmlPropertiesExtended;
-  private XmlDocument _xmlPropertiesCustom;
+  private readonly XmlDocument _xmlPropertiesCore;
+  private readonly XmlDocument _xmlPropertiesExtended;
 
   private readonly Uri _uriPropertiesCore = new("/docProps/core.xml", UriKind.Relative);
   private readonly Uri _uriPropertiesExtended = new("/docProps/app.xml", UriKind.Relative);
-  private readonly Uri _uriPropertiesCustom = new("/docProps/custom.xml", UriKind.Relative);
 
   private readonly XmlHelper _coreHelper;
   private readonly XmlHelper _extendedHelper;
-  private XmlHelper _customHelper;
-  private readonly ExcelPackage _package;
 
   /// <summary>
   /// Provides access to all the office document properties.
@@ -65,67 +58,25 @@
   /// <param name="ns"></param>
   internal OfficeProperties(ExcelPackage package, XmlNamespaceManager ns)
       : base(ns) {
-    _package = package;
-
+    const string coreBaseXml =
+        $"""<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><cp:coreProperties xmlns:cp="{ExcelPackage._schemaCore}" xmlns:dc="{ExcelPackage._schemaDc}" xmlns:dcterms="{ExcelPackage._schemaDcTerms}" xmlns:dcmitype="{ExcelPackage._schemaDcmiType}" xmlns:xsi="{ExcelPackage._schemaXsi}"></cp:coreProperties>""";
+    _xmlPropertiesCore = package.GetOrCreateXmlDocument(
+        _uriPropertiesCore,
+        "application/vnd.openxmlformats-package.core-properties+xml",
+        "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
+        coreBaseXml);
     _coreHelper = XmlHelperFactory.Create(
         ns,
-        CorePropertiesXml.SelectSingleNode("cp:coreProperties", NameSpaceManager));
-    _extendedHelper = XmlHelperFactory.Create(ns, ExtendedPropertiesXml);
-    _customHelper = XmlHelperFactory.Create(ns, CustomPropertiesXml);
-  }
+        _xmlPropertiesCore.SelectSingleNode("cp:coreProperties", NameSpaceManager));
 
-  /// <summary>
-  /// Provides access to the XML document that holds all the code
-  /// document properties.
-  /// </summary>
-  public XmlDocument CorePropertiesXml {
-    get {
-      if (_xmlPropertiesCore == null) {
-        string xml = string.Format(
-            "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><cp:coreProperties xmlns:cp=\"{0}\" xmlns:dc=\"{1}\" xmlns:dcterms=\"{2}\" xmlns:dcmitype=\"{3}\" xmlns:xsi=\"{4}\"></cp:coreProperties>",
-            ExcelPackage._schemaCore,
-            ExcelPackage._schemaDc,
-            ExcelPackage._schemaDcTerms,
-            ExcelPackage._schemaDcmiType,
-            ExcelPackage._schemaXsi);
-
-        _xmlPropertiesCore = GetXmlDocument(
-            xml,
-            _uriPropertiesCore,
-            "application/vnd.openxmlformats-package.core-properties+xml",
-            "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties");
-      }
-      return (_xmlPropertiesCore);
-    }
-  }
-
-  private XmlDocument GetXmlDocument(
-      string startXml,
-      Uri uri,
-      string contentType,
-      string relationship) {
-    XmlDocument xmlDoc;
-    if (_package.Package.PartExists(uri)) {
-      xmlDoc = _package.GetXmlFromUri(uri);
-    } else {
-      xmlDoc = new();
-      xmlDoc.LoadXml(startXml);
-
-      // Create a the part and add to the package
-      ZipPackagePart part = _package.Package.CreatePart(uri, contentType);
-
-      // Save it to the package
-      StreamWriter stream = new StreamWriter(part.GetStream(FileMode.Create, FileAccess.Write));
-      xmlDoc.Save(stream);
-      //stream.Close();
-
-      // create the relationship between the workbook and the new shared strings part
-      _package.Package.CreateRelationship(
-          UriHelper.GetRelativeUri(new("/xl", UriKind.Relative), uri),
-          TargetMode.Internal,
-          relationship);
-    }
-    return xmlDoc;
+    const string extendedBaseXml =
+        $"""<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><Properties xmlns:vt="{ExcelPackage._schemaVt}" xmlns="{ExcelPackage._schemaExtended}"></Properties>""";
+    _xmlPropertiesExtended = package.GetOrCreateXmlDocument(
+        _uriPropertiesExtended,
+        "application/vnd.openxmlformats-officedocument.extended-properties+xml",
+        "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties",
+        extendedBaseXml);
+    _extendedHelper = XmlHelperFactory.Create(ns, _xmlPropertiesExtended);
   }
 
   private const string _titlePath = "dc:title";
@@ -240,16 +191,7 @@
   /// </summary>
   public XmlDocument ExtendedPropertiesXml {
     get {
-      if (_xmlPropertiesExtended == null) {
-        _xmlPropertiesExtended = GetXmlDocument(
-            string.Format(
-                "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><Properties xmlns:vt=\"{0}\" xmlns=\"{1}\"></Properties>",
-                ExcelPackage._schemaVt,
-                ExcelPackage._schemaExtended),
-            _uriPropertiesExtended,
-            "application/vnd.openxmlformats-officedocument.extended-properties+xml",
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties");
-      }
+      if (_xmlPropertiesExtended == null) {}
       return (_xmlPropertiesExtended);
     }
   }
@@ -320,142 +262,4 @@
       _coreHelper.SetXmlNodeString(_modifiedPath + "/@xsi:type", "dcterms:W3CDTF");
     }
   }
-
-  /// <summary>
-  /// Provides access to the XML document which holds the document's custom properties
-  /// </summary>
-  public XmlDocument CustomPropertiesXml {
-    get {
-      if (_xmlPropertiesCustom == null) {
-        _xmlPropertiesCustom = GetXmlDocument(
-            string.Format(
-                "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><Properties xmlns:vt=\"{0}\" xmlns=\"{1}\"></Properties>",
-                ExcelPackage._schemaVt,
-                ExcelPackage._schemaCustom),
-            _uriPropertiesCustom,
-            "application/vnd.openxmlformats-officedocument.custom-properties+xml",
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties");
-      }
-      return (_xmlPropertiesCustom);
-    }
-  }
-
-  /// <summary>
-  /// Gets the value of a custom property
-  /// </summary>
-  /// <param name="propertyName">The name of the property</param>
-  /// <returns>The current value of the property</returns>
-  public object GetCustomPropertyValue(string propertyName) {
-    string searchString = string.Format("ctp:Properties/ctp:property[@name='{0}']", propertyName);
-    XmlElement node =
-        CustomPropertiesXml.SelectSingleNode(searchString, NameSpaceManager) as XmlElement;
-    if (node != null) {
-      string value = node.LastChild.InnerText;
-      switch (node.LastChild.LocalName) {
-        case "filetime":
-          if (DateTime.TryParse(value, out var dt)) {
-            return dt;
-          }
-          return null;
-        case "i4":
-          if (int.TryParse(value, out var i)) {
-            return i;
-          }
-          return null;
-        case "r8":
-          if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var d)) {
-            return d;
-          }
-          return null;
-        case "bool":
-          if (value == "true") {
-            return true;
-          }
-          if (value == "false") {
-            return false;
-          }
-          return null;
-        default:
-          return value;
-      }
-    }
-    return null;
-  }
-
-  /// <summary>
-  /// Allows you to set the value of a current custom property or create your own custom property.
-  /// </summary>
-  /// <param name="propertyName">The name of the property</param>
-  /// <param name="value">The value of the property</param>
-  public void SetCustomPropertyValue(string propertyName, object value) {
-    XmlNode allProps = CustomPropertiesXml.SelectSingleNode("ctp:Properties", NameSpaceManager);
-
-    var prop = string.Format("ctp:Properties/ctp:property[@name='{0}']", propertyName);
-    XmlElement node = CustomPropertiesXml.SelectSingleNode(prop, NameSpaceManager) as XmlElement;
-    if (node == null) {
-      int pid;
-      var maxNode = CustomPropertiesXml.SelectSingleNode(
-          "ctp:Properties/ctp:property[not(@pid <= preceding-sibling::ctp:property/@pid) and not(@pid <= following-sibling::ctp:property/@pid)]",
-          NameSpaceManager);
-      if (maxNode == null) {
-        pid = 2;
-      } else {
-        if (!int.TryParse(maxNode.Attributes["pid"].Value, out pid)) {
-          pid = 2;
-        }
-        pid++;
-      }
-      node = CustomPropertiesXml.CreateElement("property", ExcelPackage._schemaCustom);
-      node.SetAttribute("fmtid", "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}");
-      node.SetAttribute("pid", pid.ToString()); // custom property pid
-      node.SetAttribute("name", propertyName);
-
-      allProps.AppendChild(node);
-    } else {
-      while (node.ChildNodes.Count > 0) {
-        node.RemoveChild(node.ChildNodes[0]);
-      }
-    }
-    XmlElement valueElem;
-    if (value is bool) {
-      valueElem = CustomPropertiesXml.CreateElement("vt", "bool", ExcelPackage._schemaVt);
-      valueElem.InnerText = value.ToString().ToLower(CultureInfo.InvariantCulture);
-    } else if (value is DateTime time) {
-      valueElem = CustomPropertiesXml.CreateElement("vt", "filetime", ExcelPackage._schemaVt);
-      valueElem.InnerText = time.AddHours(-1).ToString("yyyy-MM-ddTHH:mm:ssZ");
-    } else if (value is short || value is int) {
-      valueElem = CustomPropertiesXml.CreateElement("vt", "i4", ExcelPackage._schemaVt);
-      valueElem.InnerText = value.ToString();
-    } else if (value is double || value is decimal || value is float || value is long) {
-      valueElem = CustomPropertiesXml.CreateElement("vt", "r8", ExcelPackage._schemaVt);
-      if (value is double d) {
-        valueElem.InnerText = d.ToString(CultureInfo.InvariantCulture);
-      } else if (value is float f) {
-        valueElem.InnerText = f.ToString(CultureInfo.InvariantCulture);
-      } else if (value is decimal value1) {
-        valueElem.InnerText = value1.ToString(CultureInfo.InvariantCulture);
-      } else {
-        valueElem.InnerText = value.ToString();
-      }
-    } else {
-      valueElem = CustomPropertiesXml.CreateElement("vt", "lpwstr", ExcelPackage._schemaVt);
-      valueElem.InnerText = value.ToString();
-    }
-    node.AppendChild(valueElem);
-  }
-
-  /// <summary>
-  /// Saves the document properties back to the package.
-  /// </summary>
-  internal void Save() {
-    if (_xmlPropertiesCore != null) {
-      _package.SavePart(_uriPropertiesCore, _xmlPropertiesCore);
-    }
-    if (_xmlPropertiesExtended != null) {
-      _package.SavePart(_uriPropertiesExtended, _xmlPropertiesExtended);
-    }
-    if (_xmlPropertiesCustom != null) {
-      _package.SavePart(_uriPropertiesCustom, _xmlPropertiesCustom);
-    }
-  }
 }
diff --git a/EPPlus/Packaging/ZipPackage.cs b/EPPlus/Packaging/ZipPackage.cs
index 843d105..921c744 100644
--- a/EPPlus/Packaging/ZipPackage.cs
+++ b/EPPlus/Packaging/ZipPackage.cs
@@ -35,6 +35,7 @@
 using System.IO;
 using System.IO.Compression;
 using System.Linq;
+using System.Runtime.InteropServices;
 using System.Text;
 using System.Xml;
 using OfficeOpenXml.Utils;
@@ -59,7 +60,7 @@
 /// <summary>
 /// Represent an OOXML Zip package.
 /// </summary>
-public class ZipPackage : ZipPackageRelationshipBase {
+internal class ZipPackage : ZipPackageRelationshipBase {
   internal class ContentType {
     internal string Name;
     internal bool IsExtension;
@@ -111,10 +112,13 @@
             if (e.FullName.EndsWith(".rels", StringComparison.InvariantCultureIgnoreCase)) {
               rels.Add(GetUriKey(e.FullName), e);
             } else {
+              var data = new byte[e.Length];
               using var inputStream = e.Open();
-              var part = new ZipPackagePart(this, e);
-              part.Stream = new();
-              inputStream.CopyTo(part.Stream);
+              inputStream.ReadExactly(data);
+              var part = new ZipPackagePart(
+                  this,
+                  e,
+                  ImmutableCollectionsMarshal.AsImmutableArray(data));
               Parts.Add(GetUriKey(e.FullName), part);
             }
           }
@@ -158,24 +162,16 @@
     }
   }
 
-  internal ZipPackagePart CreatePart(Uri partUri, string contentType) {
-    return CreatePart(partUri, contentType, CompressionLevel.Default);
-  }
-
-  internal ZipPackagePart CreatePart(
-      Uri partUri,
-      string contentType,
-      CompressionLevel compressionLevel) {
+  internal void CreatePart(Uri partUri, string contentType, Action<StreamWriter> saveHandler) {
     if (PartExists(partUri)) {
       throw (new InvalidOperationException("Part already exist"));
     }
 
-    var part = new ZipPackagePart(this, partUri, contentType, compressionLevel);
+    var part = new ZipPackagePart(this, partUri, contentType, saveHandler);
     _contentTypes.Add(
         GetUriKey(part.Uri.OriginalString),
         new(contentType, false, part.Uri.OriginalString));
     Parts.Add(GetUriKey(part.Uri.OriginalString), part);
-    return part;
   }
 
   internal ZipPackagePart GetPart(Uri partUri) {
@@ -223,7 +219,6 @@
     while (rels.Count > 0) {
       rels.Remove(rels.First().Id);
     }
-    rels = null;
     _contentTypes.Remove(GetUriKey(uri.OriginalString));
     //remove all relations
     Parts.Remove(GetUriKey(uri.OriginalString));
@@ -231,13 +226,13 @@
 
   internal void Save(Stream stream) {
     using var zipArchive = new ZipArchive(stream, ZipArchiveMode.Create, leaveOpen: true);
-    /**** ContentType****/
+    // Content types
     var contentTypesEntry = zipArchive.CreateEntry("[Content_Types].xml");
     using (var contentTypesWriter = new StreamWriter(contentTypesEntry.Open())) {
       contentTypesWriter.Write(GetContentTypeXml());
     }
-    /**** Top Rels ****/
-    _rels.WriteZip(zipArchive, "_rels\\.rels");
+    // Top Rels
+    _rels.WriteZip(zipArchive, "_rels/.rels");
     ZipPackagePart ssPart = null;
     foreach (var part in Parts.Values) {
       if (part.ContentType != ExcelPackage._contentTypeSharedString) {
@@ -250,7 +245,6 @@
     if (ssPart != null) {
       ssPart.WriteZip(zipArchive);
     }
-    //return ms;
   }
 
   private string GetContentTypeXml() {
@@ -258,29 +252,12 @@
         "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">");
     foreach (ContentType ct in _contentTypes.Values) {
       if (ct.IsExtension) {
-        xml.AppendFormat("<Default ContentType=\"{0}\" Extension=\"{1}\"/>", ct.Name, ct.Match);
+        xml.Append($"<Default ContentType=\"{ct.Name}\" Extension=\"{ct.Match}\"/>");
       } else {
-        xml.AppendFormat(
-            "<Override ContentType=\"{0}\" PartName=\"{1}\" />",
-            ct.Name,
-            GetUriKey(ct.Match));
+        xml.Append($"<Override ContentType=\"{ct.Name}\" PartName=\"{GetUriKey(ct.Match)}\" />");
       }
     }
     xml.Append("</Types>");
     return xml.ToString();
   }
-
-  private CompressionLevel _compression = CompressionLevel.Default;
-
-  public CompressionLevel Compression {
-    get => _compression;
-    set {
-      foreach (var part in Parts.Values) {
-        if (part.CompressionLevel == _compression) {
-          part.CompressionLevel = value;
-        }
-      }
-      _compression = value;
-    }
-  }
 }
diff --git a/EPPlus/Packaging/ZipPackagePart.cs b/EPPlus/Packaging/ZipPackagePart.cs
index 407428d..47d20fb 100644
--- a/EPPlus/Packaging/ZipPackagePart.cs
+++ b/EPPlus/Packaging/ZipPackagePart.cs
@@ -30,42 +30,35 @@
  *******************************************************************************/
 
 using System;
+using System.Collections.Immutable;
 using System.IO;
 using System.IO.Compression;
+using System.Runtime.InteropServices;
 
 namespace OfficeOpenXml.Packaging;
 
 internal class ZipPackagePart : ZipPackageRelationshipBase, IDisposable {
-  internal delegate void SaveHandlerDelegate(StreamWriter streamWriter);
-
-  internal ZipPackagePart(ZipPackage package, ZipArchiveEntry entry) {
+  internal ZipPackagePart(ZipPackage package, ZipArchiveEntry entry, ImmutableArray<byte> data) {
     Package = package;
     SaveHandler = null;
     Uri = new(package.GetUriKey(entry.FullName), UriKind.Relative);
+    _data = data;
   }
 
+  private readonly ImmutableArray<byte> _data;
+
   internal ZipPackagePart(
       ZipPackage package,
       Uri partUri,
       string contentType,
-      CompressionLevel compressionLevel) {
+      Action<StreamWriter> saveHandler) {
     Package = package;
-    //Entry = new ZipEntry();
-    //Entry.FileName = partUri.OriginalString.Replace('/','\\');
     Uri = partUri;
     ContentType = contentType;
-    CompressionLevel = compressionLevel;
+    SaveHandler = saveHandler;
   }
 
-  internal ZipPackage Package { get; set; }
-
-  internal CompressionLevel CompressionLevel;
-  private MemoryStream _stream;
-
-  internal MemoryStream Stream {
-    get => _stream;
-    set => _stream = value;
-  }
+  private ZipPackage Package { get; }
 
   internal override ZipPackageRelationship CreateRelationship(
       Uri targetUri,
@@ -77,20 +70,7 @@
   }
 
   internal MemoryStream GetStream() {
-    return GetStream(FileMode.OpenOrCreate, FileAccess.ReadWrite);
-  }
-
-  internal MemoryStream GetStream(FileMode fileMode) {
-    return GetStream(FileMode.Create, FileAccess.ReadWrite);
-  }
-
-  internal MemoryStream GetStream(FileMode fileMode, FileAccess fileAccess) {
-    if (_stream == null || fileMode == FileMode.CreateNew || fileMode == FileMode.Create) {
-      _stream = new();
-    } else {
-      _stream.Seek(0, SeekOrigin.Begin);
-    }
-    return _stream;
+    return new(ImmutableCollectionsMarshal.AsArray(_data) ?? [], false);
   }
 
   private string _contentType = "";
@@ -110,22 +90,18 @@
     }
   }
 
-  public Uri Uri { get; private set; }
+  public Uri Uri { get; }
 
-  internal SaveHandlerDelegate SaveHandler { get; set; }
+  internal Action<StreamWriter> SaveHandler { get; set; }
 
   internal void WriteZip(ZipArchive zipArchive) {
-    byte[] b;
     if (SaveHandler == null) {
-      b = GetStream().ToArray();
-      if (b.Length
-          == 0) //Make sure the file isn't empty. DotNetZip streams does not seems to handle zero sized files.
-      {
+      if (_data.Length == 0) {
         return;
       }
       var zipEntry = zipArchive.CreateEntry(Uri.OriginalString);
       using var os = zipEntry.Open();
-      os.Write(b);
+      os.Write(_data.AsSpan());
     } else {
       var zipEntry = zipArchive.CreateEntry(Uri.OriginalString);
       using var streamWriter = new StreamWriter(zipEntry.Open());
@@ -137,11 +113,7 @@
       var name = Path.GetFileName(f);
       _rels.WriteZip(zipArchive, $"{f.Substring(0, f.Length - name.Length)}_rels/{name}.rels");
     }
-    b = null;
   }
 
-  public void Dispose() {
-    _stream.Close();
-    _stream.Dispose();
-  }
+  public void Dispose() {}
 }
diff --git a/EPPlus/Packaging/ZipPackageRelationship.cs b/EPPlus/Packaging/ZipPackageRelationship.cs
index 15c4539..1d76501 100644
--- a/EPPlus/Packaging/ZipPackageRelationship.cs
+++ b/EPPlus/Packaging/ZipPackageRelationship.cs
@@ -33,7 +33,7 @@
 
 namespace OfficeOpenXml.Packaging;
 
-public class ZipPackageRelationship {
+internal class ZipPackageRelationship {
   public Uri TargetUri { get; internal set; }
 
   public Uri SourceUri { get; internal set; }
diff --git a/EPPlus/Packaging/ZipPackageRelationshipBase.cs b/EPPlus/Packaging/ZipPackageRelationshipBase.cs
index 882358e..ff9bf8f 100644
--- a/EPPlus/Packaging/ZipPackageRelationshipBase.cs
+++ b/EPPlus/Packaging/ZipPackageRelationshipBase.cs
@@ -35,7 +35,7 @@
 
 namespace OfficeOpenXml.Packaging;
 
-public abstract class ZipPackageRelationshipBase {
+internal abstract class ZipPackageRelationshipBase {
   protected ZipPackageRelationshipCollection _rels = new();
   protected internal int maxRId = 1;
 
diff --git a/EPPlus/Packaging/ZipPackageRelationshipCollection.cs b/EPPlus/Packaging/ZipPackageRelationshipCollection.cs
index f828f61..7890ad6 100644
--- a/EPPlus/Packaging/ZipPackageRelationshipCollection.cs
+++ b/EPPlus/Packaging/ZipPackageRelationshipCollection.cs
@@ -38,7 +38,7 @@
 
 namespace OfficeOpenXml.Packaging;
 
-public class ZipPackageRelationshipCollection : IEnumerable<ZipPackageRelationship> {
+internal class ZipPackageRelationshipCollection : IEnumerable<ZipPackageRelationship> {
   protected internal Dictionary<string, ZipPackageRelationship> _rels = new(
       StringComparer.InvariantCultureIgnoreCase);
 
diff --git a/EPPlus/Style/Dxf/DxfStyleBase.cs b/EPPlus/Style/Dxf/DxfStyleBase.cs
index 0c73cb0..b81e07a 100644
--- a/EPPlus/Style/Dxf/DxfStyleBase.cs
+++ b/EPPlus/Style/Dxf/DxfStyleBase.cs
@@ -8,7 +8,6 @@
 
   internal DxfStyleBase(ExcelStyles styles) {
     _styles = styles;
-    AllowChange = false; //Don't touch this value in the styles.xml (by default). When Dxfs is fully implemented this can be removed.
   }
 
   protected internal abstract string Id { get; }
@@ -75,5 +74,5 @@
   /// <summary>
   /// Is this value allowed to be changed?
   /// </summary>
-  protected internal bool AllowChange { get; set; }
+  protected internal bool AllowChange { get; set; } = false;
 }
diff --git a/EPPlus/Style/Dxf/ExcelDxfStyle.cs b/EPPlus/Style/Dxf/ExcelDxfStyle.cs
index 325d5b1..4784a1e 100644
--- a/EPPlus/Style/Dxf/ExcelDxfStyle.cs
+++ b/EPPlus/Style/Dxf/ExcelDxfStyle.cs
@@ -1,11 +1,22 @@
 using System;
+using System.Collections.Immutable;
 using System.Globalization;
 using System.Xml;
 
 namespace OfficeOpenXml.Style.Dxf;
 
 public class ExcelDxfStyleConditionalFormatting : DxfStyleBase<ExcelDxfStyleConditionalFormatting> {
-  private readonly XmlHelperInstance _helper;
+  private readonly DxfStyleXmlHelper _helper;
+
+  private class DxfStyleXmlHelper(XmlNamespaceManager nameSpaceManager, XmlNode topNode)
+      : XmlHelper(nameSpaceManager, topNode) {
+    protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
+      "font",
+      "numFmt",
+      "fill",
+      "border",
+    ];
+  }
 
   internal ExcelDxfStyleConditionalFormatting(
       XmlNamespaceManager nameSpaceManager,
@@ -40,12 +51,11 @@
       Fill.BackgroundColor = GetColor(_helper, "d:fill/d:patternFill/d:bgColor/");
       Fill.PatternColor = GetColor(_helper, "d:fill/d:patternFill/d:fgColor/");
     } else {
-      _helper = new(nameSpaceManager);
+      _helper = new(nameSpaceManager, null);
     }
-    _helper.SchemaNodeOrder = new[] { "font", "numFmt", "fill", "border" };
   }
 
-  private ExcelDxfBorderItem GetBorderItem(XmlHelperInstance helper, string path) {
+  private ExcelDxfBorderItem GetBorderItem(XmlHelper helper, string path) {
     ExcelDxfBorderItem bi = new ExcelDxfBorderItem(_styles);
     bi.Style = GetBorderStyleEnum(helper.GetXmlNodeString(path + "/@style"));
     bi.Color = GetColor(helper, path + "/d:color");
@@ -58,7 +68,7 @@
   private ExcelFillStyle GetPatternTypeEnum(string patternType) =>
     Enum.TryParse<ExcelFillStyle>(patternType, true, out var result) ? result : ExcelFillStyle.None;
 
-  private ExcelDxfColor GetColor(XmlHelperInstance helper, string path) {
+  private ExcelDxfColor GetColor(XmlHelper helper, string path) {
     ExcelDxfColor ret = new ExcelDxfColor(_styles);
     ret.Theme = helper.GetXmlNodeIntNull(path + "/@theme");
     ret.Index = helper.GetXmlNodeIntNull(path + "/@indexed");
diff --git a/EPPlus/Style/ExcelBorderItem.cs b/EPPlus/Style/ExcelBorderItem.cs
index 6f8208b..89dc283 100644
--- a/EPPlus/Style/ExcelBorderItem.cs
+++ b/EPPlus/Style/ExcelBorderItem.cs
@@ -30,7 +30,6 @@
  * Jan Källman		License changed GPL-->LGPL 2011-12-16
  *******************************************************************************/
 
-using System;
 using OfficeOpenXml.Style.XmlAccess;
 
 namespace OfficeOpenXml.Style;
diff --git a/EPPlus/Style/ExcelColor.cs b/EPPlus/Style/ExcelColor.cs
index 8725905..e02de45 100644
--- a/EPPlus/Style/ExcelColor.cs
+++ b/EPPlus/Style/ExcelColor.cs
@@ -30,7 +30,6 @@
  * Jan Källman		License changed GPL-->LGPL 2011-12-16
  *******************************************************************************/
 
-using System;
 using OfficeOpenXml.Style.XmlAccess;
 
 namespace OfficeOpenXml.Style;
@@ -103,98 +102,4 @@
   internal override void SetIndex(int index) {
     _parent.Index = index;
   }
-
-  /// <summary>
-  /// Return the RGB value for the color object that uses the Indexed or Tint property
-  /// </summary>
-  /// <param name="theColor">The color object</param>
-  /// <returns>The RGB color starting with a #</returns>
-  public string LookupColor(ExcelColor theColor) {
-    //Thanks to neaves for contributing this method.
-    int iTint = 0;
-    string translatedRgb = "";
-
-    // reference extracted from ECMA-376, Part 4, Section 3.8.26
-    string[] rgbLookup = {
-      "#FF000000", // 0
-      "#FFFFFFFF",
-      "#FFFF0000",
-      "#FF00FF00",
-      "#FF0000FF",
-      "#FFFFFF00",
-      "#FFFF00FF",
-      "#FF00FFFF",
-      "#FF000000", // 8
-      "#FFFFFFFF",
-      "#FFFF0000",
-      "#FF00FF00",
-      "#FF0000FF",
-      "#FFFFFF00",
-      "#FFFF00FF",
-      "#FF00FFFF",
-      "#FF800000",
-      "#FF008000",
-      "#FF000080",
-      "#FF808000",
-      "#FF800080",
-      "#FF008080",
-      "#FFC0C0C0",
-      "#FF808080",
-      "#FF9999FF",
-      "#FF993366",
-      "#FFFFFFCC",
-      "#FFCCFFFF",
-      "#FF660066",
-      "#FFFF8080",
-      "#FF0066CC",
-      "#FFCCCCFF",
-      "#FF000080",
-      "#FFFF00FF",
-      "#FFFFFF00",
-      "#FF00FFFF",
-      "#FF800080",
-      "#FF800000",
-      "#FF008080",
-      "#FF0000FF",
-      "#FF00CCFF",
-      "#FFCCFFFF",
-      "#FFCCFFCC",
-      "#FFFFFF99",
-      "#FF99CCFF",
-      "#FFFF99CC",
-      "#FFCC99FF",
-      "#FFFFCC99",
-      "#FF3366FF",
-      "#FF33CCCC",
-      "#FF99CC00",
-      "#FFFFCC00",
-      "#FFFF9900",
-      "#FFFF6600",
-      "#FF666699",
-      "#FF969696",
-      "#FF003366",
-      "#FF339966",
-      "#FF003300",
-      "#FF333300",
-      "#FF993300",
-      "#FF993366",
-      "#FF333399",
-      "#FF333333", // 63
-    };
-
-    if ((0 <= theColor.Indexed) && (rgbLookup.Length > theColor.Indexed)) {
-      // coloring by pre-set color codes
-      translatedRgb = rgbLookup[theColor.Indexed];
-    } else if (null != theColor.Rgb && 0 < theColor.Rgb.Length) {
-      // coloring by RGB value ("FFRRGGBB")
-      translatedRgb = "#" + theColor.Rgb;
-    } else {
-      // coloring by shades of grey (-1 -> 0)
-      iTint = ((int)(theColor.Tint * 160) + 0x80);
-      translatedRgb = ((int)(decimal.Round(theColor.Tint * -512))).ToString("X");
-      translatedRgb = "#FF" + translatedRgb + translatedRgb + translatedRgb;
-    }
-
-    return translatedRgb;
-  }
 }
diff --git a/EPPlus/Style/ExcelGradientFill.cs b/EPPlus/Style/ExcelGradientFill.cs
index 7103b37..4ab355e 100644
--- a/EPPlus/Style/ExcelGradientFill.cs
+++ b/EPPlus/Style/ExcelGradientFill.cs
@@ -30,7 +30,6 @@
  * Jan Källman		License changed GPL-->LGPL 2011-12-16
  *******************************************************************************/
 
-using System;
 using OfficeOpenXml.Style.XmlAccess;
 
 namespace OfficeOpenXml.Style;
diff --git a/EPPlus/Style/ExcelParagraph.cs b/EPPlus/Style/ExcelParagraph.cs
deleted file mode 100644
index a758a83..0000000
--- a/EPPlus/Style/ExcelParagraph.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-/*******************************************************************************
- * You may amend and distribute as you like, but don't remove this header!
- *
- * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
- * See http://www.codeplex.com/EPPlus for details.
- *
- * Copyright (C) 2011  Jan Källman
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
-
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU Lesser General Public License for more details.
- *
- * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
- * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
- *
- * All code and executables are provided "as is" with no warranty either express or implied.
- * The author accepts no liability for any damage or loss of business that this product may cause.
- *
- * Code change notes:
- *
- * Author							Change						Date
- * ******************************************************************************
- * Jan Källman		                Initial Release		        2009-10-01
- * Jan Källman		License changed GPL-->LGPL 2011-12-16
- *******************************************************************************/
-
-using System.Xml;
-
-namespace OfficeOpenXml.Style;
-
-/// <summary>
-/// Handels paragraph text
-/// </summary>
-public sealed class ExcelParagraph : ExcelTextFont {
-  public ExcelParagraph(
-      XmlNamespaceManager ns,
-      XmlNode rootNode,
-      string path,
-      string[] schemaNodeOrder)
-      : base(ns, rootNode, path + "a:rPr", schemaNodeOrder) {}
-
-  private const string _textPath = "../a:t";
-
-  /// <summary>
-  /// Text
-  /// </summary>
-  public string Text {
-    get => GetXmlNodeString(_textPath);
-    set {
-      CreateTopNode();
-      SetXmlNodeString(_textPath, value);
-    }
-  }
-}
diff --git a/EPPlus/Style/ExcelParagraphCollection.cs b/EPPlus/Style/ExcelParagraphCollection.cs
deleted file mode 100644
index a74fe57..0000000
--- a/EPPlus/Style/ExcelParagraphCollection.cs
+++ /dev/null
@@ -1,145 +0,0 @@
-/*******************************************************************************
- * You may amend and distribute as you like, but don't remove this header!
- *
- * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
- * See http://www.codeplex.com/EPPlus for details.
- *
- * Copyright (C) 2011  Jan Källman
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
-
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU Lesser General Public License for more details.
- *
- * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
- * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
- *
- * All code and executables are provided "as is" with no warranty either express or implied.
- * The author accepts no liability for any damage or loss of business that this product may cause.
- *
- * Code change notes:
- *
- * Author							Change						Date
- * ******************************************************************************
- * Jan Källman		                Initial Release		        2009-10-01
- * Jan Källman		License changed GPL-->LGPL 2011-12-16
- *******************************************************************************/
-
-using System.Collections;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-
-namespace OfficeOpenXml.Style;
-
-/// <summary>
-/// A collection of Paragraph objects
-/// </summary>
-public class ExcelParagraphCollection : XmlHelper, IEnumerable<ExcelParagraph> {
-  private readonly List<ExcelParagraph> _list = new();
-  private readonly string _path;
-
-  internal ExcelParagraphCollection(
-      XmlNamespaceManager ns,
-      XmlNode topNode,
-      string path,
-      string[] schemaNodeOrder)
-      : base(ns, topNode) {
-    var nl = topNode.SelectNodes(path + "/a:r", NameSpaceManager);
-    SchemaNodeOrder = schemaNodeOrder;
-    if (nl != null) {
-      foreach (XmlNode n in nl) {
-        _list.Add(new(ns, n, "", schemaNodeOrder));
-      }
-    }
-    _path = path;
-  }
-
-  public ExcelParagraph this[int index] => _list[index];
-
-  public int Count => _list.Count;
-
-  /// <summary>
-  /// Add a rich text string
-  /// </summary>
-  /// <param name="text">The text to add</param>
-  /// <returns></returns>
-  public ExcelParagraph Add(string text) {
-    XmlDocument doc;
-    if (TopNode is XmlDocument document) {
-      doc = document;
-    } else {
-      doc = TopNode.OwnerDocument;
-    }
-    XmlNode parentNode = TopNode.SelectSingleNode(_path, NameSpaceManager);
-    if (parentNode == null) {
-      CreateNode(_path);
-      parentNode = TopNode.SelectSingleNode(_path, NameSpaceManager);
-    }
-
-    var node = doc.CreateElement("a", "r", ExcelPackage._schemaDrawings);
-    parentNode.AppendChild(node);
-    var childNode = doc.CreateElement("a", "rPr", ExcelPackage._schemaDrawings);
-    node.AppendChild(childNode);
-    var rt = new ExcelParagraph(NameSpaceManager, node, "", SchemaNodeOrder);
-    rt.ComplexFont = "Calibri";
-    rt.LatinFont = "Calibri";
-    rt.Size = 11;
-
-    rt.Text = text;
-    _list.Add(rt);
-    return rt;
-  }
-
-  public void Clear() {
-    _list.Clear();
-    TopNode.RemoveAll();
-  }
-
-  public void RemoveAt(int index) {
-    var node = _list[index].TopNode;
-    while (node != null && node.Name != "a:r") {
-      node = node.ParentNode;
-    }
-    node.ParentNode.RemoveChild(node);
-    _list.RemoveAt(index);
-  }
-
-  public void Remove(ExcelRichText item) {
-    TopNode.RemoveChild(item.TopNode);
-  }
-
-  public string Text {
-    get {
-      StringBuilder sb = new StringBuilder();
-      foreach (var item in _list) {
-        sb.Append(item.Text);
-      }
-      return sb.ToString();
-    }
-    set {
-      if (Count == 0) {
-        Add(value);
-      } else {
-        this[0].Text = value;
-        int count = Count;
-        for (int ix = Count - 1; ix > 0; ix--) {
-          RemoveAt(ix);
-        }
-      }
-    }
-  }
-
-  IEnumerator<ExcelParagraph> IEnumerable<ExcelParagraph>.GetEnumerator() {
-    return _list.GetEnumerator();
-  }
-
-  IEnumerator IEnumerable.GetEnumerator() {
-    return _list.GetEnumerator();
-  }
-}
diff --git a/EPPlus/Style/ExcelRichText.cs b/EPPlus/Style/ExcelRichText.cs
index 2458430..3e9994e 100644
--- a/EPPlus/Style/ExcelRichText.cs
+++ b/EPPlus/Style/ExcelRichText.cs
@@ -33,6 +33,7 @@
  *******************************************************************************/
 
 using System;
+using System.Collections.Immutable;
 using System.Globalization;
 using System.Xml;
 
@@ -42,26 +43,27 @@
 /// A richtext part
 /// </summary>
 public class ExcelRichText : XmlHelper {
+  protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
+    "rPr",
+    "t",
+    "b",
+    "i",
+    "strike",
+    "u",
+    "vertAlign",
+    "sz",
+    "color",
+    "rFont",
+    "family",
+    "scheme",
+    "charset",
+  ];
+
   internal ExcelRichText(
       XmlNamespaceManager ns,
       XmlNode topNode,
       ExcelRichTextCollection collection)
       : base(ns, topNode) {
-    SchemaNodeOrder = new[] {
-      "rPr",
-      "t",
-      "b",
-      "i",
-      "strike",
-      "u",
-      "vertAlign",
-      "sz",
-      "color",
-      "rFont",
-      "family",
-      "scheme",
-      "charset",
-    };
     _collection = collection;
   }
 
diff --git a/EPPlus/Style/ExcelRichTextCollection.cs b/EPPlus/Style/ExcelRichTextCollection.cs
index 62925f7..21f4803 100644
--- a/EPPlus/Style/ExcelRichTextCollection.cs
+++ b/EPPlus/Style/ExcelRichTextCollection.cs
@@ -32,7 +32,6 @@
 
 using System.Collections;
 using System.Collections.Generic;
-using System.Globalization;
 using System.Linq;
 using System.Text;
 using System.Xml;
diff --git a/EPPlus/Style/ExcelStyle.cs b/EPPlus/Style/ExcelStyle.cs
index 3292c7b..088b8c3 100644
--- a/EPPlus/Style/ExcelStyle.cs
+++ b/EPPlus/Style/ExcelStyle.cs
@@ -30,7 +30,6 @@
  * Jan Källman		License changed GPL-->LGPL 2011-12-16
  *******************************************************************************/
 
-using System;
 using OfficeOpenXml.Style.XmlAccess;
 
 namespace OfficeOpenXml.Style;
diff --git a/EPPlus/Style/ExcelTextFont.cs b/EPPlus/Style/ExcelTextFont.cs
deleted file mode 100644
index 9252413..0000000
--- a/EPPlus/Style/ExcelTextFont.cs
+++ /dev/null
@@ -1,222 +0,0 @@
-/*******************************************************************************
- * You may amend and distribute as you like, but don't remove this header!
- *
- * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
- * See http://www.codeplex.com/EPPlus for details.
- *
- * Copyright (C) 2011  Jan Källman
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
-
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU Lesser General Public License for more details.
- *
- * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
- * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
- *
- * All code and executables are provided "as is" with no warranty either express or implied.
- * The author accepts no liability for any damage or loss of business that this product may cause.
- *
- * Code change notes:
- *
- * Author							Change						Date
- * ******************************************************************************
- * Jan Källman		                Initial Release		        2009-10-01
- * Jan Källman		License changed GPL-->LGPL 2011-12-16
- *******************************************************************************/
-
-using System;
-using System.Globalization;
-using System.Xml;
-
-namespace OfficeOpenXml.Style;
-
-/// <summary>
-/// Linestyle
-/// </summary>
-public enum eUnderLineType {
-  Dash,
-  DashHeavy,
-  DashLong,
-  DashLongHeavy,
-  Double,
-  DotDash,
-  DotDashHeavy,
-  DotDotDash,
-  DotDotDashHeavy,
-  Dotted,
-  DottedHeavy,
-  Heavy,
-  None,
-  Single,
-  Wavy,
-  WavyDbl,
-  WavyHeavy,
-  Words,
-}
-
-/// <summary>
-/// Type of font strike
-/// </summary>
-public enum eStrikeType {
-  Double,
-  No,
-  Single,
-}
-
-/// <summary>
-/// Used by Rich-text and Paragraphs.
-/// </summary>
-public class ExcelTextFont : XmlHelper {
-  private readonly string _path;
-  private readonly XmlNode _rootNode;
-
-  internal ExcelTextFont(
-      XmlNamespaceManager namespaceManager,
-      XmlNode rootNode,
-      string path,
-      string[] schemaNodeOrder)
-      : base(namespaceManager, rootNode) {
-    SchemaNodeOrder = schemaNodeOrder;
-    _rootNode = rootNode;
-    if (path != "") {
-      XmlNode node = rootNode.SelectSingleNode(path, namespaceManager);
-      if (node != null) {
-        TopNode = node;
-      }
-    }
-    _path = path;
-  }
-
-  private readonly string _fontLatinPath = "a:latin/@typeface";
-
-  public string LatinFont {
-    get => GetXmlNodeString(_fontLatinPath);
-    set {
-      CreateTopNode();
-      SetXmlNodeString(_fontLatinPath, value);
-    }
-  }
-
-  protected internal void CreateTopNode() {
-    if (_path != "" && TopNode == _rootNode) {
-      CreateNode(_path);
-      TopNode = _rootNode.SelectSingleNode(_path, NameSpaceManager);
-    }
-  }
-
-  private readonly string _fontCsPath = "a:cs/@typeface";
-
-  public string ComplexFont {
-    get => GetXmlNodeString(_fontCsPath);
-    set {
-      CreateTopNode();
-      SetXmlNodeString(_fontCsPath, value);
-    }
-  }
-
-  private readonly string _boldPath = "@b";
-
-  public bool Bold {
-    get => GetXmlNodeBool(_boldPath);
-    set {
-      CreateTopNode();
-      SetXmlNodeString(_boldPath, value ? "1" : "0");
-    }
-  }
-
-  private readonly string _underLinePath = "@u";
-
-  public eUnderLineType UnderLine {
-    get => TranslateUnderline(GetXmlNodeString(_underLinePath));
-    set {
-      CreateTopNode();
-      SetXmlNodeString(_underLinePath, TranslateUnderlineText(value));
-    }
-  }
-
-  private readonly string _italicPath = "@i";
-
-  public bool Italic {
-    get => GetXmlNodeBool(_italicPath);
-    set {
-      CreateTopNode();
-      SetXmlNodeString(_italicPath, value ? "1" : "0");
-    }
-  }
-
-  private readonly string _strikePath = "@strike";
-
-  public eStrikeType Strike {
-    get => TranslateStrike(GetXmlNodeString(_strikePath));
-    set {
-      CreateTopNode();
-      SetXmlNodeString(_strikePath, TranslateStrikeText(value));
-    }
-  }
-
-  private readonly string _sizePath = "@sz";
-
-  public float Size {
-    get => GetXmlNodeInt(_sizePath) / 100;
-    set {
-      CreateTopNode();
-      SetXmlNodeString(_sizePath, ((int)(value * 100)).ToString());
-    }
-  }
-
-  private eUnderLineType TranslateUnderline(string text) {
-    switch (text) {
-      case "sng":
-        return eUnderLineType.Single;
-      case "dbl":
-        return eUnderLineType.Double;
-      case "":
-        return eUnderLineType.None;
-      default:
-        return Enum.TryParse<eUnderLineType>(text, true, out var result)
-            ? result
-            : eUnderLineType.None;
-    }
-  }
-
-  private string TranslateUnderlineText(eUnderLineType value) {
-    switch (value) {
-      case eUnderLineType.Single:
-        return "sng";
-      case eUnderLineType.Double:
-        return "dbl";
-      default:
-        string ret = value.ToString();
-        return ret.Substring(0, 1).ToLower(CultureInfo.InvariantCulture)
-            + ret.Substring(1, ret.Length - 1);
-    }
-  }
-
-  private eStrikeType TranslateStrike(string text) {
-    switch (text) {
-      case "dblStrike":
-        return eStrikeType.Double;
-      case "sngStrike":
-        return eStrikeType.Single;
-      default:
-        return eStrikeType.No;
-    }
-  }
-
-  private string TranslateStrikeText(eStrikeType value) {
-    switch (value) {
-      case eStrikeType.Single:
-        return "sngStrike";
-      case eStrikeType.Double:
-        return "dblStrike";
-      default:
-        return "noStrike";
-    }
-  }
-}
diff --git a/EPPlus/Style/XmlAccess/ExcelNumberFormatXml.cs b/EPPlus/Style/XmlAccess/ExcelNumberFormatXml.cs
index 6e5d10d..c67aed9 100644
--- a/EPPlus/Style/XmlAccess/ExcelNumberFormatXml.cs
+++ b/EPPlus/Style/XmlAccess/ExcelNumberFormatXml.cs
@@ -357,11 +357,8 @@
 
     internal string NetFormatForWidth { get; private set; }
 
-    //internal string FractionFormatInteger { get; private set; }
     internal string FractionFormat { get; private set; }
 
-    //internal string FractionFormat2 { get; private set; }
-
     private void ToNetFormat(string excelFormat, bool forColWidth) {
       DataType = eFormatType.Unknown;
       int secCount = 0;
@@ -372,7 +369,6 @@
       bool useMinute = false;
       bool prevUnderScore = false;
       bool ignoreNext = false;
-      int fractionPos = -1;
       string specialDateFormat = "";
       bool containsAmPm = excelFormat.Contains("AM/PM");
       List<int> lstDec = new List<int>();
@@ -511,7 +507,6 @@
                 sb.Append(' ');
               } else if (c == '/') {
                 if (DataType == eFormatType.Number) {
-                  fractionPos = sb.Length;
                   int startPos = pos - 1;
                   while (startPos >= 0
                           && (excelFormat[startPos] == '?'
diff --git a/EPPlus/Table/ExcelTable.cs b/EPPlus/Table/ExcelTable.cs
index 2438154..674bb73 100644
--- a/EPPlus/Table/ExcelTable.cs
+++ b/EPPlus/Table/ExcelTable.cs
@@ -32,9 +32,7 @@
 
 using System;
 using System.Collections.Generic;
-using System.Security;
-using System.Text;
-using System.Text.RegularExpressions;
+using System.Collections.Immutable;
 using System.Xml;
 using OfficeOpenXml.Packaging;
 using OfficeOpenXml.Utils;
@@ -113,101 +111,37 @@
 /// An Excel Table
 /// </summary>
 public class ExcelTable : XmlHelper, IEqualityComparer<ExcelTable> {
+  protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
+    "autoFilter",
+    "tableColumns",
+    "tableStyleInfo",
+  ];
+
   internal ExcelTable(ZipPackageRelationship rel, ExcelWorksheet sheet)
       : base(sheet.NameSpaceManager) {
     WorkSheet = sheet;
     TableUri = UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri);
     RelationshipID = rel.Id;
-    var pck = sheet._package.Package;
-    Part = pck.GetPart(TableUri);
-
-    TableXml = new();
-    LoadXmlSafe(TableXml, Part.GetStream());
-    Init();
+    TableXml = sheet._package.GetXmlDocument(TableUri);
+    TopNode = TableXml.DocumentElement;
     Address = new(GetXmlNodeString("@ref"));
   }
 
-  internal ExcelTable(ExcelWorksheet sheet, ExcelAddressBase address, string name, int tblId)
-      : base(sheet.NameSpaceManager) {
-    WorkSheet = sheet;
-    Address = address;
-    TableXml = new();
-    LoadXmlSafe(TableXml, GetStartXml(name, tblId), Encoding.UTF8);
-    TopNode = TableXml.DocumentElement;
-
-    Init();
-
-    //If the table is just one row we can not have a header.
-    if (address._fromRow == address._toRow) {
-      ShowHeader = false;
-    }
-  }
-
-  private void Init() {
-    TopNode = TableXml.DocumentElement;
-    SchemaNodeOrder = new[] { "autoFilter", "tableColumns", "tableStyleInfo" };
-  }
-
-  private string GetStartXml(string name, int tblId) {
-    string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>";
-    xml += string.Format(
-        "<table xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" id=\"{0}\" name=\"{1}\" displayName=\"{2}\" ref=\"{3}\" headerRowCount=\"1\">",
-        tblId,
-        name,
-        CleanDisplayName(name),
-        Address.Address);
-    xml += string.Format("<autoFilter ref=\"{0}\" />", Address.Address);
-
-    int cols = Address._toCol - Address._fromCol + 1;
-    xml += string.Format("<tableColumns count=\"{0}\">", cols);
-    var names = new Dictionary<string, string>();
-    for (int i = 1; i <= cols; i++) {
-      var cell = WorkSheet.Cells[Address._fromRow, Address._fromCol + i - 1];
-      string colName;
-      if (cell.Value == null || names.ContainsKey(cell.Value.ToString())) {
-        //Get an unique name
-        int a = i;
-        do {
-          colName = string.Format("Column{0}", a++);
-        } while (names.ContainsKey(colName));
-      } else {
-        colName = SecurityElement.Escape(cell.Value.ToString());
-      }
-      names.Add(colName, colName);
-      xml += string.Format("<tableColumn id=\"{0}\" name=\"{1}\" />", i, colName);
-    }
-    xml += "</tableColumns>";
-    xml +=
-        "<tableStyleInfo name=\"TableStyleMedium9\" showFirstColumn=\"0\" showLastColumn=\"0\" showRowStripes=\"1\" showColumnStripes=\"0\" /> ";
-    xml += "</table>";
-
-    return xml;
-  }
-
-  private string CleanDisplayName(string name) {
-    return Regex.Replace(name, @"[^\w\.-_]", "_");
-  }
-
-  internal ZipPackagePart Part { get; set; }
-
   /// <summary>
   /// Provides access to the XML data representing the table in the package.
   /// </summary>
-  public XmlDocument TableXml { get; set; }
+  internal XmlDocument TableXml { get; }
 
   /// <summary>
   /// The package internal URI to the Table Xml Document.
   /// </summary>
-  public Uri TableUri { get; internal set; }
+  private Uri TableUri { get; }
 
   internal string RelationshipID { get; set; }
 
   private const string _idPath = "@id";
 
-  internal int Id {
-    get => GetXmlNodeInt(_idPath);
-    set => SetXmlNodeString(_idPath, value.ToString());
-  }
+  internal int Id => GetXmlNodeInt(_idPath);
 
   private const string _namePath = "@name";
   private const string _displayNamePath = "@displayName";
@@ -215,27 +149,12 @@
   /// <summary>
   /// The name of the table object in Excel
   /// </summary>
-  public string Name {
-    get => GetXmlNodeString(_namePath);
-    set {
-      if (WorkSheet.Workbook.ExistsTableName(value)) {
-        throw (new ArgumentException("Tablename is not unique"));
-      }
-      string prevName = Name;
-      if (WorkSheet.Tables._tableNames.ContainsKey(prevName)) {
-        int ix = WorkSheet.Tables._tableNames[prevName];
-        WorkSheet.Tables._tableNames.Remove(prevName);
-        WorkSheet.Tables._tableNames.Add(value, ix);
-      }
-      SetXmlNodeString(_namePath, value);
-      SetXmlNodeString(_displayNamePath, CleanDisplayName(value));
-    }
-  }
+  public string Name => GetXmlNodeString(_namePath);
 
   /// <summary>
   /// The worksheet of the table
   /// </summary>
-  public ExcelWorksheet WorkSheet { get; set; }
+  public ExcelWorksheet WorkSheet { get; }
 
   private ExcelAddressBase _address;
 
@@ -265,21 +184,6 @@
     }
   }
 
-  private TableStyles _tableStyle = TableStyles.Medium6;
-
-  /// <summary>
-  /// The table style. If this property is cusom, the style from the StyleName propery is used.
-  /// </summary>
-  public TableStyles TableStyle {
-    get => _tableStyle;
-    set {
-      _tableStyle = value;
-      if (value != TableStyles.Custom) {
-        SetXmlNodeString(_stylenamePath, "TableStyle" + value);
-      }
-    }
-  }
-
   private const string _headerrowcountPath = "@headerRowCount";
   private const string _autofilterPath = "d:autoFilter/@ref";
 
@@ -341,21 +245,7 @@
   /// <summary>
   /// If the header row has an autofilter
   /// </summary>
-  public bool ShowFilter {
-    get => ShowHeader && AutoFilterAddress != null;
-    set {
-      if (ShowHeader) {
-        if (value) {
-          WriteAutoFilter(ShowTotal);
-        } else {
-          DeleteAllNode(_autofilterPath);
-        }
-      } else if (value) {
-        throw (new InvalidOperationException(
-                "Filter can only be applied when ShowHeader is set to true"));
-      }
-    }
-  }
+  public bool ShowFilter => ShowHeader && AutoFilterAddress != null;
 
   private const string _totalsrowcountPath = "@totalsRowCount";
 
@@ -399,131 +289,56 @@
   /// <summary>
   /// The style name for custum styles
   /// </summary>
-  public string StyleName {
-    get => GetXmlNodeString(_stylenamePath);
-    set {
-      if (value.StartsWith("TableStyle")) {
-        try {
-          _tableStyle = (TableStyles)
-            Enum.Parse(typeof(TableStyles), value.Substring(10, value.Length - 10), true);
-        } catch {
-          _tableStyle = TableStyles.Custom;
-        }
-      } else if (value == "None") {
-        _tableStyle = TableStyles.None;
-        value = "";
-      } else {
-        _tableStyle = TableStyles.Custom;
-      }
-      SetXmlNodeString(_stylenamePath, value, true);
-    }
-  }
+  public string StyleName => GetXmlNodeString(_stylenamePath);
 
   private const string _showfirstcolumnPath = "d:tableStyleInfo/@showFirstColumn";
 
   /// <summary>
   /// Display special formatting for the first row
   /// </summary>
-  public bool ShowFirstColumn {
-    get => GetXmlNodeBool(_showfirstcolumnPath);
-    set => SetXmlNodeBool(_showfirstcolumnPath, value, false);
-  }
+  public bool ShowFirstColumn => GetXmlNodeBool(_showfirstcolumnPath);
 
   private const string _showlastcolumnPath = "d:tableStyleInfo/@showLastColumn";
 
   /// <summary>
   /// Display special formatting for the last row
   /// </summary>
-  public bool ShowLastColumn {
-    get => GetXmlNodeBool(_showlastcolumnPath);
-    set => SetXmlNodeBool(_showlastcolumnPath, value, false);
-  }
+  public bool ShowLastColumn => GetXmlNodeBool(_showlastcolumnPath);
 
   private const string _showrowstripesPath = "d:tableStyleInfo/@showRowStripes";
 
   /// <summary>
   /// Display banded rows
   /// </summary>
-  public bool ShowRowStripes {
-    get => GetXmlNodeBool(_showrowstripesPath);
-    set => SetXmlNodeBool(_showrowstripesPath, value, false);
-  }
+  public bool ShowRowStripes => GetXmlNodeBool(_showrowstripesPath);
 
   private const string _showcolumnstripesPath = "d:tableStyleInfo/@showColumnStripes";
 
   /// <summary>
   /// Display banded columns
   /// </summary>
-  public bool ShowColumnStripes {
-    get => GetXmlNodeBool(_showcolumnstripesPath);
-    set => SetXmlNodeBool(_showcolumnstripesPath, value, false);
-  }
+  public bool ShowColumnStripes => GetXmlNodeBool(_showcolumnstripesPath);
 
   private const string _totalsrowcellstylePath = "@totalsRowCellStyle";
 
   /// <summary>
   /// Named style used for the total row
   /// </summary>
-  public string TotalsRowCellStyle {
-    get => GetXmlNodeString(_totalsrowcellstylePath);
-    set {
-      if (WorkSheet.Workbook.Styles.NamedStyles.FindIndexById(value) < 0) {
-        throw (new(string.Format("Named style {0} does not exist.", value)));
-      }
-      SetXmlNodeString(TopNode, _totalsrowcellstylePath, value, true);
-
-      if (ShowTotal) {
-        WorkSheet.Cells[Address._toRow,
-          Address._fromCol,
-          Address._toRow,
-          Address._toCol].StyleName = value;
-      }
-    }
-  }
+  public string TotalsRowCellStyle => GetXmlNodeString(_totalsrowcellstylePath);
 
   private const string _datacellstylePath = "@dataCellStyle";
 
   /// <summary>
   /// Named style used for the data cells
   /// </summary>
-  public string DataCellStyleName {
-    get => GetXmlNodeString(_datacellstylePath);
-    set {
-      if (WorkSheet.Workbook.Styles.NamedStyles.FindIndexById(value) < 0) {
-        throw (new(string.Format("Named style {0} does not exist.", value)));
-      }
-      SetXmlNodeString(TopNode, _datacellstylePath, value, true);
-
-      int fromRow = Address._fromRow + (ShowHeader ? 1 : 0),
-          toRow = Address._toRow - (ShowTotal ? 1 : 0);
-
-      if (fromRow < toRow) {
-        WorkSheet.Cells[fromRow, Address._fromCol, toRow, Address._toCol].StyleName = value;
-      }
-    }
-  }
+  public string DataCellStyleName => GetXmlNodeString(_datacellstylePath);
 
   private const string _headerrowcellstylePath = "@headerRowCellStyle";
 
   /// <summary>
   /// Named style used for the header row
   /// </summary>
-  public string HeaderRowCellStyle {
-    get => GetXmlNodeString(_headerrowcellstylePath);
-    set {
-      if (WorkSheet.Workbook.Styles.NamedStyles.FindIndexById(value) < 0) {
-        throw (new(string.Format("Named style {0} does not exist.", value)));
-      }
-      SetXmlNodeString(TopNode, _headerrowcellstylePath, value, true);
-
-      if (ShowHeader) {
-        WorkSheet.Cells[Address._fromRow,
-          Address._fromCol,
-          Address._fromRow,
-          Address._toCol].StyleName = value;
-      }
-    }
-  }
+  public string HeaderRowCellStyle => GetXmlNodeString(_headerrowcellstylePath);
 
   public bool Equals(ExcelTable x, ExcelTable y) {
     return x.WorkSheet == y.WorkSheet && x.Id == y.Id && x.TableXml.OuterXml == y.TableXml.OuterXml;
diff --git a/EPPlus/Table/ExcelTableCollection.cs b/EPPlus/Table/ExcelTableCollection.cs
index 75c255d..5a7941c 100644
--- a/EPPlus/Table/ExcelTableCollection.cs
+++ b/EPPlus/Table/ExcelTableCollection.cs
@@ -58,95 +58,6 @@
     }
   }
 
-  private ExcelTable Add(ExcelTable tbl) {
-    _tables.Add(tbl);
-    _tableNames.Add(tbl.Name, _tables.Count - 1);
-    if (tbl.Id >= _ws.Workbook._nextTableID) {
-      _ws.Workbook._nextTableID = tbl.Id + 1;
-    }
-    return tbl;
-  }
-
-  /// <summary>
-  /// Create a table on the supplied range
-  /// </summary>
-  /// <param name="range">The range address including header and total row</param>
-  /// <param name="name">The name of the table. Must be unique </param>
-  /// <returns>The table object</returns>
-  public ExcelTable Add(ExcelAddressBase range, string name) {
-    if (range.WorkSheet != null && range.WorkSheet != _ws.Name) {
-      throw new ArgumentException("Range does not belong to worksheet", "range");
-    }
-
-    if (string.IsNullOrEmpty(name)) {
-      name = GetNewTableName();
-    } else if (_ws.Workbook.ExistsTableName(name)) {
-      throw (new ArgumentException("Tablename is not unique"));
-    }
-
-    ValidateTableName(name);
-
-    foreach (var t in _tables) {
-      if (t.Address.Collide(range) != ExcelAddressBase.eAddressCollition.No) {
-        throw (new ArgumentException(string.Format("Table range collides with table {0}", t.Name)));
-      }
-    }
-    return Add(new(_ws, range, name, _ws.Workbook._nextTableID));
-  }
-
-  private void ValidateTableName(string name) {
-    if (string.IsNullOrEmpty(name)) {
-      throw new ArgumentException("Tablename is null or empty");
-    }
-
-    char firstLetterOfName = name[0];
-    if (Char.IsLetter(firstLetterOfName) == false
-        && firstLetterOfName != '_'
-        && firstLetterOfName != '\\') {
-      throw new ArgumentException("Tablename start with invalid character");
-    }
-
-    if (name.Contains(" ")) {
-      throw new ArgumentException("Tablename has spaces");
-    }
-  }
-
-  public void Delete(int index, bool clearRange = false) {
-    Delete(this[index], clearRange);
-  }
-
-  public void Delete(string name, bool clearRange = false) {
-    if (this[name] == null) {
-      throw new ArgumentOutOfRangeException(
-          string.Format("Cannot delete non-existant table {0} in sheet {1}.", name, _ws.Name));
-    }
-    Delete(this[name], clearRange);
-  }
-
-  public void Delete(ExcelTable excelTable, bool clearRange = false) {
-    if (!_tables.Contains(excelTable)) {
-      throw new ArgumentOutOfRangeException(
-          "excelTable",
-          String.Format("Table {0} does not exist in this collection", excelTable.Name));
-    }
-    lock (this) {
-      var range = _ws.Cells[excelTable.Address.Address];
-      _tableNames.Remove(excelTable.Name);
-      _tables.Remove(excelTable);
-      foreach (var sheet in excelTable.WorkSheet.Workbook.Worksheets) {
-        foreach (var table in sheet.Tables) {
-          if (table.Id > excelTable.Id) {
-            table.Id--;
-          }
-        }
-        excelTable.WorkSheet.Workbook._nextTableID--;
-      }
-      if (clearRange) {
-        range.Clear();
-      }
-    }
-  }
-
   internal string GetNewTableName() {
     string name = "Table1";
     int i = 2;
diff --git a/EPPlus/Table/ExcelTableColumnCollection.cs b/EPPlus/Table/ExcelTableColumnCollection.cs
index 57d59d8..e74adbe 100644
--- a/EPPlus/Table/ExcelTableColumnCollection.cs
+++ b/EPPlus/Table/ExcelTableColumnCollection.cs
@@ -104,7 +104,7 @@
 
   internal string GetUniqueName(string name) {
     if (_colNames.ContainsKey(name)) {
-      var newName = name;
+      string newName;
       var i = 2;
       do {
         newName = name + (i++).ToString(CultureInfo.InvariantCulture);
diff --git a/EPPlus/Table/PivotTable/ExcelPivotCacheDefinition.cs b/EPPlus/Table/PivotTable/ExcelPivotCacheDefinition.cs
index d25001f..44543ee 100644
--- a/EPPlus/Table/PivotTable/ExcelPivotCacheDefinition.cs
+++ b/EPPlus/Table/PivotTable/ExcelPivotCacheDefinition.cs
@@ -32,7 +32,6 @@
 
 using System;
 using System.Linq;
-using System.Text;
 using System.Xml;
 using OfficeOpenXml.Packaging;
 using OfficeOpenXml.Utils;
@@ -75,7 +74,6 @@
 
     var pck = pivotTable.WorkSheet._package.Package;
     Part = pck.GetPart(CacheDefinitionUri);
-    CacheDefinitionXml = new();
     LoadXmlSafe(CacheDefinitionXml, Part.GetStream());
 
     TopNode = CacheDefinitionXml.DocumentElement;
@@ -91,40 +89,6 @@
     }
   }
 
-  internal ExcelPivotCacheDefinition(
-      XmlNamespaceManager ns,
-      ExcelPivotTable pivotTable,
-      ExcelRangeBase sourceAddress,
-      int tblId)
-      : base(ns, null) {
-    PivotTable = pivotTable;
-
-    var pck = pivotTable.WorkSheet._package.Package;
-
-    //CacheDefinition
-    CacheDefinitionXml = new();
-    LoadXmlSafe(CacheDefinitionXml, GetStartXml(sourceAddress), Encoding.UTF8);
-    CacheDefinitionUri = GetNewUri(pck, "/xl/pivotCache/pivotCacheDefinition{0}.xml", tblId);
-    Part = pck.CreatePart(CacheDefinitionUri, ExcelPackage._schemaPivotCacheDefinition);
-    TopNode = CacheDefinitionXml.DocumentElement;
-
-    //CacheRecord. Create an empty one.
-    CacheRecordUri = GetNewUri(pck, "/xl/pivotCache/pivotCacheRecords{0}.xml", tblId);
-    var cacheRecord = new XmlDocument();
-    cacheRecord.LoadXml(
-        "<pivotCacheRecords xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" count=\"0\" />");
-    var recPart = pck.CreatePart(CacheRecordUri, ExcelPackage._schemaPivotCacheRecords);
-    cacheRecord.Save(recPart.GetStream());
-
-    RecordRelationship = Part.CreateRelationship(
-        UriHelper.ResolvePartUri(CacheDefinitionUri, CacheRecordUri),
-        TargetMode.Internal,
-        ExcelPackage._schemaRelationships + "/pivotCacheRecords");
-    RecordRelationshipID = RecordRelationship.Id;
-
-    CacheDefinitionXml.Save(Part.GetStream());
-  }
-
   /// <summary>
   /// Reference to the internal package part
   /// </summary>
@@ -133,7 +97,7 @@
   /// <summary>
   /// Provides access to the XML data representing the cache definition in the package.
   /// </summary>
-  public XmlDocument CacheDefinitionXml { get; private set; }
+  public XmlDocument CacheDefinitionXml { get; private set; } = new();
 
   /// <summary>
   /// The package internal URI to the pivottable cache definition Xml Document.
diff --git a/EPPlus/Table/PivotTable/ExcelPivotTable.cs b/EPPlus/Table/PivotTable/ExcelPivotTable.cs
index 6ea4244..47031ca 100644
--- a/EPPlus/Table/PivotTable/ExcelPivotTable.cs
+++ b/EPPlus/Table/PivotTable/ExcelPivotTable.cs
@@ -31,7 +31,7 @@
  *******************************************************************************/
 
 using System;
-using System.Text;
+using System.Collections.Immutable;
 using System.Text.RegularExpressions;
 using System.Xml;
 using OfficeOpenXml.Packaging;
@@ -43,21 +43,32 @@
 /// An Excel Pivottable
 /// </summary>
 public class ExcelPivotTable : XmlHelper {
+  protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
+    "location",
+    "pivotFields",
+    "rowFields",
+    "rowItems",
+    "colFields",
+    "colItems",
+    "pageFields",
+    "pageItems",
+    "dataFields",
+    "dataItems",
+    "formats",
+    "pivotTableStyleInfo",
+  ];
+
   internal ExcelPivotTable(ZipPackageRelationship rel, ExcelWorksheet sheet)
       : base(sheet.NameSpaceManager) {
     WorkSheet = sheet;
     PivotTableUri = UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri);
     Relationship = rel;
-    var pck = sheet._package.Package;
-    Part = pck.GetPart(PivotTableUri);
-
-    PivotTableXml = new();
-    LoadXmlSafe(PivotTableXml, Part.GetStream());
-    Init();
+    var package = sheet._package;
+    PivotTableXml = package.GetXmlDocument(PivotTableUri);
     TopNode = PivotTableXml.DocumentElement;
     Address = new(GetXmlNodeString("d:location/@ref"));
 
-    _cacheDefinition = new(sheet.NameSpaceManager, this);
+    CacheDefinition = new(sheet.NameSpaceManager, this);
     LoadFields();
 
     //Add row fields.
@@ -103,72 +114,6 @@
     }
   }
 
-  /// <summary>
-  /// Add a new pivottable
-  /// </summary>
-  /// <param name="sheet">The worksheet</param>
-  /// <param name="address">the address of the pivottable</param>
-  /// <param name="sourceAddress">The address of the Source data</param>
-  /// <param name="name"></param>
-  /// <param name="tblId"></param>
-  internal ExcelPivotTable(
-      ExcelWorksheet sheet,
-      ExcelAddressBase address,
-      ExcelRangeBase sourceAddress,
-      string name,
-      int tblId)
-      : base(sheet.NameSpaceManager) {
-    WorkSheet = sheet;
-    Address = address;
-    var pck = sheet._package.Package;
-
-    PivotTableXml = new();
-    LoadXmlSafe(PivotTableXml, GetStartXml(name, tblId, address, sourceAddress), Encoding.UTF8);
-    TopNode = PivotTableXml.DocumentElement;
-    PivotTableUri = GetNewUri(pck, "/xl/pivotTables/pivotTable{0}.xml", tblId);
-    Init();
-
-    Part = pck.CreatePart(PivotTableUri, ExcelPackage._schemaPivotTable);
-    PivotTableXml.Save(Part.GetStream());
-
-    //Worksheet-Pivottable relationship
-    Relationship = sheet.Part.CreateRelationship(
-        UriHelper.ResolvePartUri(sheet.WorksheetUri, PivotTableUri),
-        TargetMode.Internal,
-        ExcelPackage._schemaRelationships + "/pivotTable");
-
-    _cacheDefinition = new(sheet.NameSpaceManager, this, sourceAddress, tblId);
-    _cacheDefinition.Relationship = Part.CreateRelationship(
-        UriHelper.ResolvePartUri(PivotTableUri, _cacheDefinition.CacheDefinitionUri),
-        TargetMode.Internal,
-        ExcelPackage._schemaRelationships + "/pivotCacheDefinition");
-
-    sheet.Workbook.AddPivotTable(CacheID.ToString(), _cacheDefinition.CacheDefinitionUri);
-
-    LoadFields();
-
-    using (var r = sheet.Cells[address.Address]) {
-      r.Clear();
-    }
-  }
-
-  private void Init() {
-    SchemaNodeOrder = new[] {
-      "location",
-      "pivotFields",
-      "rowFields",
-      "rowItems",
-      "colFields",
-      "colItems",
-      "pageFields",
-      "pageItems",
-      "dataFields",
-      "dataItems",
-      "formats",
-      "pivotTableStyleInfo",
-    };
-  }
-
   private void LoadFields() {
     //Fields.Clear();
     //int ix=0;
@@ -188,7 +133,7 @@
 
     //Add fields.
     index = 0;
-    foreach (XmlElement fieldElem in _cacheDefinition.TopNode.SelectNodes(
+    foreach (XmlElement fieldElem in CacheDefinition.TopNode.SelectNodes(
         "d:cacheFields/d:cacheField",
         NameSpaceManager)) {
       var fld = Fields[index++];
@@ -196,34 +141,34 @@
     }
   }
 
-  private string GetStartXml(
+  private XmlDocument GetStartXml(
       string name,
       int id,
       ExcelAddressBase address,
       ExcelAddressBase sourceAddress) {
-    string xml = string.Format(
-        "<pivotTableDefinition xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" name=\"{0}\" cacheId=\"{1}\" dataOnRows=\"1\" applyNumberFormats=\"0\" applyBorderFormats=\"0\" applyFontFormats=\"0\" applyPatternFormats=\"0\" applyAlignmentFormats=\"0\" applyWidthHeightFormats=\"1\" dataCaption=\"Data\"  createdVersion=\"4\" showMemberPropertyTips=\"0\" useAutoFormatting=\"1\" itemPrintTitles=\"1\" indent=\"0\" compact=\"0\" compactData=\"0\" gridDropZones=\"1\">",
-        name,
-        id);
+    string xml = $"""
+         <pivotTableDefinition xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" name="
+         {name}" cacheId="{id
+         }" dataOnRows="1" applyNumberFormats="0" applyBorderFormats="0" applyFontFormats="0" applyPatternFormats="0" applyAlignmentFormats="0" applyWidthHeightFormats="1" dataCaption="Data"  createdVersion="4" showMemberPropertyTips="0" useAutoFormatting="1" itemPrintTitles="1" indent="0" compact="0" compactData="0" gridDropZones="1">
 
-    xml += string.Format(
-        "<location ref=\"{0}\" firstHeaderRow=\"1\" firstDataRow=\"1\" firstDataCol=\"1\" /> ",
-        address.FirstAddress);
-    xml += string.Format(
-        "<pivotFields count=\"{0}\">",
-        sourceAddress._toCol - sourceAddress._fromCol + 1);
+         """;
+    xml +=
+        $"""<location ref="{address.FirstAddress
+    }" firstHeaderRow="1" firstDataRow="1" firstDataCol="1" />""";
+    xml += $"""<pivotFields count="{sourceAddress._toCol - sourceAddress._fromCol + 1}">""";
     for (int col = sourceAddress._fromCol; col <= sourceAddress._toCol; col++) {
-      xml += "<pivotField showAll=\"0\" />"; //compact=\"0\" outline=\"0\" subtotalTop=\"0\" includeNewItemsInFilter=\"1\"
+      xml += """<pivotField showAll="0" />""";
     }
-
     xml += "</pivotFields>";
     xml +=
-        "<pivotTableStyleInfo name=\"PivotStyleMedium9\" showRowHeaders=\"1\" showColHeaders=\"1\" showRowStripes=\"0\" showColStripes=\"0\" showLastColumn=\"1\" />";
+        """<pivotTableStyleInfo name="PivotStyleMedium9" showRowHeaders="1" showColHeaders="1" showRowStripes="0" showColStripes="0" showLastColumn="1" />""";
     xml += "</pivotTableDefinition>";
-    return xml;
+    var result = new XmlDocument();
+    result.LoadXml(xml);
+    return result;
   }
 
-  internal ZipPackagePart Part { get; set; }
+  internal ZipPackagePart Part { get; }
 
   /// <summary>
   /// Provides access to the XML data representing the pivottable in the package.
@@ -272,19 +217,10 @@
     }
   }
 
-  private ExcelPivotCacheDefinition _cacheDefinition;
-
   /// <summary>
   /// Reference to the pivot table cache definition object
   /// </summary>
-  public ExcelPivotCacheDefinition CacheDefinition {
-    get {
-      if (_cacheDefinition == null) {
-        _cacheDefinition = new(NameSpaceManager, this, null, 1);
-      }
-      return _cacheDefinition;
-    }
-  }
+  public ExcelPivotCacheDefinition CacheDefinition { get; }
 
   private string CleanDisplayName(string name) {
     return Regex.Replace(name, @"[^\w\.-_]", "_");
diff --git a/EPPlus/Table/PivotTable/ExcelPivotTableCollection.cs b/EPPlus/Table/PivotTable/ExcelPivotTableCollection.cs
index c6c918f..fe9cc63 100644
--- a/EPPlus/Table/PivotTable/ExcelPivotTableCollection.cs
+++ b/EPPlus/Table/PivotTable/ExcelPivotTableCollection.cs
@@ -41,12 +41,9 @@
 /// </summary>
 public class ExcelPivotTableCollection : IEnumerable<ExcelPivotTable> {
   private readonly List<ExcelPivotTable> _pivotTables = new();
-  internal Dictionary<string, int> _pivotTableNames = new();
-  private readonly ExcelWorksheet _ws;
+  internal readonly Dictionary<string, int> _pivotTableNames = new();
 
   internal ExcelPivotTableCollection(ExcelWorksheet ws) {
-    var pck = ws._package.Package;
-    _ws = ws;
     foreach (var rel in ws.Part.GetRelationships()) {
       if (rel.RelationshipType == ExcelPackage._schemaRelationships + "/pivotTable") {
         var tbl = new ExcelPivotTable(rel, ws);
@@ -56,49 +53,6 @@
     }
   }
 
-  private ExcelPivotTable Add(ExcelPivotTable tbl) {
-    _pivotTables.Add(tbl);
-    _pivotTableNames.Add(tbl.Name, _pivotTables.Count - 1);
-    if (tbl.CacheID >= _ws.Workbook._nextPivotTableID) {
-      _ws.Workbook._nextPivotTableID = tbl.CacheID + 1;
-    }
-    return tbl;
-  }
-
-  /// <summary>
-  /// Create a pivottable on the supplied range
-  /// </summary>
-  /// <param name="range">The range address including header and total row</param>
-  /// <param name="source">The Source data range address</param>
-  /// <param name="name">The name of the table. Must be unique </param>
-  /// <returns>The pivottable object</returns>
-  public ExcelPivotTable Add(ExcelAddressBase range, ExcelRangeBase source, string name) {
-    if (string.IsNullOrEmpty(name)) {
-      name = GetNewTableName();
-    }
-    if (range.WorkSheet != _ws.Name) {
-      throw (new("The Range must be in the current worksheet"));
-    }
-    if (_ws.Workbook.ExistsTableName(name)) {
-      throw (new ArgumentException("Tablename is not unique"));
-    }
-    foreach (var t in _pivotTables) {
-      if (t.Address.Collide(range) != ExcelAddressBase.eAddressCollition.No) {
-        throw (new ArgumentException(string.Format("Table range collides with table {0}", t.Name)));
-      }
-    }
-    return Add(new(_ws, range, source, name, _ws.Workbook._nextPivotTableID++));
-  }
-
-  internal string GetNewTableName() {
-    string name = "Pivottable1";
-    int i = 2;
-    while (_ws.Workbook.ExistsPivotTableName(name)) {
-      name = string.Format("Pivottable{0}", i++);
-    }
-    return name;
-  }
-
   public int Count => _pivotTables.Count;
 
   /// <summary>
diff --git a/EPPlus/Table/PivotTable/ExcelPivotTableField.cs b/EPPlus/Table/PivotTable/ExcelPivotTableField.cs
index 9869a11..05570f2 100644
--- a/EPPlus/Table/PivotTable/ExcelPivotTableField.cs
+++ b/EPPlus/Table/PivotTable/ExcelPivotTableField.cs
@@ -31,7 +31,6 @@
  *******************************************************************************/
 
 using System;
-using System.Collections.Generic;
 using System.Globalization;
 using System.Xml;
 
@@ -545,221 +544,6 @@
     }
   }
 
-  internal ExcelPivotTableFieldDateGroup SetDateGroup(
-      eDateGroupBy groupBy,
-      DateTime startDate,
-      DateTime endDate,
-      int interval) {
-    ExcelPivotTableFieldDateGroup group;
-    group = new(NameSpaceManager, _cacheFieldHelper.TopNode);
-    _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsDate", true);
-    _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsNonDate", false);
-    _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsSemiMixedTypes", false);
-
-    group.TopNode.InnerXml += string.Format(
-        "<fieldGroup base=\"{0}\"><rangePr groupBy=\"{1}\" /><groupItems /></fieldGroup>",
-        BaseIndex,
-        groupBy.ToString().ToLower(CultureInfo.InvariantCulture));
-
-    if (startDate.Year < 1900) {
-      _cacheFieldHelper.SetXmlNodeString(
-          "d:fieldGroup/d:rangePr/@startDate",
-          "1900-01-01T00:00:00");
-    } else {
-      _cacheFieldHelper.SetXmlNodeString(
-          "d:fieldGroup/d:rangePr/@startDate",
-          startDate.ToString("s", CultureInfo.InvariantCulture));
-      _cacheFieldHelper.SetXmlNodeString("d:fieldGroup/d:rangePr/@autoStart", "0");
-    }
-
-    if (endDate == DateTime.MaxValue) {
-      _cacheFieldHelper.SetXmlNodeString("d:fieldGroup/d:rangePr/@endDate", "9999-12-31T00:00:00");
-    } else {
-      _cacheFieldHelper.SetXmlNodeString(
-          "d:fieldGroup/d:rangePr/@endDate",
-          endDate.ToString("s", CultureInfo.InvariantCulture));
-      _cacheFieldHelper.SetXmlNodeString("d:fieldGroup/d:rangePr/@autoEnd", "0");
-    }
-
-    int items = AddDateGroupItems(group, groupBy, startDate, endDate, interval);
-    AddFieldItems(items);
-
-    _grouping = group;
-    return group;
-  }
-
-  internal ExcelPivotTableFieldNumericGroup SetNumericGroup(
-      double start,
-      double end,
-      double interval) {
-    ExcelPivotTableFieldNumericGroup group;
-    group = new(NameSpaceManager, _cacheFieldHelper.TopNode);
-    _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsNumber", true);
-    _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsInteger", true);
-    _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsSemiMixedTypes", false);
-    _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsString", false);
-
-    group.TopNode.InnerXml += string.Format(
-        "<fieldGroup base=\"{0}\"><rangePr autoStart=\"0\" autoEnd=\"0\" startNum=\"{1}\" endNum=\"{2}\" groupInterval=\"{3}\"/><groupItems /></fieldGroup>",
-        BaseIndex,
-        start.ToString(CultureInfo.InvariantCulture),
-        end.ToString(CultureInfo.InvariantCulture),
-        interval.ToString(CultureInfo.InvariantCulture));
-    int items = AddNumericGroupItems(group, start, end, interval);
-    AddFieldItems(items);
-
-    _grouping = group;
-    return group;
-  }
-
-  private int AddNumericGroupItems(
-      ExcelPivotTableFieldNumericGroup group,
-      double start,
-      double end,
-      double interval) {
-    if (interval < 0) {
-      throw (new("The interval must be a positiv"));
-    }
-    if (start > end) {
-      throw (new("Then End number must be larger than the Start number"));
-    }
-
-    XmlElement groupItems =
-        group.TopNode.SelectSingleNode("d:fieldGroup/d:groupItems", group.NameSpaceManager)
-            as XmlElement;
-    int items = 2;
-    //First date
-    double index = start;
-    double nextIndex = start + interval;
-    AddGroupItem(groupItems, "<" + start.ToString(CultureInfo.InvariantCulture));
-
-    while (index < end) {
-      AddGroupItem(
-          groupItems,
-          string.Format(
-              "{0}-{1}",
-              index.ToString(CultureInfo.InvariantCulture),
-              nextIndex.ToString(CultureInfo.InvariantCulture)));
-      index = nextIndex;
-      nextIndex += interval;
-      items++;
-    }
-    AddGroupItem(groupItems, ">" + nextIndex.ToString(CultureInfo.InvariantCulture));
-    return items;
-  }
-
-  private void AddFieldItems(int items) {
-    XmlElement prevNode = null;
-    XmlElement itemsNode = TopNode.SelectSingleNode("d:items", NameSpaceManager) as XmlElement;
-    for (int x = 0; x < items; x++) {
-      var itemNode = itemsNode.OwnerDocument.CreateElement("item", ExcelPackage._schemaMain);
-      itemNode.SetAttribute("x", x.ToString());
-      if (prevNode == null) {
-        itemsNode.PrependChild(itemNode);
-      } else {
-        itemsNode.InsertAfter(itemNode, prevNode);
-      }
-      prevNode = itemNode;
-    }
-    itemsNode.SetAttribute("count", (items + 1).ToString());
-  }
-
-  private int AddDateGroupItems(
-      ExcelPivotTableFieldGroup group,
-      eDateGroupBy groupBy,
-      DateTime startDate,
-      DateTime endDate,
-      int interval) {
-    XmlElement groupItems =
-        group.TopNode.SelectSingleNode("d:fieldGroup/d:groupItems", group.NameSpaceManager)
-            as XmlElement;
-    int items = 2;
-    //First date
-    AddGroupItem(
-        groupItems,
-        "<" + startDate.ToString("s", CultureInfo.InvariantCulture).Substring(0, 10));
-
-    switch (groupBy) {
-      case eDateGroupBy.Seconds:
-      case eDateGroupBy.Minutes:
-        AddTimeSerie(60, groupItems);
-        items += 60;
-        break;
-      case eDateGroupBy.Hours:
-        AddTimeSerie(24, groupItems);
-        items += 24;
-        break;
-      case eDateGroupBy.Days:
-        if (interval == 1) {
-          DateTime dt = new DateTime(2008, 1, 1); //pick a year with 366 days
-          while (dt.Year == 2008) {
-            AddGroupItem(groupItems, dt.ToString("dd-MMM"));
-            dt = dt.AddDays(1);
-          }
-          items += 366;
-        } else {
-          DateTime dt = startDate;
-          items = 0;
-          while (dt < endDate) {
-            AddGroupItem(groupItems, dt.ToString("dd-MMM"));
-            dt = dt.AddDays(interval);
-            items++;
-          }
-        }
-        break;
-      case eDateGroupBy.Months:
-        AddGroupItem(groupItems, "jan");
-        AddGroupItem(groupItems, "feb");
-        AddGroupItem(groupItems, "mar");
-        AddGroupItem(groupItems, "apr");
-        AddGroupItem(groupItems, "may");
-        AddGroupItem(groupItems, "jun");
-        AddGroupItem(groupItems, "jul");
-        AddGroupItem(groupItems, "aug");
-        AddGroupItem(groupItems, "sep");
-        AddGroupItem(groupItems, "oct");
-        AddGroupItem(groupItems, "nov");
-        AddGroupItem(groupItems, "dec");
-        items += 12;
-        break;
-      case eDateGroupBy.Quarters:
-        AddGroupItem(groupItems, "Qtr1");
-        AddGroupItem(groupItems, "Qtr2");
-        AddGroupItem(groupItems, "Qtr3");
-        AddGroupItem(groupItems, "Qtr4");
-        items += 4;
-        break;
-      case eDateGroupBy.Years:
-        if (startDate.Year >= 1900 && endDate != DateTime.MaxValue) {
-          for (int year = startDate.Year; year <= endDate.Year; year++) {
-            AddGroupItem(groupItems, year.ToString());
-          }
-          items += endDate.Year - startDate.Year + 1;
-        }
-        break;
-      default:
-        throw (new("unsupported grouping"));
-    }
-
-    //Lastdate
-    AddGroupItem(
-        groupItems,
-        ">" + endDate.ToString("s", CultureInfo.InvariantCulture).Substring(0, 10));
-    return items;
-  }
-
-  private void AddTimeSerie(int count, XmlElement groupItems) {
-    for (int i = 0; i < count; i++) {
-      AddGroupItem(groupItems, string.Format("{0:00}", i));
-    }
-  }
-
-  private void AddGroupItem(XmlElement groupItems, string value) {
-    var s = groupItems.OwnerDocument.CreateElement("s", ExcelPackage._schemaMain);
-    s.SetAttribute("v", value);
-    groupItems.AppendChild(s);
-  }
-
   internal ExcelPivotTableFieldCollectionBase<ExcelPivotTableFieldItem> _items;
 
   /// <summary>
@@ -775,236 +559,8 @@
             _items.AddInternal(item);
           }
         }
-        //if (_grouping is ExcelPivotTableFieldDateGroup)
-        //{
-        //    ExcelPivotTableFieldDateGroup dtgrp = ((ExcelPivotTableFieldDateGroup)_grouping);
-
-        //    ExcelPivotTableFieldItem minItem=null, maxItem=null;
-        //    foreach (var item in _items)
-        //    {
-        //        if (item.X == 0)
-        //        {
-        //            minItem = item;
-        //        }
-        //        else if (maxItem == null || maxItem.X < item.X)
-        //        {
-        //            maxItem = item;
-        //        }
-        //    }
-        //    if (dtgrp.AutoStart)
-        //    {
-        //        _items._list.Remove(minItem);
-        //    }
-        //    if (dtgrp.AutoEnd)
-        //    {
-        //        _items._list.Remove(maxItem);
-        //    }
-
-        //}
       }
       return _items;
     }
   }
-
-  /// <summary>
-  /// Add numberic grouping to the field
-  /// </summary>
-  /// <param name="start">Start value</param>
-  /// <param name="end">End value</param>
-  /// <param name="interval">Interval</param>
-  public void AddNumericGrouping(double start, double end, double interval) {
-    ValidateGrouping();
-    SetNumericGroup(start, end, interval);
-  }
-
-  /// <summary>
-  /// Add a date grouping on this field.
-  /// </summary>
-  /// <param name="groupBy">Group by</param>
-  public void AddDateGrouping(eDateGroupBy groupBy) {
-    AddDateGrouping(groupBy, DateTime.MinValue, DateTime.MaxValue, 1);
-  }
-
-  /// <summary>
-  /// Add a date grouping on this field.
-  /// </summary>
-  /// <param name="groupBy">Group by</param>
-  /// <param name="startDate">Fixed start date. Use DateTime.MinValue for auto</param>
-  /// <param name="endDate">Fixed end date. Use DateTime.MaxValue for auto</param>
-  public void AddDateGrouping(eDateGroupBy groupBy, DateTime startDate, DateTime endDate) {
-    AddDateGrouping(groupBy, startDate, endDate, 1);
-  }
-
-  /// <summary>
-  /// Add a date grouping on this field.
-  /// </summary>
-  /// <param name="days">Number of days when grouping on days</param>
-  /// <param name="startDate">Fixed start date. Use DateTime.MinValue for auto</param>
-  /// <param name="endDate">Fixed end date. Use DateTime.MaxValue for auto</param>
-  public void AddDateGrouping(int days, DateTime startDate, DateTime endDate) {
-    AddDateGrouping(eDateGroupBy.Days, startDate, endDate, days);
-  }
-
-  private void AddDateGrouping(
-      eDateGroupBy groupBy,
-      DateTime startDate,
-      DateTime endDate,
-      int groupInterval) {
-    if (groupInterval < 1 || groupInterval >= Int16.MaxValue) {
-      throw (new ArgumentOutOfRangeException("Group interval is out of range"));
-    }
-    if (groupInterval > 1 && groupBy != eDateGroupBy.Days) {
-      throw (new ArgumentException("Group interval is can only be used when groupBy is Days"));
-    }
-    ValidateGrouping();
-
-    bool firstField = true;
-    List<ExcelPivotTableField> fields = new List<ExcelPivotTableField>();
-    //Seconds
-    if ((groupBy & eDateGroupBy.Seconds) == eDateGroupBy.Seconds) {
-      fields.Add(AddField(eDateGroupBy.Seconds, startDate, endDate, ref firstField));
-    }
-    //Minutes
-    if ((groupBy & eDateGroupBy.Minutes) == eDateGroupBy.Minutes) {
-      fields.Add(AddField(eDateGroupBy.Minutes, startDate, endDate, ref firstField));
-    }
-    //Hours
-    if ((groupBy & eDateGroupBy.Hours) == eDateGroupBy.Hours) {
-      fields.Add(AddField(eDateGroupBy.Hours, startDate, endDate, ref firstField));
-    }
-    //Days
-    if ((groupBy & eDateGroupBy.Days) == eDateGroupBy.Days) {
-      fields.Add(AddField(eDateGroupBy.Days, startDate, endDate, ref firstField, groupInterval));
-    }
-    //Month
-    if ((groupBy & eDateGroupBy.Months) == eDateGroupBy.Months) {
-      fields.Add(AddField(eDateGroupBy.Months, startDate, endDate, ref firstField));
-    }
-    //Quarters
-    if ((groupBy & eDateGroupBy.Quarters) == eDateGroupBy.Quarters) {
-      fields.Add(AddField(eDateGroupBy.Quarters, startDate, endDate, ref firstField));
-    }
-    //Years
-    if ((groupBy & eDateGroupBy.Years) == eDateGroupBy.Years) {
-      fields.Add(AddField(eDateGroupBy.Years, startDate, endDate, ref firstField));
-    }
-
-    if (fields.Count > 1) {
-      _cacheFieldHelper.SetXmlNodeString("d:fieldGroup/@par", (_table.Fields.Count - 1).ToString());
-    }
-    if (groupInterval != 1) {
-      _cacheFieldHelper.SetXmlNodeString(
-          "d:fieldGroup/d:rangePr/@groupInterval",
-          groupInterval.ToString());
-    } else {
-      _cacheFieldHelper.DeleteNode("d:fieldGroup/d:rangePr/@groupInterval");
-    }
-    _items = null;
-  }
-
-  private void ValidateGrouping() {
-    if (!(IsColumnField || IsRowField)) {
-      throw (new("Field must be a row or column field"));
-    }
-    foreach (var field in _table.Fields) {
-      if (field.Grouping != null) {
-        throw (new("Grouping already exists"));
-      }
-    }
-  }
-
-  private ExcelPivotTableField AddField(
-      eDateGroupBy groupBy,
-      DateTime startDate,
-      DateTime endDate,
-      ref bool firstField) {
-    return AddField(groupBy, startDate, endDate, ref firstField, 1);
-  }
-
-  private ExcelPivotTableField AddField(
-      eDateGroupBy groupBy,
-      DateTime startDate,
-      DateTime endDate,
-      ref bool firstField,
-      int interval) {
-    if (firstField == false) {
-      //Pivot field
-      var topNode = _table.PivotTableXml.SelectSingleNode(
-          "//d:pivotFields",
-          _table.NameSpaceManager);
-      var fieldNode = _table.PivotTableXml.CreateElement("pivotField", ExcelPackage._schemaMain);
-      fieldNode.SetAttribute("compact", "0");
-      fieldNode.SetAttribute("outline", "0");
-      fieldNode.SetAttribute("showAll", "0");
-      fieldNode.SetAttribute("defaultSubtotal", "0");
-      topNode.AppendChild(fieldNode);
-
-      var field = new ExcelPivotTableField(
-          _table.NameSpaceManager,
-          fieldNode,
-          _table,
-          _table.Fields.Count,
-          Index);
-      field.DateGrouping = groupBy;
-
-      XmlNode rowColFields;
-      if (IsRowField) {
-        rowColFields = TopNode.SelectSingleNode("../../d:rowFields", NameSpaceManager);
-      } else {
-        rowColFields = TopNode.SelectSingleNode("../../d:colFields", NameSpaceManager);
-      }
-
-      var index = 0;
-      foreach (XmlElement rowfield in rowColFields.ChildNodes) {
-        if (int.TryParse(rowfield.GetAttribute("x"), out var fieldIndex)) {
-          if (_table.Fields[fieldIndex].BaseIndex == BaseIndex) {
-            var newElement = rowColFields.OwnerDocument.CreateElement(
-                "field",
-                ExcelPackage._schemaMain);
-            newElement.SetAttribute("x", field.Index.ToString());
-            rowColFields.InsertBefore(newElement, rowfield);
-            break;
-          }
-        }
-        index++;
-      }
-
-      if (IsRowField) {
-        _table.RowFields.Insert(field, index);
-      } else {
-        _table.ColumnFields.Insert(field, index);
-      }
-
-      _table.Fields.AddInternal(field);
-
-      AddCacheField(field, startDate, endDate, interval);
-      return field;
-    }
-    firstField = false;
-    DateGrouping = groupBy;
-    Compact = false;
-    SetDateGroup(groupBy, startDate, endDate, interval);
-    return this;
-  }
-
-  private void AddCacheField(
-      ExcelPivotTableField field,
-      DateTime startDate,
-      DateTime endDate,
-      int interval) {
-    //Add Cache definition field.
-    var cacheTopNode = _table.CacheDefinition.CacheDefinitionXml.SelectSingleNode(
-        "//d:cacheFields",
-        _table.NameSpaceManager);
-    var cacheFieldNode = _table.CacheDefinition.CacheDefinitionXml.CreateElement(
-        "cacheField",
-        ExcelPackage._schemaMain);
-
-    cacheFieldNode.SetAttribute("name", field.DateGrouping.ToString());
-    cacheFieldNode.SetAttribute("databaseField", "0");
-    cacheTopNode.AppendChild(cacheFieldNode);
-    field.SetCacheFieldNode(cacheFieldNode);
-
-    field.SetDateGroup(field.DateGrouping, startDate, endDate, interval);
-  }
 }
diff --git a/EPPlus/XmlHelper.cs b/EPPlus/XmlHelper.cs
index a958baf..b311997 100644
--- a/EPPlus/XmlHelper.cs
+++ b/EPPlus/XmlHelper.cs
@@ -33,11 +33,11 @@
  *******************************************************************************/
 
 using System;
+using System.Collections.Immutable;
 using System.Globalization;
 using System.IO;
 using System.Text;
 using System.Xml;
-using OfficeOpenXml.Packaging;
 using OfficeOpenXml.Style;
 
 namespace OfficeOpenXml;
@@ -64,15 +64,10 @@
 
   internal XmlNode TopNode { get; set; }
 
-  private string[] _schemaNodeOrder;
-
   /// <summary>
   /// Schema order list
   /// </summary>
-  internal string[] SchemaNodeOrder {
-    get => _schemaNodeOrder;
-    set => _schemaNodeOrder = value;
-  }
+  protected virtual ImmutableArray<string> SchemaNodeOrder => ImmutableArray<string>.Empty;
 
   internal XmlNode CreateNode(string path) {
     if (path == "") {
@@ -90,10 +85,10 @@
         string nodeName;
         string nodePrefix;
 
-        string nameSpaceUri = "";
+        string nameSpaceUri;
         string[] nameSplit = subPath.Split(':');
 
-        if (SchemaNodeOrder != null && subPath[0] != '@') {
+        if (SchemaNodeOrder.Length > 0 && subPath[0] != '@') {
           insertFirst = false;
           prependNode = GetPrependNode(subPath, node);
         }
@@ -176,16 +171,6 @@
   }
 
   /// <summary>
-  /// Create a complex node. Insert the node according to SchemaOrder
-  /// using the TopNode as the parent
-  /// </summary>
-  /// <param name="path"></param>
-  /// <returns></returns>
-  internal XmlNode CreateComplexNode(string path) {
-    return CreateComplexNode(TopNode, path, eNodeInsertOrder.SchemaOrder, null);
-  }
-
-  /// <summary>
   /// Create a complex node. Insert the node according to the <paramref name="path"/>
   /// using the <paramref name="topNode"/> as the parent
   /// </summary>
@@ -243,7 +228,6 @@
     }
 
     XmlNode node = topNode;
-    string nameSpaceUri = string.Empty;
 
     //TODO: BUG: when the "path" contains "/" in an attrribue value, it gives an error.
 
@@ -312,7 +296,7 @@
             string nodeName;
             string nodePrefix;
             string[] nameSplit = subPath.Split(':');
-            nameSpaceUri = string.Empty;
+            string nameSpaceUri;
 
             // Check if the name has a prefix like "d:nodeName"
             if (nameSplit.Length > 1) {
@@ -347,7 +331,7 @@
             // Check if we need to use the "SchemaOrder"
             if (nodeInsertOrder == eNodeInsertOrder.SchemaOrder) {
               // Check if the Schema Order List is empty
-              if ((SchemaNodeOrder == null) || (SchemaNodeOrder.Length == 0)) {
+              if (SchemaNodeOrder.Length == 0) {
                 // Use the "Insert Last" option when Schema Order List is empty
                 nodeInsertOrder = eNodeInsertOrder.Last;
               } else {
@@ -428,8 +412,8 @@
     if (ix > 0) {
       nodeName = nodeName.Substring(ix + 1, nodeName.Length - (ix + 1));
     }
-    for (int i = 0; i < _schemaNodeOrder.Length; i++) {
-      if (nodeName == _schemaNodeOrder[i]) {
+    for (int i = 0; i < SchemaNodeOrder.Length; i++) {
+      if (nodeName == SchemaNodeOrder[i]) {
         return i;
       }
     }
@@ -464,10 +448,6 @@
     }
   }
 
-  internal void DeleteTopNode() {
-    TopNode.ParentNode.RemoveChild(TopNode);
-  }
-
   internal void SetXmlNodeString(string path, string value) {
     SetXmlNodeString(TopNode, path, value, false, false);
   }
@@ -476,10 +456,6 @@
     SetXmlNodeString(TopNode, path, value, removeIfBlank, false);
   }
 
-  internal void SetXmlNodeString(XmlNode node, string path, string value) {
-    SetXmlNodeString(node, path, value, false, false);
-  }
-
   internal void SetXmlNodeString(XmlNode node, string path, string value, bool removeIfBlank) {
     SetXmlNodeString(node, path, value, removeIfBlank, false);
   }
@@ -637,37 +613,6 @@
     return GetXmlNodeString(TopNode, path);
   }
 
-  internal static Uri GetNewUri(ZipPackage package, string sUri) {
-    return GetNewUri(package, sUri, 1);
-  }
-
-  internal static Uri GetNewUri(ZipPackage package, string sUri, int id) {
-    Uri uri;
-    do {
-      uri = new(string.Format(sUri, id++), UriKind.Relative);
-    } while (package.PartExists(uri));
-    return uri;
-  }
-
-  /// <summary>
-  /// Insert the new node before any of the nodes in the comma separeted list
-  /// </summary>
-  /// <param name="parentNode">Parent node</param>
-  /// <param name="beforeNodes">comma separated list containing nodes to insert after. Left to right order</param>
-  /// <param name="newNode">The new node to be inserterd</param>
-  internal void InserAfter(XmlNode parentNode, string beforeNodes, XmlNode newNode) {
-    string[] nodePaths = beforeNodes.Split(',');
-
-    foreach (string nodePath in nodePaths) {
-      XmlNode node = parentNode.SelectSingleNode(nodePath, NameSpaceManager);
-      if (node != null) {
-        parentNode.InsertAfter(newNode, node);
-        return;
-      }
-    }
-    parentNode.InsertAfter(newNode, null);
-  }
-
   internal static void LoadXmlSafe(XmlDocument xmlDoc, Stream stream) {
     XmlReaderSettings settings = new XmlReaderSettings();
     //Disable entity parsing (to aviod xmlbombs, External Entity Attacks etc).