diff --git a/src/MiniExcel.Core/Helpers/NetStandardHelper.cs b/src/MiniExcel.Core/Helpers/NetStandardHelper.cs
deleted file mode 100644
index 16372635..00000000
--- a/src/MiniExcel.Core/Helpers/NetStandardHelper.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace MiniExcelLib.Core.Helpers;
-
-#if NETSTANDARD2_0
-
-///
-/// Provides .NET Standard 2.0 polyfills for utility methods found in later framework versions.
-/// This enables a unified API surface across the codebase without the need for conditional compilation directives.
-///
-public static class NetStandardExtensions
-{
- public static TValue? GetValueOrDefault(this IReadOnlyDictionary dictionary, TKey key, TValue? defaultValue = default)
- {
- return dictionary.TryGetValue(key, out var value) ? value : defaultValue;
- }
-}
-#endif
diff --git a/src/MiniExcel.Core/Helpers/Polyfills.cs b/src/MiniExcel.Core/Helpers/Polyfills.cs
new file mode 100644
index 00000000..c84b1421
--- /dev/null
+++ b/src/MiniExcel.Core/Helpers/Polyfills.cs
@@ -0,0 +1,63 @@
+using System.ComponentModel;
+using System.IO.Compression;
+
+namespace MiniExcelLib.Core.Helpers;
+
+/* todo: instead of using the EditorBrowsableAttribute consider making this class internal and link it for compilation
+ in the other projects that require it so as to prevent the consumers' IDEs to be polluted with these extension methods */
+[EditorBrowsable(EditorBrowsableState.Advanced)]
+public static class Polyfills
+{
+#if NETSTANDARD2_0
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public static TValue? GetValueOrDefault(this IDictionary dictionary, TKey key, TValue? defaultValue = default)
+ {
+ return dictionary.TryGetValue(key, out var value) ? value : defaultValue;
+ }
+
+ extension(Math)
+ {
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public static TNumber Clamp(TNumber value, TNumber min, TNumber max) where TNumber : unmanaged, IComparable
+ {
+ if (value.CompareTo(min) < 0) return min;
+ if (value.CompareTo(max) > 0) return max;
+ return value;
+ }
+ }
+#endif
+
+#if !NET10_0_OR_GREATER
+ extension(ZipArchiveEntry entry)
+ {
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public ValueTask OpenAsync(CancellationToken cancellationToken = default)
+ {
+ var stream = entry.Open();
+ return new ValueTask(stream);
+ }
+ }
+
+ extension(ZipArchive)
+ {
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public static ValueTask CreateAsync(Stream stream, ZipArchiveMode mode, bool leaveOpen, Encoding? entryNameEncoding = null, CancellationToken cancellationToken = default)
+ {
+ ZipArchive? archive = null;
+
+ try
+ {
+ archive = new ZipArchive(stream, mode, leaveOpen, entryNameEncoding);
+ var result = new ValueTask(archive);
+
+ archive = null;
+ return result;
+ }
+ finally
+ {
+ archive?.Dispose();
+ }
+ }
+ }
+#endif
+}
diff --git a/src/MiniExcel.Core/Helpers/SynchronousHelper.cs b/src/MiniExcel.Core/Helpers/SynchronousHelper.cs
index 9277af6c..5a1bac57 100644
--- a/src/MiniExcel.Core/Helpers/SynchronousHelper.cs
+++ b/src/MiniExcel.Core/Helpers/SynchronousHelper.cs
@@ -10,7 +10,7 @@ public static class SynchronousHelper
{
extension(ZipArchive)
{
- public static ZipArchive Create(Stream stream, ZipArchiveMode mode, bool leaveOpen, Encoding? encoding = null)
- => new(stream, mode, leaveOpen, encoding);
+ public static ZipArchive Create(Stream stream, ZipArchiveMode mode, bool leaveOpen, Encoding? entryNameEncoding = null)
+ => new(stream, mode, leaveOpen, entryNameEncoding);
}
}
diff --git a/src/MiniExcel.OpenXml/Api/OpenXmlExporter.cs b/src/MiniExcel.OpenXml/Api/OpenXmlExporter.cs
index 7634227f..8f8378b1 100644
--- a/src/MiniExcel.OpenXml/Api/OpenXmlExporter.cs
+++ b/src/MiniExcel.OpenXml/Api/OpenXmlExporter.cs
@@ -69,4 +69,44 @@ public async Task ExportAsync(Stream stream, object value, bool printHead
return await writer.SaveAsAsync(progress, cancellationToken).ConfigureAwait(false);
}
+
+ ///
+ /// Modify the properties of a worksheet in the specified document.
+ ///
+ /// The path to the OpenXml document.
+ /// The name of the worksheet to modify.
+ /// The new name to assign to the worksheet, or null to leave as is.
+ /// The position in the workbook to assign to the worksheet, or null to leave as is.
+ /// The visibility state to assign to the worksheet, or null to leave as is.
+ /// The token to monitor for cancellation requests
+ [CreateSyncVersion]
+ public async Task AlterSheetAsync(string path, string sheetName, string? newSheetName = null, int? newSheetIndex = null, SheetState? newSheetState = null, CancellationToken cancellationToken = default)
+ {
+#if NET8_0_OR_GREATER
+ var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
+ await using var disposableStream = stream.ConfigureAwait(false);
+#else
+ using var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
+#endif
+ await AlterSheetAsync(stream, sheetName, newSheetName, newSheetIndex, newSheetState, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Modify the properties of a worksheet in the specified document.
+ ///
+ /// The stream to the OpenXml document.
+ /// The name of the worksheet to modify.
+ /// The new name to assign to the worksheet, or null to leave as is.
+ /// The position in the workbook to assign to the worksheet, or null to leave as is.
+ /// The visibility state to assign to the worksheet, or null to leave as is.
+ /// The token to monitor for cancellation requests
+ [CreateSyncVersion]
+ public async Task AlterSheetAsync(Stream stream, string sheetName, string? newSheetName = null, int? newSheetIndex = null, SheetState? newSheetState = null, CancellationToken cancellationToken = default)
+ {
+ var writer = await OpenXmlWriter
+ .CreateAsync(stream, null, sheetName, false, new OpenXmlConfiguration { FastMode = true }, cancellationToken)
+ .ConfigureAwait(false);
+
+ await writer.AlterWorksheetAsync(sheetName, newSheetName, newSheetIndex, newSheetState, cancellationToken).ConfigureAwait(false);
+ }
}
diff --git a/src/MiniExcel.OpenXml/FluentMapping/MappingTemplateApplicator.cs b/src/MiniExcel.OpenXml/FluentMapping/MappingTemplateApplicator.cs
index 969eb35e..1d9bcdd6 100644
--- a/src/MiniExcel.OpenXml/FluentMapping/MappingTemplateApplicator.cs
+++ b/src/MiniExcel.OpenXml/FluentMapping/MappingTemplateApplicator.cs
@@ -1,6 +1,3 @@
-using System.IO.Compression;
-using Zomp.SyncMethodGenerator;
-
namespace MiniExcelLib.OpenXml.FluentMapping;
internal static partial class MappingTemplateApplicator where T : class
@@ -37,12 +34,20 @@ public static async Task ApplyTemplateAsync(
}
templateStream.Position = 0;
-
+
+#if NET10_0_OR_GREATER
// Open template archive for reading
- using var templateArchive = new ZipArchive(templateStream, ZipArchiveMode.Read, leaveOpen: true);
-
+ var templateArchive = await ZipArchive.CreateAsync(templateStream, ZipArchiveMode.Read, true, null, cancellationToken: cancellationToken).ConfigureAwait(false);
+ await using var disposableTemplateArchive = templateArchive.ConfigureAwait(false);
+
// Create output archive
- using var outputArchive = new ZipArchive(outputStream, ZipArchiveMode.Create, leaveOpen: true);
+ var outputArchive = await ZipArchive.CreateAsync(outputStream, ZipArchiveMode.Create, true, null, cancellationToken).ConfigureAwait(false);
+ await using var disposableOutputArchive = outputArchive.ConfigureAwait(false);
+#else
+ using var templateArchive = new ZipArchive(templateStream, ZipArchiveMode.Read, true);
+ using var outputArchive = new ZipArchive(outputStream, ZipArchiveMode.Create, true);
+#endif
+
// Process each entry
foreach (var entry in templateArchive.Entries)
@@ -106,17 +111,18 @@ private static async Task CopyEntryAsync(
targetEntry.LastWriteTime = sourceEntry.LastWriteTime;
// Copy content
-#if NET10_0_OR_GREATER
- using var sourceStream = await sourceEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
- using var targetStream = await targetEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
+#if NET8_0_OR_GREATER
+ var sourceStream = await sourceEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
+ var targetStream = await targetEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
+
+ await using var disposableSourceStream = sourceStream.ConfigureAwait(false);
+ await using var disposableTargetStream = targetStream.ConfigureAwait(false);
+
+ await sourceStream.CopyToAsync(targetStream, cancellationToken).ConfigureAwait(false);
#else
using var sourceStream = sourceEntry.Open();
using var targetStream = targetEntry.Open();
-#endif
-
-#if NETCOREAPP2_1_OR_GREATER
- await sourceStream.CopyToAsync(targetStream, cancellationToken).ConfigureAwait(false);
-#else
+
await sourceStream.CopyToAsync(targetStream).ConfigureAwait(false);
#endif
}
@@ -135,9 +141,12 @@ private static async Task ProcessWorksheetAsync(
targetEntry.LastWriteTime = sourceEntry.LastWriteTime;
// Open streams
-#if NET10_0_OR_GREATER
- using var sourceStream = await sourceEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
- using var targetStream = await targetEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
+#if NET8_0_OR_GREATER
+ var sourceStream = await sourceEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
+ var targetStream = await targetEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
+
+ await using var disposableSourceStream = sourceStream.ConfigureAwait(false);
+ await using var disposableTargetStream = targetStream.ConfigureAwait(false);
#else
using var sourceStream = sourceEntry.Open();
using var targetStream = targetEntry.Open();
diff --git a/src/MiniExcel.OpenXml/Models/ExcelSheetInfo.cs b/src/MiniExcel.OpenXml/Models/ExcelSheetInfo.cs
new file mode 100644
index 00000000..328683a7
--- /dev/null
+++ b/src/MiniExcel.OpenXml/Models/ExcelSheetInfo.cs
@@ -0,0 +1,15 @@
+namespace MiniExcelLib.OpenXml.Models;
+
+internal class ExcelSheetInfo
+{
+ public object Key { get; set; }
+ public string? ExcelSheetName { get; set; }
+ public SheetState ExcelSheetState { get; set; }
+
+ public SheetDto ToDto(int sheetIndex) => new()
+ {
+ Name = ExcelSheetName,
+ SheetIdx = sheetIndex,
+ State = ExcelSheetState.ToString().ToLower()
+ };
+}
\ No newline at end of file
diff --git a/src/MiniExcel.OpenXml/Models/ExcellSheetInfo.cs b/src/MiniExcel.OpenXml/Models/ExcellSheetInfo.cs
deleted file mode 100644
index 57cb3efd..00000000
--- a/src/MiniExcel.OpenXml/Models/ExcellSheetInfo.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace MiniExcelLib.OpenXml.Models;
-
-internal class ExcellSheetInfo
-{
- public object Key { get; set; }
- public string? ExcelSheetName { get; set; }
- public SheetState ExcelSheetState { get; set; }
-
- private string ExcelSheetStateAsString => ExcelSheetState.ToString().ToLower();
-
- public SheetDto ToDto(int sheetIndex)
- {
- return new SheetDto { Name = ExcelSheetName, SheetIdx = sheetIndex, State = ExcelSheetStateAsString };
- }
-}
\ No newline at end of file
diff --git a/src/MiniExcel.OpenXml/Models/SheetInfo.cs b/src/MiniExcel.OpenXml/Models/SheetInfo.cs
index ac063d16..0e60e824 100644
--- a/src/MiniExcel.OpenXml/Models/SheetInfo.cs
+++ b/src/MiniExcel.OpenXml/Models/SheetInfo.cs
@@ -3,17 +3,17 @@ namespace MiniExcelLib.OpenXml.Models;
public class SheetInfo(uint id, uint index, string name, SheetState sheetState, bool active)
{
///
- /// Internal sheet id - depends on the order in which the sheet is added
+ /// Internal sheet id - depends on the order in which the sheet is added.
///
public uint Id { get; } = id;
///
- /// Next sheet index - numbered from 0
+ /// The 0-based index of the worksheet in the workbook
///
public uint Index { get; } = index;
///
- /// Sheet name
+ /// The name of the worksheet
///
public string Name { get; } = name;
@@ -23,7 +23,7 @@ public class SheetInfo(uint id, uint index, string name, SheetState sheetState,
public SheetState State { get; } = sheetState;
///
- /// Indicates whether the sheet is active
+ /// Indicates whether the worksheet was active the last time
///
public bool Active { get; } = active;
}
diff --git a/src/MiniExcel.OpenXml/OpenXmlReader.cs b/src/MiniExcel.OpenXml/OpenXmlReader.cs
index a6924561..c623398d 100644
--- a/src/MiniExcel.OpenXml/OpenXmlReader.cs
+++ b/src/MiniExcel.OpenXml/OpenXmlReader.cs
@@ -21,9 +21,9 @@ internal partial class OpenXmlReader : IMiniExcelReader
internal readonly OpenXmlZip Archive;
internal IDictionary SharedStrings = new Dictionary();
- private OpenXmlReader(OpenXmlZip openXmlZip, IMiniExcelConfiguration? configuration)
+ private OpenXmlReader(OpenXmlZip archive, IMiniExcelConfiguration? configuration)
{
- Archive = openXmlZip;
+ Archive = archive;
_config = (OpenXmlConfiguration?)configuration ?? OpenXmlConfiguration.Default;
}
@@ -34,8 +34,8 @@ internal static async Task CreateAsync(Stream stream, IMiniExcelC
var archive = await OpenXmlZip.CreateAsync(stream, cancellationToken: cancellationToken).ConfigureAwait(false);
var reader = new OpenXmlReader(archive, configuration);
-
await reader.SetSharedStringsAsync(cancellationToken).ConfigureAwait(false);
+
return reader;
}
@@ -48,7 +48,7 @@ internal static async Task CreateAsync(Stream stream, IMiniExcelC
[CreateSyncVersion]
public IAsyncEnumerable QueryAsync(string? sheetName, string startCell, bool mapHeaderAsData, CancellationToken cancellationToken = default) where T : class, new()
{
- sheetName ??= MiniExcelPropertyHelper.GetExcellSheetInfo(typeof(T), _config)?.ExcelSheetName;
+ sheetName ??= MiniExcelPropertyHelper.GetExcelSheetInfo(typeof(T), _config)?.ExcelSheetName;
var query = QueryAsync(false, sheetName, startCell, cancellationToken);
if (!CellReferenceConverter.TryParseCellReference(startCell, out _, out var rowOffset))
{
@@ -178,27 +178,24 @@ internal static async Task CreateAsync(Stream stream, IMiniExcelC
maxColumnIndex = endColumnIndex.Value;
}
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var sheetStream = await sheetEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableSheetStream = sheetStream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var sheetStream = sheetEntry.Open();
- await using var disposableSheetStream = sheetStream.ConfigureAwait(false);
#else
using var sheetStream = sheetEntry.Open();
#endif
using var reader = XmlReader.Create(sheetStream, xmlSettings);
- if (!XmlReaderHelper.IsStartElement(reader, "worksheet", Ns))
+ if (!reader.IsStartElement("worksheet", Ns))
yield break;
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
yield break;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "sheetData", Ns))
+ if (reader.IsStartElement("sheetData", Ns))
{
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
continue;
int rowIndex = -1;
@@ -206,7 +203,7 @@ internal static async Task CreateAsync(Stream stream, IMiniExcelC
var headRows = new Dictionary();
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "row", Ns))
+ if (reader.IsStartElement("row", Ns))
{
var nextRowIndex = rowIndex + 1;
if (int.TryParse(reader.GetAttribute("r"), out int arValue))
@@ -216,9 +213,9 @@ internal static async Task CreateAsync(Stream stream, IMiniExcelC
if (rowIndex < startRowIndex)
{
- if (await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
{
- await XmlReaderHelper.SkipToNextSameLevelDomAsync(reader, cancellationToken).ConfigureAwait(false);
+ await reader.SkipToNextSiblingAsync(cancellationToken).ConfigureAwait(false);
}
continue;
}
@@ -243,13 +240,13 @@ internal static async Task CreateAsync(Stream stream, IMiniExcelC
yield return row;
}
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
}
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
@@ -286,7 +283,7 @@ internal static async Task CreateAsync(Stream stream, IMiniExcelC
}
// row -> c, must after `if (nextRowIndex < rowIndex)` condition code, eg. The first empty row has no xml element,and the second row xml element is
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false) && !_config.IgnoreEmptyRows)
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false) && !_config.IgnoreEmptyRows)
{
//Fill in case of self closed empty row tag eg.
yield return GetCell(useHeaderRow, maxColumnIndex, headRows, startColumnIndex);
@@ -297,7 +294,7 @@ internal static async Task CreateAsync(Stream stream, IMiniExcelC
var columnIndex = withoutCr ? -1 : 0;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "c", Ns))
+ if (reader.IsStartElement("c", Ns))
{
var aS = reader.GetAttribute("s");
var aR = reader.GetAttribute("r");
@@ -335,7 +332,7 @@ internal static async Task CreateAsync(Stream stream, IMiniExcelC
SetCellsValueAndHeaders(cellValue, useHeaderRow, headRows, isFirstRow, cell, columnIndex);
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
@@ -429,12 +426,9 @@ private async Task SetSharedStringsAsync(CancellationToken cancellationToken = d
return;
var idx = 0;
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var stream = await sharedStringsEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableStream = stream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var stream = sharedStringsEntry.Open();
- await using var disposableStream = stream.ConfigureAwait(false);
#else
using var stream = sharedStringsEntry.Open();
#endif
@@ -473,33 +467,30 @@ private static async IAsyncEnumerable ReadWorkbookAsync(ReadOnlyCol
);
var entry = entries.Single(w => w.FullName == "xl/workbook.xml");
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var stream = await entry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableStream = stream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var stream = entry.Open();
- await using var disposableStream = stream.ConfigureAwait(false);
#else
using var stream = entry.Open();
#endif
using var reader = XmlReader.Create(stream, xmlSettings);
- if (!XmlReaderHelper.IsStartElement(reader, "workbook", Ns))
+ if (!reader.IsStartElement("workbook", Ns))
yield break;
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
yield break;
var activeSheetIndex = 0;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "bookViews", Ns))
+ if (reader.IsStartElement("bookViews", Ns))
{
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
continue;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "workbookView", Ns))
+ if (reader.IsStartElement("workbookView", Ns))
{
var activeSheet = reader.GetAttribute("activeTab");
if (int.TryParse(activeSheet, out var index))
@@ -513,27 +504,27 @@ await reader.SkipAsync()
#endif
.ConfigureAwait(false);
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
}
}
- else if (XmlReaderHelper.IsStartElement(reader, "sheets", Ns))
+ else if (reader.IsStartElement("sheets", Ns))
{
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
continue;
var sheetCount = 0;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "sheet", Ns))
+ if (reader.IsStartElement("sheet", Ns))
{
yield return new SheetRecord(
reader.GetAttribute("name"),
reader.GetAttribute("state"),
uint.Parse(reader.GetAttribute("sheetId")),
- XmlReaderHelper.GetAttribute(reader, "id", RelationshiopNs),
+ reader.GetAttribute("id", RelationshiopNs),
sheetCount == activeSheetIndex
);
sheetCount++;
@@ -543,13 +534,13 @@ await reader.SkipAsync()
#endif
.ConfigureAwait(false);
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
}
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
yield break;
}
@@ -573,12 +564,9 @@ await reader.SkipAsync()
var entry = entries.Single(w => w.FullName == "xl/_rels/workbook.xml.rels");
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var stream = await entry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableStream = stream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var stream = entry.Open();
- await using var disposableStream = stream.ConfigureAwait(false);
#else
using var stream = entry.Open();
#endif
@@ -586,7 +574,7 @@ await reader.SkipAsync()
if (!XmlReaderHelper.IsStartElement(reader, "Relationships", Schemas.OpenXmlPackageRelationships))
return null;
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
return null;
while (!reader.EOF)
@@ -606,7 +594,7 @@ await reader.SkipAsync()
#endif
.ConfigureAwait(false);
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
@@ -647,25 +635,25 @@ private async Task ReadCellAndSetColumnIndexAsync(XmlReader reade
if (columnIndex < startColumnIndex)
{
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
return new CellAndColumn(null, columnIndex);
while (!reader.EOF)
{
- if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
break;
}
return new CellAndColumn(null, columnIndex);
}
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
return new CellAndColumn(null, columnIndex);
object? value = null;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "v", Ns))
+ if (reader.IsStartElement("v", Ns))
{
var rawValue = await reader.ReadElementContentAsStringAsync()
#if NET6_0_OR_GREATER
@@ -676,13 +664,13 @@ private async Task ReadCellAndSetColumnIndexAsync(XmlReader reade
if (!string.IsNullOrEmpty(rawValue))
ConvertCellValue(rawValue, aT, xfIndex, out value);
}
- else if (XmlReaderHelper.IsStartElement(reader, "is", Ns))
+ else if (reader.IsStartElement("is", Ns))
{
- var rawValue = await XmlReaderHelper.ReadStringItemAsync(reader, cancellationToken).ConfigureAwait(false);
+ var rawValue = await reader.ReadStringItemAsync(cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrEmpty(rawValue))
ConvertCellValue(rawValue, aT, xfIndex, out value);
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
@@ -720,15 +708,13 @@ private void ConvertCellValue(string rawValue, string aT, int xfIndex, out objec
if (v?.StartsWith("@@@fileid@@@,", StringComparison.Ordinal) ?? false)
{
var path = v[13..];
- var entry = Archive.GetEntry(path);
- var bytes = new byte[entry.Length];
+ var entry = Archive.GetEntry(path)!;
- using (var stream = entry.Open())
- using (var ms = new MemoryStream(bytes))
- {
- stream.CopyTo(ms);
- }
- value = bytes;
+ using var stream = entry.Open();
+ using var ms = new MemoryStream((int)entry.Length);
+
+ stream.CopyTo(ms);
+ value = ms.ToArray();
}
}
break;
@@ -782,12 +768,9 @@ internal async Task> GetDimensionsAsync(CancellationToken canc
var withoutCr = false;
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var crSheetStream = await sheet.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableCrSheetStream = crSheetStream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var crSheetStream = sheet.Open();
- await using var disposableCrSheetStream = crSheetStream.ConfigureAwait(false);
#else
using var crSheetStream = sheet.Open();
#endif
@@ -795,7 +778,7 @@ internal async Task> GetDimensionsAsync(CancellationToken canc
{
while (await reader.ReadAsync().ConfigureAwait(false))
{
- if (XmlReaderHelper.IsStartElement(reader, "c", Ns))
+ if (reader.IsStartElement("c", Ns))
{
var r = reader.GetAttribute("r");
if (r is not null)
@@ -815,7 +798,7 @@ internal async Task> GetDimensionsAsync(CancellationToken canc
}
}
- else if (XmlReaderHelper.IsStartElement(reader, "dimension", Ns))
+ else if (reader.IsStartElement("dimension", Ns))
{
var refAttr = reader.GetAttribute("ref");
if (string.IsNullOrEmpty(refAttr))
@@ -838,59 +821,56 @@ internal async Task> GetDimensionsAsync(CancellationToken canc
if (withoutCr)
{
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var sheetStream = await sheet.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableSheetStream = sheetStream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var sheetStream = sheet.Open();
- await using var disposableSheetStream = sheetStream.ConfigureAwait(false);
#else
using var sheetStream = sheet.Open();
#endif
using var reader = XmlReader.Create(sheetStream, xmlSettings);
- if (!XmlReaderHelper.IsStartElement(reader, "worksheet", Ns))
+ if (!reader.IsStartElement("worksheet", Ns))
throw new InvalidDataException("No worksheet data found for the sheet");
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
throw new InvalidOperationException("Excel sheet does not contain any data");
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "sheetData", Ns))
+ if (reader.IsStartElement("sheetData", Ns))
{
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
continue;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "row", Ns))
+ if (reader.IsStartElement("row", Ns))
{
maxRowIndex++;
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
continue;
var cellIndex = -1;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "c", Ns))
+ if (reader.IsStartElement("c", Ns))
{
cellIndex++;
maxColumnIndex = Math.Max(maxColumnIndex, cellIndex);
}
- if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
break;
}
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
}
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
@@ -941,12 +921,9 @@ internal static async Task TryGetMaxRowColumnIndexAs
int maxRowIndex = -1;
int maxColumnIndex = -1;
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var crSheetStream = await sheetEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableCrSheetStream = crSheetStream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var crSheetStream = sheetEntry.Open();
- await using var disposableCrSheetStream = crSheetStream.ConfigureAwait(false);
#else
using var crSheetStream = sheetEntry.Open();
#endif
@@ -958,7 +935,7 @@ internal static async Task TryGetMaxRowColumnIndexAs
#endif
.ConfigureAwait(false))
{
- if (XmlReaderHelper.IsStartElement(reader, "c", Ns))
+ if (reader.IsStartElement("c", Ns))
{
var r = reader.GetAttribute("r");
if (r is not null)
@@ -978,7 +955,7 @@ internal static async Task TryGetMaxRowColumnIndexAs
}
}
//this method logic depends on dimension to get maxcolumnIndex, if without dimension then it need to foreach all rows first time to get maxColumn and maxRowColumn
- else if (XmlReaderHelper.IsStartElement(reader, "dimension", Ns))
+ else if (reader.IsStartElement("dimension", Ns))
{
var refAttr = reader.GetAttribute("ref");
if (string.IsNullOrEmpty(refAttr))
@@ -999,60 +976,57 @@ internal static async Task TryGetMaxRowColumnIndexAs
if (withoutCr)
{
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var sheetStream = await sheetEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableSheetStream = sheetStream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var sheetStream = sheetEntry.Open();
- await using var disposableSheetStream = sheetStream.ConfigureAwait(false);
#else
using var sheetStream = sheetEntry.Open();
#endif
using var reader = XmlReader.Create(sheetStream, xmlSettings);
- if (!XmlReaderHelper.IsStartElement(reader, "worksheet", Ns))
+ if (!reader.IsStartElement("worksheet", Ns))
return new GetMaxRowColumnIndexResult(false);
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
return new GetMaxRowColumnIndexResult(false);
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "sheetData", Ns))
+ if (reader.IsStartElement("sheetData", Ns))
{
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
continue;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "row", Ns))
+ if (reader.IsStartElement("row", Ns))
{
maxRowIndex++;
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
continue;
// Cells
var cellIndex = -1;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "c", Ns))
+ if (reader.IsStartElement("c", Ns))
{
cellIndex++;
maxColumnIndex = Math.Max(maxColumnIndex, cellIndex);
}
- if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
break;
}
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
}
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
@@ -1082,31 +1056,28 @@ internal static async Task TryGetMergeCellsAsync(ZipArchiveEntry sheetEntr
);
var mergeCells = new MergeCells();
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var sheetStream = await sheetEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableSheetStream = sheetStream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var sheetStream = sheetEntry.Open();
- await using var disposableSheetStream = sheetStream.ConfigureAwait(false);
#else
using var sheetStream = sheetEntry.Open();
#endif
using var reader = XmlReader.Create(sheetStream, xmlSettings);
- if (!XmlReaderHelper.IsStartElement(reader, "worksheet", Ns))
+ if (!reader.IsStartElement("worksheet", Ns))
return false;
while (await reader.ReadAsync().ConfigureAwait(false))
{
- if (!XmlReaderHelper.IsStartElement(reader, "mergeCells", Ns))
+ if (!reader.IsStartElement("mergeCells", Ns))
continue;
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
return false;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "mergeCell", Ns))
+ if (reader.IsStartElement("mergeCell", Ns))
{
var refAttr = reader.GetAttribute("ref");
var refs = refAttr.Split(':');
@@ -1130,9 +1101,9 @@ internal static async Task TryGetMergeCellsAsync(ZipArchiveEntry sheetEntr
}
}
- await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false);
+ await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false);
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
@@ -1162,12 +1133,9 @@ internal async Task ReadCommentsAsync(string? sheetName, Cance
List people = [];
if (Archive.GetEntry("xl/persons/person.xml") is { } persons)
{
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var personStream = await persons.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposablePersonStream = personStream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var personStream = persons.Open();
- await using var disposablePersonStream = personStream.ConfigureAwait(false);
#else
using var personStream = persons.Open();
#endif
@@ -1191,12 +1159,9 @@ internal async Task ReadCommentsAsync(string? sheetName, Cance
if (Archive.GetEntry($"xl/worksheets/_rels/{sheetFile}.rels") is not { } rel)
return new CommentResultSet(sheetName, [], []);
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var stream = await rel.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableStream = stream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var stream = rel.Open();
- await using var disposableStream = stream.ConfigureAwait(false);
#else
using var stream = rel.Open();
#endif
@@ -1222,12 +1187,9 @@ internal async Task ReadCommentsAsync(string? sheetName, Cance
HashSet refCells = [];
if (Archive.GetEntry($"xl/{threadedCommentsPath}") is { } threadEntry)
{
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var threadEntryStream = await threadEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableThreadEntryStream = threadEntryStream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var threadEntryStream = threadEntry.Open();
- await using var disposableThreadEntryStream = threadEntryStream.ConfigureAwait(false);
#else
using var threadEntryStream = threadEntry.Open();
#endif
@@ -1278,12 +1240,9 @@ internal async Task ReadCommentsAsync(string? sheetName, Cance
if (Archive.GetEntry($"xl/{notesPath}") is { } noteEntry)
{
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var noteEntryStream = await noteEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableNoteEntryStream = noteEntryStream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var noteEntryStream = noteEntry.Open();
- await using var disposableNoteEntryStream = noteEntryStream.ConfigureAwait(false);
#else
using var noteEntryStream = noteEntry.Open();
#endif
@@ -1353,34 +1312,31 @@ internal async IAsyncEnumerable QueryMappedAsync(
Async = true
};
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var sheetStream = await sheetEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableSheetStream = sheetStream.ConfigureAwait(false);
-#elif !NETSTANDARD2_0
- var sheetStream = sheetEntry.Open();
- await using var disposableSheetStream = sheetStream.ConfigureAwait(false);
#else
using var sheetStream = sheetEntry.Open();
#endif
using var reader = XmlReader.Create(sheetStream, xmlSettings);
- if (!XmlReaderHelper.IsStartElement(reader, "worksheet", Ns))
+ if (!reader.IsStartElement("worksheet", Ns))
yield break;
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
yield break;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "sheetData", Ns))
+ if (reader.IsStartElement("sheetData", Ns))
{
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
continue;
int rowIndex = -1;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "row", Ns))
+ if (reader.IsStartElement("row", Ns))
{
if (int.TryParse(reader.GetAttribute("r"), out int arValue))
rowIndex = arValue - 1; // The row attribute is 1-based
@@ -1393,13 +1349,13 @@ internal async IAsyncEnumerable QueryMappedAsync(
yield return mappedRow;
}
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
}
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
@@ -1414,7 +1370,7 @@ private async IAsyncEnumerable ReadMappedRowAsync(
MergeCells? mergeCells,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
- if (!await XmlReaderHelper.ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
{
// Empty row
yield return new MappedRow(rowIndex);
@@ -1426,7 +1382,7 @@ private async IAsyncEnumerable ReadMappedRowAsync(
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "c", Ns))
+ if (reader.IsStartElement("c", Ns))
{
var aS = reader.GetAttribute("s");
var aR = reader.GetAttribute("r");
@@ -1459,7 +1415,7 @@ private async IAsyncEnumerable ReadMappedRowAsync(
row.SetCell(columnIndex, cellValue);
}
- else if (!await XmlReaderHelper.SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
diff --git a/src/MiniExcel.OpenXml/OpenXmlWriter.DefaultOpenXml.cs b/src/MiniExcel.OpenXml/OpenXmlWriter.DefaultOpenXml.cs
index feecc08a..bd7bc3ac 100644
--- a/src/MiniExcel.OpenXml/OpenXmlWriter.DefaultOpenXml.cs
+++ b/src/MiniExcel.OpenXml/OpenXmlWriter.DefaultOpenXml.cs
@@ -9,7 +9,7 @@ internal partial class OpenXmlWriter
private readonly Dictionary _zipDictionary = [];
private Dictionary _cellXfIdMap = [];
- private IEnumerable> GetSheets()
+ private IEnumerable<(SheetDto, object?)> GetSheets()
{
var sheetId = 0;
if (_value is IDictionary dictionary)
@@ -20,7 +20,7 @@ internal partial class OpenXmlWriter
sheetId++;
var sheetInfos = GetSheetInfos(sheet.Key);
- yield return Tuple.Create(sheetInfos.ToDto(sheetId), sheet.Value);
+ yield return (sheetInfos.ToDto(sheetId), sheet.Value);
}
yield break;
@@ -32,7 +32,7 @@ internal partial class OpenXmlWriter
{
sheetId++;
var sheetInfos = GetSheetInfos(dt.TableName);
- yield return Tuple.Create(sheetInfos.ToDto(sheetId), (object?)dt);
+ yield return (sheetInfos.ToDto(sheetId), dt);
}
yield break;
@@ -40,12 +40,12 @@ internal partial class OpenXmlWriter
sheetId++;
var defaultSheetInfo = GetSheetInfos(_defaultSheetName);
- yield return Tuple.Create(defaultSheetInfo.ToDto(sheetId), _value);
+ yield return (defaultSheetInfo.ToDto(sheetId), _value);
}
- private ExcellSheetInfo GetSheetInfos(string sheetName)
+ private ExcelSheetInfo GetSheetInfos(string sheetName)
{
- var info = new ExcellSheetInfo
+ var info = new ExcelSheetInfo
{
ExcelSheetName = sheetName,
Key = sheetName,
@@ -153,20 +153,20 @@ private string GetPanes()
return sb.ToString();
}
- private Tuple GetCellValue(int rowIndex, int cellIndex, object value, MiniExcelColumnMapping? columnInfo, bool valueIsNull)
+ private (string, string?, string?) GetCellValue(int rowIndex, int cellIndex, object value, MiniExcelColumnMapping? columnInfo, bool valueIsNull)
{
if (valueIsNull)
- return Tuple.Create("2", "str", string.Empty);
+ return ("2", "str", string.Empty);
if (value is string str)
- return Tuple.Create("2", "str", XmlHelper.EncodeXml(str));
+ return ("2", "str", XmlHelper.EncodeXml(str));
var type = GetValueType(value, columnInfo);
if (columnInfo is { ExcelFormat: not null, ExcelFormatId: -1 } && value is IFormattable formattableValue)
{
var formattedStr = formattableValue.ToString(columnInfo.ExcelFormat, _configuration.Culture);
- return Tuple.Create("2", "str", XmlHelper.EncodeXml(formattedStr));
+ return ("2", "str", XmlHelper.EncodeXml(formattedStr));
}
if (type == typeof(DateTime))
@@ -188,7 +188,7 @@ private Tuple GetCellValue(int rowIndex, int cellIndex,
}
description ??= value.ToString();
- return Tuple.Create("2", "str", description);
+ return ("2", "str", description);
}
if (TypeHelper.IsNumericType(type))
@@ -197,26 +197,26 @@ private Tuple GetCellValue(int rowIndex, int cellIndex,
if (columnInfo?.ExcelFormat is null)
{
- var dataType = _configuration.Culture == CultureInfo.InvariantCulture ? "n" : "str";
- return Tuple.Create("2", dataType, cellValue);
+ var dataType = ReferenceEquals(_configuration.Culture, CultureInfo.InvariantCulture) ? "n" : "str";
+ return ("2", dataType, cellValue);
}
- return Tuple.Create(columnInfo.ExcelFormatId.ToString(), (string?)null, cellValue);
+ return (columnInfo.ExcelFormatId.ToString(), null, cellValue);
}
if (type == typeof(bool))
- return Tuple.Create("2", "b", (bool)value ? "1" : "0");
+ return ("2", "b", (bool)value ? "1" : "0");
if (type == typeof(byte[]) && _configuration.EnableConvertByteArray)
{
if (!_configuration.EnableWriteFilePath)
- return Tuple.Create("4", "str", "");
+ return ("4", "str", "");
var base64 = GetFileValue(rowIndex, cellIndex, value);
- return Tuple.Create("4", "str", XmlHelper.EncodeXml(base64));
+ return ("4", "str", XmlHelper.EncodeXml(base64));
}
- return Tuple.Create("2", "str", XmlHelper.EncodeXml(value.ToString()));
+ return ("2", "str", XmlHelper.EncodeXml(value.ToString()));
}
private static Type? GetValueType(object value, MiniExcelColumnMapping? columnInfo)
@@ -303,20 +303,20 @@ private string GetFileValue(int rowIndex, int cellIndex, object value)
return base64;
}
- private Tuple GetDateTimeValue(DateTime value, MiniExcelColumnMapping columnInfo)
+ private (string, string?, string) GetDateTimeValue(DateTime value, MiniExcelColumnMapping columnInfo)
{
string? cellValue;
if (!ReferenceEquals(_configuration.Culture, CultureInfo.InvariantCulture))
{
cellValue = value.ToString(_configuration.Culture);
- return Tuple.Create("2", (string?)"str", cellValue);
+ return ("2", (string?)"str", cellValue);
}
var oaDate = CorrectDateTimeValue(value);
cellValue = oaDate.ToString(CultureInfo.InvariantCulture);
var format = columnInfo?.ExcelFormat is not null ? columnInfo.ExcelFormatId.ToString() : "3";
- return Tuple.Create(format, (string?)null, cellValue);
+ return (format, null, cellValue);
}
private static double CorrectDateTimeValue(DateTime value)
@@ -409,6 +409,6 @@ private string GetContentTypesXml()
private string GetCellXfId(string styleIndex)
{
- return _cellXfIdMap.TryGetValue(styleIndex, out var cellXfId) ? cellXfId : styleIndex;
+ return _cellXfIdMap.GetValueOrDefault(styleIndex, styleIndex);
}
}
\ No newline at end of file
diff --git a/src/MiniExcel.OpenXml/OpenXmlWriter.cs b/src/MiniExcel.OpenXml/OpenXmlWriter.cs
index 78a705e1..19ff839f 100644
--- a/src/MiniExcel.OpenXml/OpenXmlWriter.cs
+++ b/src/MiniExcel.OpenXml/OpenXmlWriter.cs
@@ -11,9 +11,9 @@ internal partial class OpenXmlWriter : IMiniExcelWriter
{
private static readonly UTF8Encoding Utf8WithBom = new(true);
+ private readonly Stream _stream;
private readonly ZipArchive _archive;
private readonly OpenXmlConfiguration _configuration;
- private readonly Stream _stream;
private readonly List _sheets = [];
private readonly List _files = [];
@@ -47,12 +47,8 @@ internal static async ValueTask CreateAsync(Stream stream, object
// A. Why ZipArchiveMode.Update and not ZipArchiveMode.Create?
// R. ZipArchiveEntry does not support seeking when Mode is Create.
var archiveMode = conf.FastMode ? ZipArchiveMode.Update : ZipArchiveMode.Create;
-
-#if NET10_0_OR_GREATER
var archive = await ZipArchive.CreateAsync(stream, archiveMode, true, Utf8WithBom, cancellationToken).ConfigureAwait(false);
-#else
- var archive = new ZipArchive(stream, archiveMode, true, Utf8WithBom);
-#endif
+
return new OpenXmlWriter(stream, archive, value, sheetName, conf, printHeader);
}
@@ -61,7 +57,9 @@ public async Task SaveAsAsync(IProgress? progress = null, Cancellati
{
try
{
- await GenerateDefaultOpenXmlAsync(cancellationToken).ConfigureAwait(false);
+ await CreateZipEntryAsync(ExcelFileNames.Rels, ExcelContentTypes.Relationships, ExcelXml.DefaultRels, cancellationToken).ConfigureAwait(false);
+ await CreateZipEntryAsync(ExcelFileNames.SharedStrings, ExcelContentTypes.SharedStrings, ExcelXml.DefaultSharedString, cancellationToken).ConfigureAwait(false);
+ await GenerateStylesXmlAsync(cancellationToken).ConfigureAwait(false);
var sheets = GetSheets();
var rowsWritten = new List();
@@ -169,14 +167,6 @@ public async Task InsertAsync(bool overwriteSheet = false, IProgress?
}
}
- [CreateSyncVersion]
- private async Task GenerateDefaultOpenXmlAsync(CancellationToken cancellationToken)
- {
- await CreateZipEntryAsync(ExcelFileNames.Rels, ExcelContentTypes.Relationships, ExcelXml.DefaultRels, cancellationToken).ConfigureAwait(false);
- await CreateZipEntryAsync(ExcelFileNames.SharedStrings, ExcelContentTypes.SharedStrings, ExcelXml.DefaultSharedString, cancellationToken).ConfigureAwait(false);
- await GenerateStylesXmlAsync(cancellationToken).ConfigureAwait(false);
- }
-
[CreateSyncVersion]
private async Task CreateSheetXmlAsync(object? values, string sheetPath, IProgress? progress, CancellationToken cancellationToken)
{
@@ -184,11 +174,7 @@ private async Task CreateSheetXmlAsync(object? values, string sheetPath, IP
var rowsWritten = 0;
#if NET8_0_OR_GREATER
-#if NET10_0_OR_GREATER
var zipStream = await entry.OpenAsync(cancellationToken).ConfigureAwait(false);
-#else
- var zipStream = entry.Open();
-#endif
await using var disposableZipStream = zipStream.ConfigureAwait(false);
var writer = new MiniExcelStreamWriter(zipStream, Utf8WithBom, _configuration.BufferSize);
@@ -247,6 +233,7 @@ private async Task WriteValuesAsync(MiniExcelStreamWriter writer, object va
{
writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration);
}
+
try
{
var count = 0;
@@ -482,11 +469,7 @@ private async Task WriteCellAsync(MiniExcelStreamWriter writer, int rowIndex, in
return;
}
- var tuple = GetCellValue(rowIndex, cellIndex, value, columnInfo, valueIsNull);
-
- var styleIndex = tuple.Item1;
- var dataType = tuple.Item2;
- var cellValue = tuple.Item3;
+ var (styleIndex, dataType, cellValue) = GetCellValue(rowIndex, cellIndex, value, columnInfo, valueIsNull);
var columnType = columnInfo.ExcelColumnType;
/*Prefix and suffix blank space will lost after SaveAs #294*/
@@ -628,21 +611,14 @@ private async Task InsertContentTypesXmlAsync(CancellationToken cancellationToke
}
#if NET8_0_OR_GREATER
-#if NET10_0_OR_GREATER
var stream = await contentTypesZipEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
-#else
- var stream = contentTypesZipEntry.Open();
-#endif
await using var disposableStream = stream.ConfigureAwait(false);
-#else
- using var stream = contentTypesZipEntry.Open();
-#endif
-
-#if NETCOREAPP2_0_OR_GREATER
var doc = await XDocument.LoadAsync(stream, LoadOptions.None, cancellationToken).ConfigureAwait(false);
#else
+ using var stream = contentTypesZipEntry.Open();
var doc = XDocument.Load(stream);
#endif
+
var ns = doc.Root?.GetDefaultNamespace();
var typesElement = doc.Descendants(ns + "Types").Single();
@@ -681,12 +657,9 @@ private async Task CreateZipEntryAsync(string path, string? contentType, string
var entry = _archive.CreateEntry(path, CompressionLevel.Fastest);
#if NET8_0_OR_GREATER
-#if NET10_0_OR_GREATER
var zipStream = await entry.OpenAsync(cancellationToken).ConfigureAwait(false);
-#else
- var zipStream = entry.Open();
-#endif
await using var disposableZipStream = zipStream.ConfigureAwait(false);
+
var writer = new MiniExcelStreamWriter(zipStream, Utf8WithBom, _configuration.BufferSize);
await using var disposableWriter = writer.ConfigureAwait(false);
#else
@@ -707,16 +680,104 @@ private async Task CreateZipEntryAsync(string path, byte[] content, Cancellation
var entry = _archive.CreateEntry(path, CompressionLevel.Fastest);
#if NET8_0_OR_GREATER
-#if NET10_0_OR_GREATER
var zipStream = await entry.OpenAsync(cancellationToken).ConfigureAwait(false);
-#else
- var zipStream = entry.Open();
-#endif
- await using var disposableZipStream = zipStream.ConfigureAwait(false);
+ await using var disposableZipStream = zipStream.ConfigureAwait(false);
await zipStream.WriteAsync(content, cancellationToken).ConfigureAwait(false);
#else
using var zipStream = entry.Open();
await zipStream.WriteAsync(content, 0, content.Length, cancellationToken).ConfigureAwait(false);
#endif
}
+
+ [CreateSyncVersion]
+ /* Todo: this method is not very efficient, but workbook.xml is generally a very small file so at the moment it's not worth over-optimizing it.
+ Also, consider adding active sheet as one of the editable properties. */
+ internal async Task AlterWorksheetAsync(string sheetName, string? newSheetName, int? newSheetIndex, SheetState? newSheetState, CancellationToken cancellationToken = default)
+ {
+ if (newSheetName is null && newSheetIndex is null && newSheetState is null)
+ return;
+
+ var oldWorkbookEntry = _archive.GetEntry("xl/workbook.xml")!;
+
+ try
+ {
+ var xmlDoc = await LoadWorkbook().ConfigureAwait(false);
+
+ oldWorkbookEntry.Delete();
+ var newWorkbookEntry = _archive.CreateEntry("xl/workbook.xml", CompressionLevel.Fastest);
+#if NET8_0_OR_GREATER
+ var newZipStream = await newWorkbookEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
+ await using var newDisposableZipStream = newZipStream.ConfigureAwait(false);
+ var writer = XmlWriter.Create(newZipStream, new XmlWriterSettings
+ {
+#if !SYNC_ONLY
+ Async = true
+#endif
+ });
+ await using var disposableWriter = writer.ConfigureAwait(false);
+ await xmlDoc.WriteToAsync(writer, CancellationToken.None).ConfigureAwait(false);
+#else
+ using var newZipStream = newWorkbookEntry.Open();
+ using var writer = XmlWriter.Create(newZipStream, new XmlWriterSettings { Async = false });
+ xmlDoc.WriteTo(writer);
+#endif
+ }
+ finally
+ {
+#if NET10_0_OR_GREATER
+ await _archive.DisposeAsync().ConfigureAwait(false);
+#else
+ _archive.Dispose();
+#endif
+ }
+ return;
+
+ async Task LoadWorkbook()
+ {
+#if NET8_0_OR_GREATER
+ var zipStream = await oldWorkbookEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
+ await using var disposableZipStream = zipStream.ConfigureAwait(false);
+ var workbookDoc = await XDocument.LoadAsync(zipStream, LoadOptions.None, cancellationToken).ConfigureAwait(false);
+#else
+ using var zipStream = oldWorkbookEntry.Open();
+ var workbookDoc = XDocument.Load(zipStream);
+#endif
+ var sheetsContainer = workbookDoc.Root?.Element((XNamespace)Schemas.SpreadsheetmlXmlMain + "sheets")!;
+ var sheets = sheetsContainer.Elements().ToList();
+
+ if (sheets.Find(s => s.Attribute("name")?.Value.Equals(sheetName, StringComparison.OrdinalIgnoreCase) is true) is not { } sheet)
+ throw new InvalidDataException($"Sheet {sheetName} not found");
+
+ if (!string.IsNullOrEmpty(newSheetName))
+ {
+ if (newSheetName.Length > 31)
+ throw new ArgumentException($"The name \"{newSheetName}\" is too long, the maximum allowed length is 31 characters.");
+
+ sheet.SetAttributeValue("name", newSheetName);
+ }
+
+ if (newSheetIndex is not null)
+ {
+ var newIndex = Math.Clamp(newSheetIndex.Value, 0, sheets.Count - 1);
+ sheets.Remove(sheet);
+ sheets.Insert(newIndex, sheet);
+
+ sheetsContainer.RemoveAll();
+ sheetsContainer.Add(sheets);
+ }
+
+ if (newSheetState is not null)
+ {
+ sheet.SetAttributeValue("state", newSheetState switch
+ {
+ SheetState.Visible => "visible",
+ SheetState.Hidden => "hidden",
+ SheetState.VeryHidden => "veryHidden",
+ _ => "visible"
+ });
+ }
+
+ return workbookDoc;
+ }
+ }
}
diff --git a/src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs b/src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs
index 920330cd..ab47642f 100644
--- a/src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs
+++ b/src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs
@@ -136,15 +136,12 @@ public static async Task AddPictureAsync(Stream excelStream, CancellationToken c
var imagePath = $"xl/media/{imageName}";
var imageEntry = archive.CreateEntry(imagePath);
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var entryStream = await imageEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableStream = entryStream.ConfigureAwait(false);
-#else
- using var entryStream = imageEntry.Open();
-#endif
-#if NET5_0_OR_GREATER
await entryStream.WriteAsync(imageBytes.AsMemory(), cancellationToken).ConfigureAwait(false);
#else
+ using var entryStream = imageEntry.Open();
await entryStream.WriteAsync(imageBytes, 0, imageBytes.Length, cancellationToken).ConfigureAwait(false);
#endif
diff --git a/src/MiniExcel.OpenXml/Styles/Builder/SheetStyleBuildContext.cs b/src/MiniExcel.OpenXml/Styles/Builder/SheetStyleBuildContext.cs
index aebade58..7e7d5d12 100644
--- a/src/MiniExcel.OpenXml/Styles/Builder/SheetStyleBuildContext.cs
+++ b/src/MiniExcel.OpenXml/Styles/Builder/SheetStyleBuildContext.cs
@@ -59,12 +59,9 @@ public async Task InitializeAsync(SheetStyleElementInfos generateElementInfos, C
if (_oldStyleXmlZipEntry is not null)
{
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
var oldStyleXmlStream = await _oldStyleXmlZipEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using (_ = oldStyleXmlStream.ConfigureAwait(false))
-#elif NET8_0_OR_GREATER
- var oldStyleXmlStream = _oldStyleXmlZipEntry.Open();
- await using (_ = oldStyleXmlStream.ConfigureAwait(false))
#else
using (var oldStyleXmlStream = _oldStyleXmlZipEntry.Open())
#endif
@@ -73,7 +70,7 @@ public async Task InitializeAsync(SheetStyleElementInfos generateElementInfos, C
OldElementInfos = await ReadSheetStyleElementInfosAsync(reader, cancellationToken).ConfigureAwait(false);
}
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
_oldXmlReaderStream = await _oldStyleXmlZipEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
#else
_oldXmlReaderStream = _oldStyleXmlZipEntry.Open();
@@ -90,7 +87,7 @@ public async Task InitializeAsync(SheetStyleElementInfos generateElementInfos, C
_newStyleXmlZipEntry = _archive.CreateEntry(ExcelFileNames.Styles, CompressionLevel.Fastest);
}
-#if NET10_0_OR_GREATER
+#if NET8_0_OR_GREATER
_newXmlWriterStream = await _newStyleXmlZipEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
#else
_newXmlWriterStream = _newStyleXmlZipEntry.Open();
@@ -160,15 +157,10 @@ public async Task FinalizeAndUpdateZipDictionaryAsync(CancellationToken cancella
var finalStyleXmlZipEntry = _archive.CreateEntry(ExcelFileNames.Styles, CompressionLevel.Fastest);
#if NET8_0_OR_GREATER
-#if NET10_0_OR_GREATER
var tempStream = await _newStyleXmlZipEntry!.OpenAsync(cancellationToken).ConfigureAwait(false);
var newStream = await finalStyleXmlZipEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
-#else
- var tempStream = _newStyleXmlZipEntry!.Open();
- var newStream = finalStyleXmlZipEntry.Open();
-#endif
- await using (var disposableTempStream = tempStream.ConfigureAwait(false))
- await using (var disposableNewStream = newStream.ConfigureAwait(false))
+ await using (_ = tempStream.ConfigureAwait(false))
+ await using (_= newStream.ConfigureAwait(false))
#else
using (var tempStream = _newStyleXmlZipEntry!.Open())
using (var newStream = finalStyleXmlZipEntry.Open())
@@ -178,7 +170,7 @@ public async Task FinalizeAndUpdateZipDictionaryAsync(CancellationToken cancella
}
_zipDictionary[ExcelFileNames.Styles] = new ZipPackageInfo(finalStyleXmlZipEntry, ExcelContentTypes.Styles);
- _newStyleXmlZipEntry.Delete();
+ _newStyleXmlZipEntry?.Delete();
_newStyleXmlZipEntry = null;
}
diff --git a/src/MiniExcel.OpenXml/Styles/OpenXmlStyles.cs b/src/MiniExcel.OpenXml/Styles/OpenXmlStyles.cs
index dd55dc1b..20f51a19 100644
--- a/src/MiniExcel.OpenXml/Styles/OpenXmlStyles.cs
+++ b/src/MiniExcel.OpenXml/Styles/OpenXmlStyles.cs
@@ -17,14 +17,14 @@ public OpenXmlStyles(OpenXmlZip zip)
if (reader is null)
throw new InvalidDataException("The OpenXml styles could not be found, the file might be malformed.");
- if (!XmlReaderHelper.IsStartElement(reader, "styleSheet", Ns))
+ if (!reader.IsStartElement("styleSheet", Ns))
return;
- if (!XmlReaderHelper.ReadFirstContent(reader))
+ if (!reader.ReadFirstContent())
return;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "cellXfs", Ns))
+ if (reader.IsStartElement("cellXfs", Ns))
{
if (!XmlReaderHelper.ReadFirstContent(reader))
continue;
@@ -32,7 +32,7 @@ public OpenXmlStyles(OpenXmlZip zip)
var index = 0;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "xf", Ns))
+ if (reader.IsStartElement("xf", Ns))
{
int.TryParse(reader.GetAttribute("xfId"), out var xfId);
int.TryParse(reader.GetAttribute("numFmtId"), out var numFmtId);
@@ -40,19 +40,19 @@ public OpenXmlStyles(OpenXmlZip zip)
reader.Skip();
index++;
}
- else if (!XmlReaderHelper.SkipContent(reader))
+ else if (!reader.SkipContent())
break;
}
}
- else if (XmlReaderHelper.IsStartElement(reader, "cellStyleXfs", Ns))
+ else if (reader.IsStartElement("cellStyleXfs", Ns))
{
- if (!XmlReaderHelper.ReadFirstContent(reader))
+ if (!reader.ReadFirstContent())
continue;
var index = 0;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "xf", Ns))
+ if (reader.IsStartElement("xf", Ns))
{
int.TryParse(reader.GetAttribute("xfId"), out var xfId);
int.TryParse(reader.GetAttribute("numFmtId"), out var numFmtId);
@@ -61,20 +61,20 @@ public OpenXmlStyles(OpenXmlZip zip)
reader.Skip();
index++;
}
- else if (!XmlReaderHelper.SkipContent(reader))
+ else if (!reader.SkipContent())
{
break;
}
}
}
- else if (XmlReaderHelper.IsStartElement(reader, "numFmts", Ns))
+ else if (reader.IsStartElement("numFmts", Ns))
{
- if (!XmlReaderHelper.ReadFirstContent(reader))
+ if (!reader.ReadFirstContent())
continue;
while (!reader.EOF)
{
- if (XmlReaderHelper.IsStartElement(reader, "numFmt", Ns))
+ if (reader.IsStartElement("numFmt", Ns))
{
_ = int.TryParse(reader.GetAttribute("numFmtId"), out var numFmtId);
var formatCode = reader.GetAttribute("formatCode");
@@ -94,13 +94,13 @@ public OpenXmlStyles(OpenXmlZip zip)
#endif
reader.Skip();
}
- else if (!XmlReaderHelper.SkipContent(reader))
+ else if (!reader.SkipContent())
{
break;
}
}
}
- else if (!XmlReaderHelper.SkipContent(reader))
+ else if (!reader.SkipContent())
{
break;
}
diff --git a/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.Impl.cs b/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.Impl.cs
index 714c6394..859654d0 100644
--- a/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.Impl.cs
+++ b/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.Impl.cs
@@ -87,11 +87,7 @@ private async Task GenerateSheetByUpdateModeAsync(ZipArchiveEntry sheetZipEntry,
private async Task GenerateSheetByCreateModeAsync(ZipArchiveEntry templateSheetZipEntry, Stream outputZipSheetEntryStream, IDictionary inputMaps, IDictionary sharedStrings, bool mergeCells = false, CancellationToken cancellationToken = default)
{
#if NET8_0_OR_GREATER
-#if NET10_0_OR_GREATER
var newTemplateStream = await templateSheetZipEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
-#else
- var newTemplateStream = templateSheetZipEntry.Open();
-#endif
await using var disposableNewTemplateStream = newTemplateStream.ConfigureAwait(false);
var doc = await XDocument.LoadAsync(newTemplateStream, LoadOptions.None, cancellationToken).ConfigureAwait(false);
#else
diff --git a/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.MergeCells.cs b/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.MergeCells.cs
index b5c7aa11..5d84f3b4 100644
--- a/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.MergeCells.cs
+++ b/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.MergeCells.cs
@@ -59,14 +59,10 @@ await stream.CopyToAsync(_outputFileStream
var entry = archive.ZipFile.CreateEntry(sheet.FullName);
#if NET8_0_OR_GREATER
-#if NET10_0_OR_GREATER
var sheetStream = await sheet.OpenAsync(cancellationToken).ConfigureAwait(false);
- var zipStream = await entry.OpenAsync(cancellationToken).ConfigureAwait(false);
-#else
- var sheetStream = sheet.Open();
- var zipStream = entry.Open();
-#endif
await using var disposableSheetStream = sheetStream.ConfigureAwait(false);
+
+ var zipStream = await entry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableZipStream = zipStream.ConfigureAwait(false);
#else
using var sheetStream = sheet.Open();
diff --git a/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.cs b/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.cs
index 56c33cf4..eb52eaa3 100644
--- a/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.cs
+++ b/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.cs
@@ -71,7 +71,7 @@ public async Task SaveAsByTemplateAsync(Stream templateStream, object value, Can
templateStream.Position = 0;
#if NET10_0_OR_GREATER
var originalArchive = await ZipArchive.CreateAsync(templateStream, ZipArchiveMode.Read, false, null, cancellationToken).ConfigureAwait(false);
- await using var disposableArchive = originalArchive.ConfigureAwait(false);
+ await using var disposableArchive = originalArchive.ConfigureAwait(false);
#else
using var originalArchive = new ZipArchive(templateStream, ZipArchiveMode.Read);
#endif
@@ -88,14 +88,10 @@ public async Task SaveAsByTemplateAsync(Stream templateStream, object value, Can
// Copy the content of the original entry to the new entry
#if NET8_0_OR_GREATER
-#if NET10_0_OR_GREATER
var originalEntryStream = await entry.OpenAsync(cancellationToken).ConfigureAwait(false);
- var newEntryStream = await newEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
-#else
- var originalEntryStream = entry.Open();
- var newEntryStream = newEntry.Open();
-#endif
await using var disposableEntryStream = originalEntryStream.ConfigureAwait(false);
+
+ var newEntryStream = await newEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableNewEntryStream = newEntryStream.ConfigureAwait(false);
#else
using var originalEntryStream = entry.Open();
@@ -133,11 +129,7 @@ await originalEntryStream.CopyToAsync(newEntryStream
var outputZipEntry = outputFileArchive.ZipFile.CreateEntry(templateFullName);
#if NET8_0_OR_GREATER
-#if NET10_0_OR_GREATER
var outputZipSheetEntryStream = await outputZipEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
-#else
- var outputZipSheetEntryStream = outputZipEntry.Open();
-#endif
await using var disposableSheetEntryStream = outputZipSheetEntryStream.ConfigureAwait(false);
#else
using var outputZipSheetEntryStream = outputZipEntry.Open();
@@ -160,11 +152,7 @@ await originalEntryStream.CopyToAsync(newEntryStream
var calcChainEntry = outputFileArchive.ZipFile.CreateEntry(calcChainPathName);
#if NET8_0_OR_GREATER
-#if NET10_0_OR_GREATER
var calcChainStream = await calcChainEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
-#else
- var calcChainStream = calcChainEntry.Open();
-#endif
await using var disposableChainEntryStream = calcChainStream.ConfigureAwait(false);
#else
using var calcChainStream = calcChainEntry.Open();
@@ -181,14 +169,10 @@ await originalEntryStream.CopyToAsync(newEntryStream
// Copy the content of the original entry to the new entry
#if NET8_0_OR_GREATER
-#if NET10_0_OR_GREATER
var originalEntryStream = await entry.OpenAsync(cancellationToken).ConfigureAwait(false);
- var newEntryStream = await newEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
-#else
- var originalEntryStream = entry.Open();
- var newEntryStream = newEntry.Open();
-#endif
await using var disposableEntryStream = originalEntryStream.ConfigureAwait(false);
+
+ var newEntryStream = await newEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableNewEntryStream = newEntryStream.ConfigureAwait(false);
#else
using var originalEntryStream = entry.Open();
diff --git a/src/MiniExcel.OpenXml/Utils/MiniExcelPropertyHelper.cs b/src/MiniExcel.OpenXml/Utils/MiniExcelPropertyHelper.cs
index b9766a72..58f73568 100644
--- a/src/MiniExcel.OpenXml/Utils/MiniExcelPropertyHelper.cs
+++ b/src/MiniExcel.OpenXml/Utils/MiniExcelPropertyHelper.cs
@@ -4,10 +4,10 @@ namespace MiniExcelLib.OpenXml.Utils;
internal static class MiniExcelPropertyHelper
{
- internal static ExcellSheetInfo GetExcellSheetInfo(Type type, MiniExcelBaseConfiguration configuration)
+ internal static ExcelSheetInfo GetExcelSheetInfo(Type type, MiniExcelBaseConfiguration configuration)
{
// default options
- var sheetInfo = new ExcellSheetInfo
+ var sheetInfo = new ExcelSheetInfo
{
Key = type.Name,
ExcelSheetName = null, // will be generated automatically as Sheet
@@ -15,7 +15,7 @@ internal static ExcellSheetInfo GetExcellSheetInfo(Type type, MiniExcelBaseConfi
};
// options from ExcelSheetAttribute
- if (type.GetCustomAttribute(typeof(MiniExcelSheetAttribute)) is MiniExcelSheetAttribute excelSheetAttr)
+ if (type.GetCustomAttribute() is { } excelSheetAttr)
{
sheetInfo.ExcelSheetName = excelSheetAttr.Name ?? type.Name;
sheetInfo.ExcelSheetState = excelSheetAttr.State;
diff --git a/src/MiniExcel.OpenXml/Utils/XmlReaderHelper.cs b/src/MiniExcel.OpenXml/Utils/XmlReaderHelper.cs
index 6b8f8c41..0373d302 100644
--- a/src/MiniExcel.OpenXml/Utils/XmlReaderHelper.cs
+++ b/src/MiniExcel.OpenXml/Utils/XmlReaderHelper.cs
@@ -5,41 +5,10 @@ namespace MiniExcelLib.OpenXml.Utils;
internal static partial class XmlReaderHelper
{
private static readonly string[] Ns = [Schemas.SpreadsheetmlXmlMain, Schemas.SpreadsheetmlXmlStrictNs];
-
- ///
- /// Pass <?xml> and <worksheet>
- ///
- [CreateSyncVersion]
- public static async Task PassXmlDeclarationAndWorksheetAsync(this XmlReader reader, CancellationToken cancellationToken = default)
- {
- await reader.MoveToContentAsync()
-#if NET6_0_OR_GREATER
- .WaitAsync(cancellationToken)
-#endif
- .ConfigureAwait(false);
- await reader.ReadAsync()
-#if NET6_0_OR_GREATER
- .WaitAsync(cancellationToken)
-#endif
- .ConfigureAwait(false);
- }
-
- ///
- /// e.g skip row 1 to row 2
- ///
- [CreateSyncVersion]
- public static async Task SkipToNextSameLevelDomAsync(XmlReader reader, CancellationToken cancellationToken = default)
- {
- while (!reader.EOF)
- {
- if (!await SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
- break;
- }
- }
- //Method from ExcelDataReader @MIT License
+ // Copied and modified from ExcelDataReader - @MIT License
[CreateSyncVersion]
- public static async Task ReadFirstContentAsync(XmlReader reader, CancellationToken cancellationToken = default)
+ public static async Task ReadFirstContentAsync(this XmlReader reader, CancellationToken cancellationToken = default)
{
if (reader.IsEmptyElement)
{
@@ -56,6 +25,7 @@ await reader.MoveToContentAsync()
.WaitAsync(cancellationToken)
#endif
.ConfigureAwait(false);
+
await reader.ReadAsync()
#if NET6_0_OR_GREATER
.WaitAsync(cancellationToken)
@@ -64,9 +34,9 @@ await reader.ReadAsync()
return true;
}
- //Method from ExcelDataReader @MIT License
+ // Copied and modified from ExcelDataReader - @MIT License
[CreateSyncVersion]
- public static async Task SkipContentAsync(XmlReader reader, CancellationToken cancellationToken = default)
+ public static async Task SkipContentAsync(this XmlReader reader, CancellationToken cancellationToken = default)
{
if (reader.NodeType == XmlNodeType.EndElement)
{
@@ -86,77 +56,82 @@ await reader.SkipAsync()
return true;
}
- public static bool IsStartElement(XmlReader reader, string name, params string[] nss)
+ ///
+ /// e.g skip row 1 to row 2
+ ///
+ [CreateSyncVersion]
+ public static async Task SkipToNextSiblingAsync(this XmlReader reader, CancellationToken cancellationToken = default)
+ {
+ while (!reader.EOF)
+ {
+ if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
+ break;
+ }
+ }
+
+ public static bool IsStartElement(this XmlReader reader, string name, params string[] nss)
{
return nss.Any(s => reader.IsStartElement(name, s));
}
- public static string? GetAttribute(XmlReader reader, string name, params string[] nss)
+ public static string? GetAttribute(this XmlReader reader, string name, params string[] nss)
{
return nss
.Select(ns => reader.GetAttribute(name, ns))
.FirstOrDefault(at => at is not null);
}
+ // Copied and modified from ExcelDataReader - @MIT License
[CreateSyncVersion]
- public static async IAsyncEnumerable GetSharedStringsAsync(Stream stream, [EnumeratorCancellation]CancellationToken cancellationToken = default, params string[] nss)
+ public static async Task ReadStringItemAsync(this XmlReader reader, CancellationToken cancellationToken = default)
{
- var xmlSettings = GetXmlReaderSettings(
-#if SYNC_ONLY
- false
-#else
- true
-#endif
- );
-
- using var reader = XmlReader.Create(stream, xmlSettings);
- if (!IsStartElement(reader, "sst", nss))
- yield break;
-
- if (!await ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
- yield break;
+ var result = new StringBuilder();
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
+ return string.Empty;
while (!reader.EOF)
{
- if (IsStartElement(reader, "si", nss))
+ if (reader.IsStartElement("t", Ns))
{
- var value = await ReadStringItemAsync(reader, cancellationToken).ConfigureAwait(false);
- yield return value;
+ // There are multiple in a . Concatenate within an .
+ result.Append(await reader.ReadElementContentAsStringAsync()
+#if NET6_0_OR_GREATER
+ .WaitAsync(cancellationToken)
+#endif
+ .ConfigureAwait(false));
}
- else if (!await SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (reader.IsStartElement("r", Ns))
+ {
+ result.Append(await reader.ReadRichTextRunAsync(cancellationToken).ConfigureAwait(false));
+ }
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
}
+
+ return result.ToString();
}
-
- ///
- /// Copied and modified from ExcelDataReader - @MIT License
- ///
+ // Copied and modified from ExcelDataReader - @MIT License
[CreateSyncVersion]
- public static async Task ReadStringItemAsync(XmlReader reader, CancellationToken cancellationToken = default)
+ private static async Task ReadRichTextRunAsync(this XmlReader reader, CancellationToken cancellationToken = default)
{
var result = new StringBuilder();
- if (!await ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
return string.Empty;
while (!reader.EOF)
{
- if (IsStartElement(reader, "t", Ns))
+ if (reader.IsStartElement("t", Ns))
{
- // There are multiple in a . Concatenate within an .
result.Append(await reader.ReadElementContentAsStringAsync()
#if NET6_0_OR_GREATER
- .WaitAsync(cancellationToken)
+ .WaitAsync(cancellationToken)
#endif
.ConfigureAwait(false));
}
- else if (IsStartElement(reader, "r", Ns))
- {
- result.Append(await ReadRichTextRunAsync(reader, cancellationToken).ConfigureAwait(false));
- }
- else if (!await SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
@@ -165,40 +140,43 @@ public static async Task ReadStringItemAsync(XmlReader reader, Cancellat
return result.ToString();
}
- ///
- /// Copied and modified from ExcelDataReader - @MIT License
- ///
[CreateSyncVersion]
- private static async Task ReadRichTextRunAsync(XmlReader reader, CancellationToken cancellationToken = default)
+ public static async IAsyncEnumerable GetSharedStringsAsync(Stream stream, [EnumeratorCancellation]CancellationToken cancellationToken = default, params string[] nss)
{
- var result = new StringBuilder();
- if (!await ReadFirstContentAsync(reader, cancellationToken).ConfigureAwait(false))
- return string.Empty;
+ var xmlSettings = GetXmlReaderSettings(
+#if SYNC_ONLY
+ false
+#else
+ true
+#endif
+ );
+
+ using var reader = XmlReader.Create(stream, xmlSettings);
+ if (!reader.IsStartElement("sst", nss))
+ yield break;
+
+ if (!await reader.ReadFirstContentAsync(cancellationToken).ConfigureAwait(false))
+ yield break;
while (!reader.EOF)
{
- if (IsStartElement(reader, "t", Ns))
+ if (reader.IsStartElement("si", nss))
{
- result.Append(await reader.ReadElementContentAsStringAsync()
-#if NET6_0_OR_GREATER
- .WaitAsync(cancellationToken)
-#endif
- .ConfigureAwait(false));
+ var value = await reader.ReadStringItemAsync(cancellationToken).ConfigureAwait(false);
+ yield return value;
}
- else if (!await SkipContentAsync(reader, cancellationToken).ConfigureAwait(false))
+ else if (!await reader.SkipContentAsync(cancellationToken).ConfigureAwait(false))
{
break;
}
}
-
- return result.ToString();
}
-
+
internal static XmlReaderSettings GetXmlReaderSettings(bool async) => new()
{
IgnoreComments = true,
IgnoreWhitespace = true,
XmlResolver = null,
- Async = async,
+ Async = async
};
}
diff --git a/src/MiniExcel.OpenXml/Zip/OpenXmlZip.cs b/src/MiniExcel.OpenXml/Zip/OpenXmlZip.cs
index 2e1a68c4..1c13805a 100644
--- a/src/MiniExcel.OpenXml/Zip/OpenXmlZip.cs
+++ b/src/MiniExcel.OpenXml/Zip/OpenXmlZip.cs
@@ -2,7 +2,7 @@
namespace MiniExcelLib.OpenXml.Zip;
-/// Copy & modified by ExcelDataReader ZipWorker @MIT License
+/// Copied & modified from ExcelDataReader ZipWorker @MIT License
internal sealed partial class OpenXmlZip : IDisposable, IAsyncDisposable
{
private static readonly XmlReaderSettings XmlSettings = new()
@@ -30,11 +30,7 @@ private OpenXmlZip(ZipArchive zipArchive, Dictionary e
internal static async Task CreateAsync(Stream fileStream, ZipArchiveMode mode = ZipArchiveMode.Read, bool leaveOpen = false, Encoding? entryNameEncoding = null, bool isUpdateMode = true, CancellationToken cancellationToken = default)
{
entryNameEncoding ??= Encoding.UTF8;
-#if NET10_0_OR_GREATER
var zipFile = await ZipArchive.CreateAsync(fileStream, mode, leaveOpen, entryNameEncoding, cancellationToken).ConfigureAwait(false);
-#else
- var zipFile = new ZipArchive(fileStream, mode, leaveOpen, entryNameEncoding);
-#endif
if (!isUpdateMode)
return new OpenXmlZip(zipFile, []);
diff --git a/tests/MiniExcel.OpenXml.Tests/AlterSheets/MiniExcelAlterSheetsTests.cs b/tests/MiniExcel.OpenXml.Tests/AlterSheets/MiniExcelAlterSheetsTests.cs
new file mode 100644
index 00000000..e76a37f9
--- /dev/null
+++ b/tests/MiniExcel.OpenXml.Tests/AlterSheets/MiniExcelAlterSheetsTests.cs
@@ -0,0 +1,120 @@
+using MiniExcelLib.OpenXml.Models;
+using static MiniExcelLib.OpenXml.Tests.Utils.SheetHelper;
+
+namespace MiniExcelLib.OpenXml.Tests.AlterSheets;
+
+public class MiniExcelAlterSheetTests
+{
+ private readonly OpenXmlExporter _excelExporter = MiniExcel.Exporters.GetOpenXmlExporter();
+
+ [Fact]
+ public void AlterSheet_WhenNewNameProvided_RenamesWorksheet()
+ {
+ // Arrange
+ const string originalName = "Sheet1";
+ const string newName = "RenamedSheet";
+ using var stream = CreateTestWorkbookStream();
+
+ // Act
+ _excelExporter.AlterSheet(stream, originalName, newSheetName: newName);
+
+ // Assert
+ stream.Position = 0;
+ using var package = new ExcelPackage(stream);
+
+ Assert.Null(package.Workbook.Worksheets[originalName]);
+ Assert.NotNull(package.Workbook.Worksheets[newName]);
+ }
+
+ [Fact]
+ public void AlterSheet_WhenNewIndexProvided_MovesWorksheet()
+ {
+ // Arrange
+ const string targetSheet = "Sheet1";
+ const int newIndex = 2;
+ using var stream = CreateTestWorkbookStream();
+
+ // Act
+ _excelExporter.AlterSheet(stream, targetSheet, newSheetIndex: newIndex);
+
+ // Assert
+ stream.Position = 0;
+ using var package = new ExcelPackage(stream);
+
+ // Assert that the sheet at the new index is indeed our target sheet
+ Assert.Equal(targetSheet, package.Workbook.Worksheets[newIndex].Name);
+ }
+
+ [Fact]
+ public void AlterSheet_WhenNewStateProvided_ChangesVisibility()
+ {
+ // Arrange
+ const string targetSheet = "Sheet2";
+ using var stream = CreateTestWorkbookStream();
+
+ // Act
+ _excelExporter.AlterSheet(stream, targetSheet, newSheetState: SheetState.Hidden);
+
+ // Assert
+ stream.Position = 0;
+ using var package = new ExcelPackage(stream);
+
+ var sheet = package.Workbook.Worksheets[targetSheet];
+ Assert.Equal(eWorkSheetHidden.Hidden, sheet.Hidden);
+ }
+
+ [Fact]
+ public void AlterSheet_WhenAllPropertiesProvided_UpdatesAllSuccessfully()
+ {
+ // Arrange
+ const string originalName = "Sheet3";
+ const string newName = "SecretData";
+ const int newIndex = 0;
+ const SheetState newState = SheetState.VeryHidden;
+ using var stream = CreateTestWorkbookStream();
+
+ // Act
+ _excelExporter.AlterSheet(
+ stream,
+ originalName,
+ newSheetName: newName,
+ newSheetIndex: newIndex,
+ newSheetState: newState);
+
+ // Assert
+ stream.Position = 0;
+ using var package = new ExcelPackage(stream);
+
+ // 1. Check Name
+ Assert.Null(package.Workbook.Worksheets[originalName]);
+ var modifiedSheet = package.Workbook.Worksheets[newName];
+ Assert.NotNull(modifiedSheet);
+
+ // 2. Check Index (Should now be the first sheet)
+ Assert.Equal(newName, package.Workbook.Worksheets[newIndex].Name);
+
+ // 3. Check State
+ Assert.Equal(eWorkSheetHidden.VeryHidden, modifiedSheet.Hidden);
+ }
+
+ [Fact]
+ public void AlterSheet_WhenNoOptionalParametersProvided_LeavesSheetUnchanged()
+ {
+ // Arrange
+ const string targetSheet = "Sheet1";
+ using var stream = CreateTestWorkbookStream();
+
+ // Act
+ _excelExporter.AlterSheet(stream, targetSheet);
+
+ // Assert
+ stream.Position = 0;
+ using var package = new ExcelPackage(stream);
+ var sheet = package.Workbook.Worksheets[targetSheet];
+
+ // Ensure defaults remain intact
+ Assert.NotNull(sheet);
+ Assert.Equal("Sheet1", package.Workbook.Worksheets[0].Name);
+ Assert.Equal(eWorkSheetHidden.Visible, sheet.Hidden);
+ }
+}
\ No newline at end of file
diff --git a/tests/MiniExcel.OpenXml.Tests/AlterSheets/MiniExcelAlterSheetsTestsAsync.cs b/tests/MiniExcel.OpenXml.Tests/AlterSheets/MiniExcelAlterSheetsTestsAsync.cs
new file mode 100644
index 00000000..091b5199
--- /dev/null
+++ b/tests/MiniExcel.OpenXml.Tests/AlterSheets/MiniExcelAlterSheetsTestsAsync.cs
@@ -0,0 +1,120 @@
+using MiniExcelLib.OpenXml.Models;
+using static MiniExcelLib.OpenXml.Tests.Utils.SheetHelper;
+
+namespace MiniExcelLib.OpenXml.Tests.AlterSheets;
+
+public class MiniExcelAlterSheetsTestAsync
+{
+ private readonly OpenXmlExporter _excelExporter = MiniExcel.Exporters.GetOpenXmlExporter();
+
+ [Fact]
+ public async Task AlterSheetAsync_WhenNewNameProvided_RenamesWorksheet()
+ {
+ // Arrange
+ const string originalName = "Sheet1";
+ const string newName = "RenamedSheet";
+ await using var stream = CreateTestWorkbookStream();
+
+ // Act
+ await _excelExporter.AlterSheetAsync(stream, originalName, newSheetName: newName);
+
+ // Assert
+ stream.Position = 0; // Reset to read the saved results
+ using var package = new ExcelPackage(stream);
+
+ Assert.Null(package.Workbook.Worksheets[originalName]);
+ Assert.NotNull(package.Workbook.Worksheets[newName]);
+ }
+
+ [Fact]
+ public async Task AlterSheetAsync_WhenNewIndexProvided_MovesWorksheet()
+ {
+ // Arrange
+ const string targetSheet = "Sheet1";
+ const int newIndex = 2;
+ await using var stream = CreateTestWorkbookStream();
+
+ // Act
+ await _excelExporter.AlterSheetAsync(stream, targetSheet, newSheetIndex: newIndex);
+
+ // Assert
+ stream.Position = 0;
+ using var package = new ExcelPackage(stream);
+
+ // Assert that the sheet at the new index is indeed our target sheet
+ Assert.Equal(targetSheet, package.Workbook.Worksheets[newIndex].Name);
+ }
+
+ [Fact]
+ public async Task AlterSheetAsync_WhenNewStateProvided_ChangesVisibility()
+ {
+ // Arrange
+ const string targetSheet = "Sheet2";
+ await using var stream = CreateTestWorkbookStream();
+
+ // Act
+ await _excelExporter.AlterSheetAsync(stream, targetSheet, newSheetState: SheetState.Hidden);
+
+ // Assert
+ stream.Position = 0;
+ using var package = new ExcelPackage(stream);
+
+ var sheet = package.Workbook.Worksheets[targetSheet];
+ Assert.Equal(eWorkSheetHidden.Hidden, sheet.Hidden);
+ }
+
+ [Fact]
+ public async Task AlterSheetAsync_WhenAllPropertiesProvided_UpdatesAllSuccessfully()
+ {
+ // Arrange
+ const string originalName = "Sheet3";
+ const string newName = "SecretData";
+ const int newIndex = 0;
+ const SheetState newState = SheetState.VeryHidden;
+ await using var stream = CreateTestWorkbookStream();
+
+ // Act
+ await _excelExporter.AlterSheetAsync(
+ stream,
+ originalName,
+ newSheetName: newName,
+ newSheetIndex: newIndex,
+ newSheetState: newState);
+
+ // Assert
+ stream.Position = 0;
+ using var package = new ExcelPackage(stream);
+
+ // 1. Check Name
+ Assert.Null(package.Workbook.Worksheets[originalName]);
+ var modifiedSheet = package.Workbook.Worksheets[newName];
+ Assert.NotNull(modifiedSheet);
+
+ // 2. Check Index (Should now be the first sheet)
+ Assert.Equal(newName, package.Workbook.Worksheets[newIndex].Name);
+
+ // 3. Check State
+ Assert.Equal(eWorkSheetHidden.VeryHidden, modifiedSheet.Hidden);
+ }
+
+ [Fact]
+ public async Task AlterSheetAsync_WhenNoOptionalParametersProvided_LeavesSheetUnchanged()
+ {
+ // Arrange
+ const string targetSheet = "Sheet1";
+ await using var stream = CreateTestWorkbookStream();
+
+ // Act
+ await _excelExporter.AlterSheetAsync(stream, targetSheet);
+
+ // Assert
+ stream.Position = 0;
+ using var package = new ExcelPackage(stream);
+ var sheet = package.Workbook.Worksheets[targetSheet];
+
+ // Ensure defaults remain intact
+ Assert.NotNull(sheet);
+ Assert.Equal("Sheet1", package.Workbook.Worksheets[0].Name);
+ Assert.Equal(eWorkSheetHidden.Visible, sheet.Hidden);
+ }
+}
\ No newline at end of file
diff --git a/tests/MiniExcel.OpenXml.Tests/Utils/SheetHelper.cs b/tests/MiniExcel.OpenXml.Tests/Utils/SheetHelper.cs
index eb89374b..4257f171 100644
--- a/tests/MiniExcel.OpenXml.Tests/Utils/SheetHelper.cs
+++ b/tests/MiniExcel.OpenXml.Tests/Utils/SheetHelper.cs
@@ -119,4 +119,18 @@ internal static Dictionary GetFirstSheetMergedCells(string path)
return mergeCellsDict;
}
+
+ internal static MemoryStream CreateTestWorkbookStream()
+ {
+ var stream = new MemoryStream();
+
+ using var package = new ExcelPackage(stream);
+ package.Workbook.Worksheets.Add("Sheet1");
+ package.Workbook.Worksheets.Add("Sheet2");
+ package.Workbook.Worksheets.Add("Sheet3");
+ package.Save();
+
+ stream.Position = 0;
+ return stream;
+ }
}
\ No newline at end of file