Skip to content

Commit ab8127b

Browse files
Introduced extensible providers for exporter, importer and templater
1 parent 36055d1 commit ab8127b

32 files changed

+932
-902
lines changed

benchmarks/MiniExcel.Benchmarks/BenchmarkSections/CreateXlsxBenchmark.cs renamed to benchmarks/MiniExcel.Benchmarks/BenchmarkSections/CreateExcelBenchmark.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,23 @@
1010

1111
namespace MiniExcelLib.Benchmarks.BenchmarkSections;
1212

13-
public class CreateXlsxBenchmark : BenchmarkBase
13+
public class CreateExcelBenchmark : BenchmarkBase
1414
{
15-
private MiniExcelExporter _exporter;
15+
private OpenXmlExporter _exporter;
1616

1717
[GlobalSetup]
1818
public void SetUp()
1919
{
2020
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
2121
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
22-
_exporter = new MiniExcelExporter();
22+
_exporter = new OpenXmlExporter();
2323
}
2424

2525
[Benchmark(Description = "MiniExcel Create Xlsx")]
2626
public void MiniExcelCreateTest()
2727
{
2828
using var path = AutoDeletingPath.Create();
29-
_exporter.ExportXlsx(path.FilePath, GetValue());
29+
_exporter.ExportExcel(path.FilePath, GetValue());
3030
}
3131

3232
[Benchmark(Description = "ClosedXml Create Xlsx")]

benchmarks/MiniExcel.Benchmarks/BenchmarkSections/QueryXlsxBenchmark.cs renamed to benchmarks/MiniExcel.Benchmarks/BenchmarkSections/QueryExcelBenchmark.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,28 @@
99

1010
namespace MiniExcelLib.Benchmarks.BenchmarkSections;
1111

12-
public class QueryXlsxBenchmark : BenchmarkBase
12+
public class QueryExcelBenchmark : BenchmarkBase
1313
{
14-
private MiniExcelImporter _importer;
14+
private OpenXmlImporter _importer;
1515

1616
[GlobalSetup]
1717
public void SetUp()
1818
{
1919
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
2020
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
21-
_importer = new MiniExcelImporter();
21+
_importer = new OpenXmlImporter();
2222
}
2323

2424
[Benchmark(Description = "MiniExcel QueryFirst")]
2525
public void MiniExcel_QueryFirst_Test()
2626
{
27-
_ = _importer.QueryXlsx(FilePath).First();
27+
_ = _importer.QueryExcel(FilePath).First();
2828
}
2929

3030
[Benchmark(Description = "MiniExcel Query")]
3131
public void MiniExcel_Query()
3232
{
33-
foreach (var _ in _importer.QueryXlsx(FilePath)) { }
33+
foreach (var _ in _importer.QueryExcel(FilePath)) { }
3434
}
3535

3636
[Benchmark(Description = "ExcelDataReader QueryFirst")]

benchmarks/MiniExcel.Benchmarks/BenchmarkSections/TemplateXlsxBenchmark.cs renamed to benchmarks/MiniExcel.Benchmarks/BenchmarkSections/TemplateExcelBenchmark.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44

55
namespace MiniExcelLib.Benchmarks.BenchmarkSections;
66

7-
public class TemplateXlsxBenchmark : BenchmarkBase
7+
public class TemplateExcelBenchmark : BenchmarkBase
88
{
9-
private MiniExcelTemplater _templater;
9+
private OpenXmlTemplater _templater;
1010

1111
[GlobalSetup]
1212
public void Setup()
1313
{
14-
_templater = new MiniExcelTemplater();
14+
_templater = new OpenXmlTemplater();
1515
}
1616

1717
[Benchmark(Description = "MiniExcel Template Generate")]

benchmarks/MiniExcel.Benchmarks/BenchmarkSections/XlsxAsyncBenchmark.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ namespace MiniExcelLib.Benchmarks.BenchmarkSections;
55

66
public class XlsxAsyncBenchmark : BenchmarkBase
77
{
8-
private MiniExcelExporter _exporter;
9-
private MiniExcelTemplater _templater;
8+
private OpenXmlExporter _exporter;
9+
private OpenXmlTemplater _templater;
1010

1111
[GlobalSetup]
1212
public void Setup()
1313
{
14-
_exporter = new MiniExcelExporter();
15-
_templater = new MiniExcelTemplater();
14+
_exporter = new OpenXmlExporter();
15+
_templater = new OpenXmlTemplater();
1616
}
1717

1818
[Benchmark(Description = "MiniExcel Create Xlsx Async")]
@@ -21,7 +21,7 @@ public async Task MiniExcelCreateAsyncTest()
2121
using var path = AutoDeletingPath.Create();
2222
await using var stream = File.Create(path.FilePath);
2323

24-
await _exporter.ExportXlsxAsync(stream, GetValue());
24+
await _exporter.ExportExcelAsync(stream, GetValue());
2525
}
2626

2727
[Benchmark(Description = "MiniExcel Generate Template Async")]

benchmarks/MiniExcel.Benchmarks/Program.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
var section = Environment.GetEnvironmentVariable("BenchmarkSection");
88
var benchmark = section?.ToLowerInvariant().Trim() switch
99
{
10-
"query" => typeof(QueryXlsxBenchmark),
11-
"create" => typeof(CreateXlsxBenchmark),
12-
"template" => typeof(TemplateXlsxBenchmark),
10+
"query" => typeof(QueryExcelBenchmark),
11+
"create" => typeof(CreateExcelBenchmark),
12+
"template" => typeof(TemplateExcelBenchmark),
1313
_ => throw new ArgumentException($"Benchmark section {section} does not exist")
1414
};
1515

@@ -20,9 +20,9 @@
2020
BenchmarkSwitcher
2121
.FromTypes(
2222
[
23-
typeof(QueryXlsxBenchmark),
24-
typeof(CreateXlsxBenchmark),
25-
typeof(TemplateXlsxBenchmark)
23+
typeof(QueryExcelBenchmark),
24+
typeof(CreateExcelBenchmark),
25+
typeof(TemplateExcelBenchmark)
2626
])
2727
.Run(args, BenchmarkConfig.Default);
2828
}

src/MiniExcel.Csv/CsvExporter.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
namespace MiniExcelLib.Csv;
2+
3+
public partial class CsvExporter
4+
{
5+
#region Append / Export
6+
7+
[CreateSyncVersion]
8+
public async Task<int> AppendToCsvAsync(string path, object value, bool printHeader = true,
9+
CsvConfiguration? configuration = null, CancellationToken cancellationToken = default)
10+
{
11+
if (!File.Exists(path))
12+
{
13+
var rowsWritten = await ExportCsvAsync(path, value, printHeader, false, configuration, cancellationToken: cancellationToken).ConfigureAwait(false);
14+
return rowsWritten.FirstOrDefault();
15+
}
16+
17+
using var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan);
18+
return await AppendToCsvAsync(stream, value, configuration, cancellationToken).ConfigureAwait(false);
19+
}
20+
21+
[CreateSyncVersion]
22+
public async Task<int> AppendToCsvAsync(Stream stream, object value, CsvConfiguration? configuration = null, CancellationToken cancellationToken = default)
23+
{
24+
stream.Seek(0, SeekOrigin.End);
25+
26+
var newValue = value is IEnumerable or IDataReader ? value : new[] { value };
27+
28+
using var writer = new CsvWriter(stream, newValue, false, configuration);
29+
return await writer.InsertAsync(false, cancellationToken).ConfigureAwait(false);
30+
}
31+
32+
[CreateSyncVersion]
33+
public async Task<int[]> ExportCsvAsync(string path, object value, bool printHeader = true, bool overwriteFile = false,
34+
CsvConfiguration? configuration = null, CancellationToken cancellationToken = default)
35+
{
36+
using var stream = overwriteFile ? File.Create(path) : new FileStream(path, FileMode.CreateNew);
37+
return await ExportCsvAsync(stream, value, printHeader, configuration, cancellationToken).ConfigureAwait(false);
38+
}
39+
40+
[CreateSyncVersion]
41+
public async Task<int[]> ExportCsvAsync(Stream stream, object value, bool printHeader = true,
42+
CsvConfiguration? configuration = null, CancellationToken cancellationToken = default)
43+
{
44+
using var writer = new CsvWriter(stream, value, printHeader, configuration);
45+
return await writer.SaveAsAsync(cancellationToken).ConfigureAwait(false);
46+
}
47+
48+
#endregion
49+
50+
#region Convert
51+
52+
[CreateSyncVersion]
53+
public async Task ConvertCsvToXlsxAsync(Stream csv, Stream xlsx, bool csvHasHeader = false, CancellationToken cancellationToken = default)
54+
{
55+
var value = new CsvImporter().
56+
QueryCsvAsync(csv, useHeaderRow: csvHasHeader, cancellationToken: cancellationToken);
57+
58+
await new OpenXmlExporter()
59+
.ExportExcelAsync(xlsx, value, printHeader: csvHasHeader, cancellationToken: cancellationToken)
60+
.ConfigureAwait(false);
61+
}
62+
63+
[CreateSyncVersion]
64+
public async Task ConvertCsvToXlsxAsync(string csvPath, string xlsx, bool csvHasHeader = false, CancellationToken cancellationToken = default)
65+
{
66+
using var csvStream = FileHelper.OpenSharedRead(csvPath);
67+
using var xlsxStream = new FileStream(xlsx, FileMode.CreateNew);
68+
69+
await ConvertCsvToXlsxAsync(csvStream, xlsxStream, csvHasHeader, cancellationToken).ConfigureAwait(false);
70+
}
71+
72+
[CreateSyncVersion]
73+
public async Task ConvertXlsxToCsvAsync(string xlsx, string csvPath, bool xlsxHasHeader = true, CancellationToken cancellationToken = default)
74+
{
75+
using var xlsxStream = FileHelper.OpenSharedRead(xlsx);
76+
using var csvStream = new FileStream(csvPath, FileMode.CreateNew);
77+
78+
await ConvertXlsxToCsvAsync(xlsxStream, csvStream, xlsxHasHeader, cancellationToken).ConfigureAwait(false);
79+
}
80+
81+
[CreateSyncVersion]
82+
public async Task ConvertXlsxToCsvAsync(Stream xlsx, Stream csv, bool xlsxHasHeader = true, CancellationToken cancellationToken = default)
83+
{
84+
var value = new OpenXmlImporter().QueryExcelAsync(xlsx, useHeaderRow: xlsxHasHeader, cancellationToken: cancellationToken).ConfigureAwait(false);
85+
await ExportCsvAsync(csv, value, printHeader: xlsxHasHeader, cancellationToken: cancellationToken).ConfigureAwait(false);
86+
}
87+
88+
#endregion
89+
}

src/MiniExcel.Csv/MiniExcelExtensions/Importer.cs renamed to src/MiniExcel.Csv/CsvImporter.cs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
using MiniExcelLib.DataReader;
22

3-
namespace MiniExcelLib.Csv.MiniExcelExtensions;
3+
namespace MiniExcelLib.Csv;
44

5-
public static partial class Importer
5+
public partial class CsvImporter
66
{
77
#region Query
88

99
[CreateSyncVersion]
10-
public static async IAsyncEnumerable<T> QueryCsvAsync<T>(this MiniExcelImporter me, string path, CsvConfiguration? configuration = null,
10+
public async IAsyncEnumerable<T> QueryCsvAsync<T>(string path, CsvConfiguration? configuration = null,
1111
bool treatHeaderAsData = false, [EnumeratorCancellation] CancellationToken cancellationToken = default)
1212
where T : class, new()
1313
{
1414
using var stream = FileHelper.OpenSharedRead(path);
1515

16-
var query = QueryCsvAsync<T>(me, stream, treatHeaderAsData, configuration, cancellationToken);
16+
var query = QueryCsvAsync<T>(stream, treatHeaderAsData, configuration, cancellationToken);
1717

1818
//Foreach yield return twice reason : https://stackoverflow.com/questions/66791982/ienumerable-extract-code-lazy-loading-show-stream-was-not-readable
1919
await foreach (var item in query.ConfigureAwait(false))
2020
yield return item;
2121
}
2222

2323
[CreateSyncVersion]
24-
public static async IAsyncEnumerable<T> QueryCsvAsync<T>(this MiniExcelImporter me, Stream stream, bool treatHeaderAsData = false,
24+
public async IAsyncEnumerable<T> QueryCsvAsync<T>(Stream stream, bool treatHeaderAsData = false,
2525
CsvConfiguration? configuration = null,
2626
[EnumeratorCancellation] CancellationToken cancellationToken = default)
2727
where T : class, new()
@@ -32,17 +32,17 @@ public static async IAsyncEnumerable<T> QueryCsvAsync<T>(this MiniExcelImporter
3232
}
3333

3434
[CreateSyncVersion]
35-
public static async IAsyncEnumerable<dynamic> QueryCsvAsync(this MiniExcelImporter me, string path, bool useHeaderRow = false,
35+
public async IAsyncEnumerable<dynamic> QueryCsvAsync(string path, bool useHeaderRow = false,
3636
CsvConfiguration? configuration = null,
3737
[EnumeratorCancellation] CancellationToken cancellationToken = default)
3838
{
3939
using var stream = FileHelper.OpenSharedRead(path);
40-
await foreach (var item in QueryCsvAsync(me, stream, useHeaderRow, configuration, cancellationToken).ConfigureAwait(false))
40+
await foreach (var item in QueryCsvAsync(stream, useHeaderRow, configuration, cancellationToken).ConfigureAwait(false))
4141
yield return item;
4242
}
4343

4444
[CreateSyncVersion]
45-
public static async IAsyncEnumerable<dynamic> QueryCsvAsync(this MiniExcelImporter me, Stream stream, bool useHeaderRow = false,
45+
public async IAsyncEnumerable<dynamic> QueryCsvAsync(Stream stream, bool useHeaderRow = false,
4646
CsvConfiguration? configuration = null,
4747
[EnumeratorCancellation] CancellationToken cancellationToken = default)
4848
{
@@ -61,18 +61,18 @@ public static async IAsyncEnumerable<dynamic> QueryCsvAsync(this MiniExcelImport
6161
/// QueryAsDataTable is not recommended, because it'll load all data into memory.
6262
/// </summary>
6363
[CreateSyncVersion]
64-
public static async Task<DataTable> QueryCsvAsDataTableAsync(this MiniExcelImporter me, string path, bool useHeaderRow = true,
64+
public async Task<DataTable> QueryCsvAsDataTableAsync(string path, bool useHeaderRow = true,
6565
CsvConfiguration? configuration = null, CancellationToken cancellationToken = default)
6666
{
6767
using var stream = FileHelper.OpenSharedRead(path);
68-
return await QueryCsvAsDataTableAsync(me, stream, useHeaderRow, configuration, cancellationToken).ConfigureAwait(false);
68+
return await QueryCsvAsDataTableAsync(stream, useHeaderRow, configuration, cancellationToken).ConfigureAwait(false);
6969
}
7070

7171
/// <summary>
7272
/// QueryAsDataTable is not recommended, because it'll load all data into memory.
7373
/// </summary>
7474
[CreateSyncVersion]
75-
public static async Task<DataTable> QueryCsvAsDataTableAsync(this MiniExcelImporter me, Stream stream, bool useHeaderRow = true,
75+
public async Task<DataTable> QueryCsvAsDataTableAsync(Stream stream, bool useHeaderRow = true,
7676
CsvConfiguration? configuration = null, CancellationToken cancellationToken = default)
7777
{
7878
var dt = new DataTable();
@@ -126,19 +126,19 @@ public static async Task<DataTable> QueryCsvAsDataTableAsync(this MiniExcelImpor
126126
#region Info
127127

128128
[CreateSyncVersion]
129-
public static async Task<ICollection<string>> GetCsvColumnsAsync(this MiniExcelImporter me, string path, bool useHeaderRow = false,
129+
public async Task<ICollection<string>> GetCsvColumnsAsync(string path, bool useHeaderRow = false,
130130
CsvConfiguration? configuration = null, CancellationToken cancellationToken = default)
131131
{
132132
using var stream = FileHelper.OpenSharedRead(path);
133-
return await GetCsvColumnsAsync(me, stream, useHeaderRow, configuration, cancellationToken).ConfigureAwait(false);
133+
return await GetCsvColumnsAsync(stream, useHeaderRow, configuration, cancellationToken).ConfigureAwait(false);
134134
}
135135

136136
[CreateSyncVersion]
137-
public static async Task<ICollection<string>> GetCsvColumnsAsync(this MiniExcelImporter me, Stream stream, bool useHeaderRow = false,
137+
public async Task<ICollection<string>> GetCsvColumnsAsync(Stream stream, bool useHeaderRow = false,
138138
CsvConfiguration? configuration = null, CancellationToken cancellationToken = default)
139139
{
140140
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
141-
await using var enumerator = QueryCsvAsync(me, stream, useHeaderRow, configuration, cancellationToken).GetAsyncEnumerator(cancellationToken);
141+
await using var enumerator = QueryCsvAsync(stream, useHeaderRow, configuration, cancellationToken).GetAsyncEnumerator(cancellationToken);
142142
#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task
143143

144144
_ = enumerator.ConfigureAwait(false);
@@ -154,35 +154,35 @@ public static async Task<ICollection<string>> GetCsvColumnsAsync(this MiniExcelI
154154

155155
#region DataReader
156156

157-
public static MiniExcelDataReader GetCsvDataReader(this MiniExcelImporter me, string path, bool useHeaderRow = false, CsvConfiguration? configuration = null)
157+
public MiniExcelDataReader GetCsvDataReader(string path, bool useHeaderRow = false, CsvConfiguration? configuration = null)
158158
{
159159
var stream = FileHelper.OpenSharedRead(path);
160-
var values = QueryCsv(me, stream, useHeaderRow, configuration).Cast<IDictionary<string, object?>>();
160+
var values = QueryCsv(stream, useHeaderRow, configuration).Cast<IDictionary<string, object?>>();
161161

162162
return MiniExcelDataReader.Create(stream, values);
163163
}
164164

165-
public static MiniExcelDataReader GetCsvDataReader(this MiniExcelImporter me, Stream stream, bool useHeaderRow = false, CsvConfiguration? configuration = null)
165+
public MiniExcelDataReader GetCsvDataReader(Stream stream, bool useHeaderRow = false, CsvConfiguration? configuration = null)
166166
{
167-
var values = QueryCsv(me, stream, useHeaderRow, configuration).Cast<IDictionary<string, object?>>();
167+
var values = QueryCsv(stream, useHeaderRow, configuration).Cast<IDictionary<string, object?>>();
168168
return MiniExcelDataReader.Create(stream, values);
169169
}
170170

171-
public static async Task<MiniExcelAsyncDataReader> GetAsyncCsvDataReader(this MiniExcelImporter me, string path, bool useHeaderRow = false,
171+
public async Task<MiniExcelAsyncDataReader> GetAsyncCsvDataReader(string path, bool useHeaderRow = false,
172172
string? sheetName = null, string startCell = "A1", CsvConfiguration? configuration = null,
173173
CancellationToken cancellationToken = default)
174174
{
175175
var stream = FileHelper.OpenSharedRead(path);
176-
var values = QueryCsvAsync(me, stream, useHeaderRow, configuration, cancellationToken);
176+
var values = QueryCsvAsync(stream, useHeaderRow, configuration, cancellationToken);
177177

178178
return await MiniExcelAsyncDataReader.CreateAsync(stream, CastAsync(values, cancellationToken)).ConfigureAwait(false);
179179
}
180180

181-
public static async Task<MiniExcelAsyncDataReader> GetAsyncCsvDataReader(this MiniExcelImporter me, Stream stream, bool useHeaderRow = false,
181+
public async Task<MiniExcelAsyncDataReader> GetAsyncCsvDataReader(Stream stream, bool useHeaderRow = false,
182182
string? sheetName = null, string startCell = "A1", CsvConfiguration? configuration = null,
183183
CancellationToken cancellationToken = default)
184184
{
185-
var values = QueryCsvAsync(me, stream, useHeaderRow, configuration, cancellationToken);
185+
var values = QueryCsvAsync(stream, useHeaderRow, configuration, cancellationToken);
186186
return await MiniExcelAsyncDataReader.CreateAsync(stream, CastAsync(values, cancellationToken)).ConfigureAwait(false);
187187
}
188188

src/MiniExcel.Csv/GlobalUsings.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
global using System.Runtime.CompilerServices;
88
global using System.Text;
99
global using System.Text.RegularExpressions;
10-
global using MiniExcelLib;
1110
global using MiniExcelLib.Abstractions;
1211
global using MiniExcelLib.Exceptions;
1312
global using MiniExcelLib.Helpers;

0 commit comments

Comments
 (0)