diff --git a/benchmarks/MiniExcel.Benchmarks/MiniExcel.Benchmarks.csproj b/benchmarks/MiniExcel.Benchmarks/MiniExcel.Benchmarks.csproj
index 1e68a8bd..c4be3c22 100644
--- a/benchmarks/MiniExcel.Benchmarks/MiniExcel.Benchmarks.csproj
+++ b/benchmarks/MiniExcel.Benchmarks/MiniExcel.Benchmarks.csproj
@@ -8,6 +8,7 @@
latest
MiniExcelLib.Benchmarks
$(NoWarn);CA2000;CA2007
+ true
diff --git a/src/MiniExcel.Core/Helpers/FileHelper.cs b/src/MiniExcel.Core/Helpers/FileHelper.cs
index dbd1180c..16b95b04 100644
--- a/src/MiniExcel.Core/Helpers/FileHelper.cs
+++ b/src/MiniExcel.Core/Helpers/FileHelper.cs
@@ -2,5 +2,6 @@
public static class FileHelper
{
- public static FileStream OpenSharedRead(string path) => File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
-}
\ No newline at end of file
+ public static FileStream OpenSharedRead(string path)
+ => File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+}
diff --git a/src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs b/src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs
index 0e658898..8c0a8604 100644
--- a/src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs
+++ b/src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs
@@ -20,7 +20,8 @@ public static async Task AddPictureAsync(Stream excelStream, CancellationToken c
var excelArchive = await OpenXmlZip.CreateAsync(excelStream, cancellationToken: cancellationToken).ConfigureAwait(false);
await using var disposableExcelArchive = excelArchive.ConfigureAwait(false);
- using var reader = await OpenXmlReader.CreateAsync(excelStream, null, false, cancellationToken).ConfigureAwait(false);
+ var reader = await OpenXmlReader.CreateAsync(excelStream, null, false, cancellationToken).ConfigureAwait(false);
+ await using var dispoableReader = reader.ConfigureAwait(false);
#if NET10_0_OR_GREATER
var archive = await ZipArchive.CreateAsync(excelStream, ZipArchiveMode.Update, true, null, cancellationToken).ConfigureAwait(false);
diff --git a/src/MiniExcel/MiniExcelConverter.cs b/src/MiniExcel/MiniExcelConverter.cs
index 331f9d45..ddf9cdfa 100644
--- a/src/MiniExcel/MiniExcelConverter.cs
+++ b/src/MiniExcel/MiniExcelConverter.cs
@@ -5,51 +5,112 @@
namespace MiniExcelLib;
+///
+/// Provides methods for converting between CSV and OpenXml (XLSX) documents.
+///
public static partial class MiniExcelConverter
{
- /// The origin stream is left open.
+ ///
+ /// Converts a CSV file to an OpenXml (XLSX) file.
+ ///
+ /// The file path to the CSV file to be converted.
+ /// The file path where the resulting XLSX file will be created.
+ /// If true, the first row will be treated as headers and printed in the Excel file. Otherwise, all rows are treated as data. Default is false
+ /// A cancellation token to signal that the operation should be cancelled.
+ ///
+ /// A task that completes when the OpenXml file has been successfully created.
+ ///
[CreateSyncVersion]
- public static async Task ConvertCsvToXlsxAsync(Stream csv, Stream xlsx, bool csvHasHeader = false, CancellationToken cancellationToken = default)
+ public static async Task ConvertCsvToXlsxAsync(string csvPath, string xlsxPath, bool csvHasHeader = false, CancellationToken cancellationToken = default)
{
- var value = MiniExcel.Importers
- .GetCsvImporter()
- .QueryAsync(csv, hasHeaderRow: csvHasHeader, leaveOpen: true, cancellationToken: cancellationToken);
+#if SYNC_ONLY
+ using var csvStream = MiniExcelLib.Core.Helpers.FileHelper.OpenSharedRead(csvPath);
+ using var xlsxStream = new FileStream(xlsxPath, FileMode.CreateNew);
+#else
+ var csvStream = FileHelper.OpenSharedRead(csvPath);
+ await using var disposableCsvStream = csvStream.ConfigureAwait(false);
- await MiniExcel.Exporters
- .GetOpenXmlExporter()
- .ExportAsync(xlsx, value, printHeader: csvHasHeader, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
+ var xlsxStream = new FileStream(xlsxPath, FileMode.CreateNew);
+ await using var disposableXlsxStream = xlsxStream.ConfigureAwait(false);
+#endif
+
+ await ConvertCsvToXlsxAsync(csvStream, xlsxStream, csvHasHeader, cancellationToken).ConfigureAwait(false);
}
+ ///
+ /// Converts CSV data from a stream to OpenXml (XLSX) format in another stream.
+ ///
+ /// A readable stream containing CSV data.
+ /// A writable stream where the Excel data will be written.
+ /// If true, the first row will be treated as headers and printed in the Excel file. Otherwise, all rows are treated as data. Default is false.
+ /// A cancellation token to signal that the operation should be cancelled.
+ ///
+ /// A task that completes when the OpenXml data has been successfully written to the output stream.
+ ///
+ ///
+ /// The streams will not be closed by this method; the caller is responsible for disposing them.
+ ///
[CreateSyncVersion]
- public static async Task ConvertCsvToXlsxAsync(string csvPath, string xlsx, bool csvHasHeader = false, CancellationToken cancellationToken = default)
+ public static async Task ConvertCsvToXlsxAsync(Stream csvStream, Stream xlsxStream, bool csvHasHeader = false, CancellationToken cancellationToken = default)
{
- using var csvStream = FileHelper.OpenSharedRead(csvPath);
- using var xlsxStream = new FileStream(xlsx, FileMode.CreateNew);
+ var value = MiniExcel.Importers.GetCsvImporter()
+ .QueryAsync(csvStream, hasHeaderRow: csvHasHeader, leaveOpen: true, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
- await ConvertCsvToXlsxAsync(csvStream, xlsxStream, csvHasHeader, cancellationToken).ConfigureAwait(false);
+ await MiniExcel.Exporters.GetOpenXmlExporter()
+ .ExportAsync(xlsxStream, value, printHeader: csvHasHeader, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
}
+ ///
+ /// Converts an OpenXml (XLSX) file to a CSV file.
+ ///
+ /// The file path to the XLSX file to be converted.
+ /// The file path where the resulting CSV file will be created.
+ /// If true, the first row will be treated as headers and printed in the CSV file. Otherwise, all rows are treated as data. Default is false.
+ /// A cancellation token to signal that the operation should be cancelled.
+ ///
+ /// A task that completes when the CSV file has been successfully created.
+ ///
[CreateSyncVersion]
- public static async Task ConvertXlsxToCsvAsync(string xlsx, string csvPath, bool xlsxHasHeader = true, CancellationToken cancellationToken = default)
+ public static async Task ConvertXlsxToCsvAsync(string xlsxPath, string csvPath, bool xlsxHasHeader = true, CancellationToken cancellationToken = default)
{
- using var xlsxStream = FileHelper.OpenSharedRead(xlsx);
+#if SYNC_ONLY
+ using var xlsxStream = MiniExcelLib.Core.Helpers.FileHelper.OpenSharedRead(xlsxPath);
using var csvStream = new FileStream(csvPath, FileMode.CreateNew);
+#else
+ var xlsxStream = FileHelper.OpenSharedRead(xlsxPath);
+ await using var disposableXlsxStream = xlsxStream.ConfigureAwait(false);
+
+ var csvStream = new FileStream(csvPath, FileMode.CreateNew);
+ await using var disposableCsvStream = csvStream.ConfigureAwait(false);
+#endif
await ConvertXlsxToCsvAsync(xlsxStream, csvStream, xlsxHasHeader, cancellationToken).ConfigureAwait(false);
}
- /// The origin stream is left open.
+ ///
+ /// Converts OpenXml (XLSX) data from a stream to CSV format in another stream.
+ ///
+ /// A readable stream containing the OpenXml data.
+ /// A writable stream where the CSV data will be written.
+ /// If true, the first row will be treated as headers and printed in the Excel file. Otherwise, all rows are treated as data. Default is false.
+ /// A cancellation token to signal that the operation should be cancelled.
+ ///
+ /// A task that completes when the CSV data has been successfully written to the output stream.
+ ///
+ ///
+ /// The streams will not be closed by this method; the caller is responsible for disposing them.
+ ///
[CreateSyncVersion]
- public static async Task ConvertXlsxToCsvAsync(Stream xlsx, Stream csv, bool xlsxHasHeader = true, CancellationToken cancellationToken = default)
+ public static async Task ConvertXlsxToCsvAsync(Stream xlsxStream, Stream csvStream, bool xlsxHasHeader = true, CancellationToken cancellationToken = default)
{
- var value = MiniExcel.Importers
- .GetOpenXmlImporter()
- .QueryAsync(xlsx, hasHeaderRow: xlsxHasHeader, leaveOpen: true, cancellationToken: cancellationToken)
+ var value = MiniExcel.Importers.GetOpenXmlImporter()
+ .QueryAsync(xlsxStream, hasHeaderRow: xlsxHasHeader, leaveOpen: true, cancellationToken: cancellationToken)
.ConfigureAwait(false);
- await MiniExcel.Exporters
- .GetCsvExporter()
- .ExportAsync(csv, value, printHeader: xlsxHasHeader, cancellationToken: cancellationToken).ConfigureAwait(false);
+ await MiniExcel.Exporters.GetCsvExporter()
+ .ExportAsync(csvStream, value, printHeader: xlsxHasHeader, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
}
}
diff --git a/tests/MiniExcel.Csv.Tests/Main/MiniExcelCsvTests.cs b/tests/MiniExcel.Csv.Tests/Main/MiniExcelCsvTests.cs
index 7242000d..443eb2ab 100644
--- a/tests/MiniExcel.Csv.Tests/Main/MiniExcelCsvTests.cs
+++ b/tests/MiniExcel.Csv.Tests/Main/MiniExcelCsvTests.cs
@@ -604,7 +604,7 @@ public void ExportAndQueryMixedFieldAndPropertyTest()
using var reader = new StreamReader(path);
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
var records = csv.GetRecords().ToList();
- var first = records[0] as IDictionary;
+ var first = (IDictionary)records[0];
Assert.Contains("F1", first.Keys);
Assert.Contains("P1", first.Keys);
@@ -622,7 +622,7 @@ public void ExportAndQueryFieldsWithoutAttributeTest()
using var reader = new StreamReader(path);
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
var records = csv.GetRecords().ToList();
- var first = records[0] as IDictionary;
+ var first = (IDictionary)records[0];
Assert.Contains("Mapped", first.Keys);
Assert.DoesNotContain("NotMappedField", first.Keys);
diff --git a/tests/MiniExcel.Csv.Tests/Main/Models.cs b/tests/MiniExcel.Csv.Tests/Main/Models.cs
index 46b4b425..e03f5150 100644
--- a/tests/MiniExcel.Csv.Tests/Main/Models.cs
+++ b/tests/MiniExcel.Csv.Tests/Main/Models.cs
@@ -9,7 +9,7 @@ internal class TestDto
internal class CsvFieldMappingTest
{
[MiniExcelColumnName("Column1")]
- public string Test1;
+ public string? Test1;
[MiniExcelColumnName("Column2")]
public int Test2;
@@ -21,18 +21,18 @@ internal class CsvFieldMappingTest
internal class MixedFieldPropertyTest
{
[MiniExcelColumnName("F1")]
- public string Field1;
+ public string? Field1;
[MiniExcelColumnName("P1")]
- public string Prop1 { get; set; }
+ public string? Prop1 { get; set; }
}
internal class CsvFieldsWithoutAttributeDemo
{
- public string NotMappedField;
+ public string? NotMappedField;
[MiniExcelColumnName("Mapped")]
- public string MappedField;
+ public string? MappedField;
}
internal class TestWithAlias
diff --git a/tests/MiniExcel.OpenXml.Tests/FluentMapping/MiniExcelMappingTests.cs b/tests/MiniExcel.OpenXml.Tests/FluentMapping/MiniExcelMappingTests.cs
index 1ee34c57..038820cc 100644
--- a/tests/MiniExcel.OpenXml.Tests/FluentMapping/MiniExcelMappingTests.cs
+++ b/tests/MiniExcel.OpenXml.Tests/FluentMapping/MiniExcelMappingTests.cs
@@ -1251,7 +1251,7 @@ public void Multiple_Items_With_Collections_Should_Detect_Pattern()
{
var boundaries = mapping.OptimizedBoundaries;
// Pattern detection for multiple items
- Assert.True(boundaries.PatternHeight > 0 || !boundaries.IsMultiItemPattern);
+ Assert.True(boundaries?.PatternHeight > 0 || boundaries?.IsMultiItemPattern is false);
}
}
diff --git a/tests/MiniExcel.OpenXml.Tests/Templates/InputValueExtractorTests.cs b/tests/MiniExcel.OpenXml.Tests/Templates/InputValueExtractorTests.cs
index 54d73057..7bc9a1b2 100644
--- a/tests/MiniExcel.OpenXml.Tests/Templates/InputValueExtractorTests.cs
+++ b/tests/MiniExcel.OpenXml.Tests/Templates/InputValueExtractorTests.cs
@@ -47,10 +47,10 @@ public void ToValueDictionary_Given_InputIsDictionaryWithDataReader_Then_DataRea
var sut = new OpenXmlValueExtractor();
var extracted = sut.ToValueDictionary(valueDictionary);
- var result = (List>)extracted["DataReader"];
+ var result = (List>?)extracted["DataReader"];
- Assert.Equal(result.Count, expectedOutput.Count);
- for (int i = 0; i < result.Count; i++)
+ Assert.Equal(result?.Count, expectedOutput.Count);
+ for (int i = 0; i < result?.Count; i++)
{
var row = result[i];
var expected = expectedOutput[i];
@@ -114,8 +114,8 @@ public void ToValueDictionary_Given_InputIsPocoClass_Then_Output_IsAnEquivalentD
private record PocoRecord(string Name, int Age, IEnumerable Fruits);
private class PocoClass
{
- public string Name { get; set; }
+ public string? Name { get; set; }
public int Age { get; set; }
- public IEnumerable Fruits; // Field
- };
-}
\ No newline at end of file
+ public IEnumerable? Fruits; // Field
+ }
+}
diff --git a/tests/MiniExcel.OpenXml.Tests/Templates/Models.cs b/tests/MiniExcel.OpenXml.Tests/Templates/Models.cs
index 24bb12dd..264424a2 100644
--- a/tests/MiniExcel.OpenXml.Tests/Templates/Models.cs
+++ b/tests/MiniExcel.OpenXml.Tests/Templates/Models.cs
@@ -2,7 +2,7 @@ namespace MiniExcelLib.OpenXml.Tests.Templates;
internal class TestIEnumerableTypePoco
{
- public string @string { get; set; }
+ public string? @string { get; set; }
public int? @int { get; set; }
public decimal? @decimal { get; set; }
public double? @double { get; set; }
@@ -13,8 +13,8 @@ internal class TestIEnumerableTypePoco
internal class Employee
{
- public string name { get; set; }
- public string department { get; set; }
+ public string? name { get; set; }
+ public string? department { get; set; }
}
internal record struct Identity(int Type, string Id);