CsvHelper.OpenXml.Excel 1.4.1

dotnet add package CsvHelper.OpenXml.Excel --version 1.4.1
                    
NuGet\Install-Package CsvHelper.OpenXml.Excel -Version 1.4.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CsvHelper.OpenXml.Excel" Version="1.4.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CsvHelper.OpenXml.Excel" Version="1.4.1" />
                    
Directory.Packages.props
<PackageReference Include="CsvHelper.OpenXml.Excel" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CsvHelper.OpenXml.Excel --version 1.4.1
                    
#r "nuget: CsvHelper.OpenXml.Excel, 1.4.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CsvHelper.OpenXml.Excel@1.4.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CsvHelper.OpenXml.Excel&version=1.4.1
                    
Install as a Cake Addin
#tool nuget:?package=CsvHelper.OpenXml.Excel&version=1.4.1
                    
Install as a Cake Tool

CsvHelper.OpenXml.Excel

NuGet

Introduction

CsvHelper.OpenXml.Excel is a small library thinked to allow the import and export data from and to Excel files. The library is the facto an extension that connects and integrates two famous libraries CsvHelper and OpenXml. It mainly providing implementations of IParser and IWriter, of CsvHelper, which read and write files in xlsx format using OpenXml.

The ultimate goal is to obtain versatility of use and accuracy in import and export results; especially with regard to export, the file obtained, although always in simple tabular form, still has all the characteristics expected for an Excel file, with the columns having the cells formatted in an adequate way and not as simple text.

Prerequisites

Knowledge of CsvHelper and its documentation.

.NET 8 or .NET 9 SDK installed.

Installation

To install the library, from the Package Manager Console use the following command:

PM> Install-Package CsvHelper.OpenXml.Excel

Or, from the .NET Core CLI Console use the following command:

 > dotnet add package CsvHelper.OpenXml.Excel

Import

For importing from Excel files, the library makes available both approaches offered by OpenXml, so there are two implementations of IParser:

  • ExcelDomParser
  • ExcelSaxParser

Both perform the exact same task, with the same input parameters and configuration characteristics, of course the second is strongly recommended for importing very large files, to avoid Out of Memory exceptions.

Example usage

ExcelDomParser and ExcelSaxParser can be used by specifying an instance of Stream as the principal constructor parameter.

byte[] Bytes = File.ReadAllBytes("path/subpath/file.xlsx");

using MemoryStream ExcelStream = new MemoryStream(Bytes);     
using var ExcelParser = new ExcelDomParser(ExcelStream);
using var ExcelReader = new CsvReader(ExcelParser);

IEnumerable<Foo> FooCollection = ExcelReader.GetRecords<Foo>().ToArray();

The constructor has two optional parameters, sheetname, which allows you to specify the name of the worksheet; configuration, for which the instance of a CsvConfiguration is used. In case the sheet name is not specified, the first worksheet is used as the data source by default; if the configuration is not specified, a configuration with InvariantCulture is used by default.

The library, also, provides specific implementations of DefaultTypeConverter that can be used in the definition of ClassMap and that allow, for Excel files having columns with specific cell formats (Date or Time or Custom (Date and Time)), to define mappings to the DateOnly or TimeOnly or DateTime types.

  • ExcelDateOnlyConverter
  • ExcelTimeOnlyConverter
  • ExcelDateTimeConverter

The use of these converters is very versatile, in fact, for example, it's possible to define the mapping of an Excel column having cell format Custom (Date and Time) to a DateOnly type, but also to a TimeOnly type; or define the mapping of an Excel column with a Time cell format to a DateTime type; as even define the mapping of an Excel column having format Date to a DateTime type.

public class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        AutoMap(CultureInfo.CurrentCulture);
        Map(x => x.Date).TypeConverter<ExcelDateOnlyConverter>();
        Map(x => x.Time).TypeConverter<ExcelTimeOnlyConverter>();
        Map(x => x.DateTime).TypeConverter<ExcelDateTimeConverter>();
    }
}

A separate discussion must be made for the following converters, which allow, for Excel files having columns with specific cell formats (Custom (Date and Time), Custom (Date and Time) in text format or Custom (Date and Time) in text format with the information of the offset with respect to UTC time), to define the mappings to the DateTimeOffset type.

  • ExcelDateTimeOffsetConverter
  • ExcelDateTimeOffsetTextConverter

With the use of these converters it's possible to define the mapping of an Excel column having cell format Custom (Date and Time) to a DateTimeOffset type using an ExcelDateTimeOffsetConverter; or map an Excel column having cell format Text with the value of a Custom (Date and Time) or with the value of a Custom (Date and Time) followed by the offset information with respect to UTC time, to a DateTimeOffset type using an ExcelDateTimeOffsetTextConverter.

public class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        AutoMap(CultureInfo.CurrentCulture);

        Map(m => m.DateTimeOffsetUnspecified).TypeConverter(new ExcelDateTimeOffsetConverter(DateTimeKind.Unspecified, new TimeSpan(3, 0, 0)));
        Map(m => m.DateTimeOffsetUnspecifiedAsText).TypeConverter(new ExcelDateTimeOffsetTextConverter());

        Map(m => m.DateTimeOffsetUnspecifiedFromDateTimeAsText).TypeConverter(new ExcelDateTimeOffsetTextConverter(DateTimeKind.Unspecified, new TimeSpan(3, 0, 0)));

        Map(m => m.DateTimeOffsetUtc).TypeConverter(new ExcelDateTimeOffsetConverter(DateTimeKind.Utc, new TimeSpan(0, 0, 0)));
        Map(m => m.DateTimeOffsetUtcAsText).TypeConverter(new ExcelDateTimeOffsetTextConverter());

        Map(m => m.DateTimeOffsettUtcFromDateTimeAsText).TypeConverter(new ExcelDateTimeOffsetTextConverter(DateTimeKind.Utc, new TimeSpan(0, 0, 0)));

        Map(m => m.DateTimeOffsetLocal).TypeConverter(new ExcelDateTimeOffsetConverter(DateTimeKind.Local, new TimeSpan(1, 0, 0)));
        Map(m => m.DateTimeOffsetLocalAsText).TypeConverter(new ExcelDateTimeOffsetTextConverter());
        
        Map(m => m.DateTimeOffsetLocalFromDateTimeAsText).TypeConverter(new ExcelDateTimeOffsetTextConverter(DateTimeKind.Local, new TimeSpan(1, 0, 0)));
    }
}
byte[] Bytes = File.ReadAllBytes("path/subpath/file.xlsx");

using MemoryStream ExcelStream = new MemoryStream(Bytes);     
using var ExcelParser = new ExcelDomParser(ExcelStream);
using var ExcelReader = new CsvReader(ExcelParser);

ExcelReader.Context.RegisterClassMap<FooMap>();

IEnumerable<Foo> FooCollection = ExcelReader.GetRecords<Foo>().ToArray();

The library, also, provides specific implementations that can be used in the definition of ClassMap and that allow, for Excel files having columns with specific Hyperlink cell formats or columns with specific text cell formats attributable to a ValueTuple, to define the mapping to the ValueTuple type.

  • ExcelValueTupleConverter
  • ExcelHyperlink Converter

Because CsvHelper doesn't have a DefaultTypeConverter for the ValueTuple type, we had to define an ExcelValueTupleConverter, and then we defined the hyperlink-specific converter, ExcelHyperlinkConverter.

public class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        AutoMap(CultureInfo.CurrentCulture);
        Map(m => m.Hyperlink).TypeConverter(new ExcelHyperlinkConverter(ExcelHyperlinkTypes.WebUrl, ExcelHyperlinkResultantValueTypes.SingleUri));
        Map(m => m.HyperlinkWithText).TypeConverter(new ExcelHyperlinkConverter(ExcelHyperlinkTypes.WebUrl, ExcelHyperlinkResultantValueTypes.TupleStringUri));
        Map(m => m.HyperlinkEmail).TypeConverter(new ExcelHyperlinkConverter(ExcelHyperlinkTypes.Email, ExcelHyperlinkResultantValueTypes.TupleStringString));
        Map(m => m.HyperlinkToAnotherCell).TypeConverter(new ExcelHyperlinkConverter(ExcelHyperlinkTypes.InternalLink, ExcelHyperlinkResultantValueTypes.TupleStringString));
    }
}

Regarding import, always keep in mind that it's the value of the cell that is imported, regardless of its formatting; in Excel you can have a cell formatted Currency (2 decimals), but its value could be with 4 decimals, so, in this specific case, assuming that the destination type is Decimal, the imported data would be with 4 decimals. If it's necessary to obtain an imported data in the destination type that is more faithful to what is displayed on Excel, it will be necessary to intervene in the definition of the ClassMap by specifying in the mapping the type of conversion with rounding that is to be applied for that specific Excel column.

Assuming that Foo has a Price property of type Decimal, and the Excel file has a Price column with cell formatting Currency (2 decimals) but with a value of 4 decimals, to get the above said

Map(x => x.Price).Convert(args => args.Row.GetField("Price") is null ? 0 : Math.Round(decimal.Parse(args.Row.GetField("Price")!.Replace('.', ',')), 2));

Export

For exporting to Excel files, the library currently only provides the DOM approach of the two offered by OpenXml, so for now there is only one implementation of IWriter:

  • ExcelDomWriter

Example usage

ExcelDomWriter, like ExcelDomParser and ExcelSaxParser, can be used by specifying an instance of Stream as the principal constructor parameter.

using MemoryStream ExcelStream = new MemoryStream();
using (ExcelDomWriter ExcelWriter = new ExcelDomWriter(ExcelStream, new CsvConfiguration(CultureInfo.CurrentCulture)))
{
    ExcelWriter.WriteRecords(FooCollection);
}

byte[] Bytes = ExcelStream.ToArray();

File.WriteAllBytes("path/subpath/file.xlsx", Bytes);

The constructor has an optional parameter, configuration, for which the instance of a CsvConfiguration is used. In case the configuration is not specified, a configuration with InvariantCulture is used by default.

Like with import, you can use the specific implementations of the DefaultTypeConverter to the definition of ClassMap. The library, also, provides both an enumeration:

  • ExcelCellFormats

which allows you to specify the Excel cell format to be applied to the column of the generated worksheet, and a specific implementation of TypeConverterOptions:

  • ExcelTypeConverterOptions

that adds the Excel cell format, to the options that can be used to define the type conversion.

public class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        AutoMap(CultureInfo.CurrentCulture);
        Map(x => x.Date).TypeConverter<ExcelDateOnlyConverter>()
            .Data.TypeConverterOptions = new ExcelTypeConverterOptions { ExcelCellFormat = ExcelCellFormats.DateDefault };
        Map(x => x.Time).TypeConverter<ExcelTimeOnlyConverter>()
            .TypeConverter<ExcelTimeOnlyConverter>().Data.TypeConverterOptions = new ExcelTypeConverterOptions { ExcelCellFormat = ExcelCellFormats.TimeHoursMinutesSecondsDefault };
        Map(x => x.DateTime).TypeConverter<ExcelDateTimeConverter>()
            .Data.TypeConverterOptions = new ExcelTypeConverterOptions { ExcelCellFormat = ExcelCellFormats.DateTimeDefault };

        //With version => 1.2.0
        Map(m => m.DateTimeOffsetUnspecified).TypeConverter(new ExcelDateTimeOffsetConverter(DateTimeKind.Unspecified)).Data.TypeConverterOptions = new ExcelTypeConverterOptions { ExcelCellFormat = ExcelCellFormats.DateTimeWithHoursMinutesSecondsDefault };
        Map(x => x.DateTimeOffsetUnspecifiedAsText).TypeConverter(new ExcelDateTimeOffsetTextConverter(DateTimeKind.Unspecified)).Data.TypeConverterOptions = new ExcelTypeConverterOptions { CultureInfo = CultureInfo.CurrentCulture };
        
        Map(x => x.DateTimeOffsetUnspecifiedFromDateTimeAsText).TypeConverter(new ExcelDateTimeOffsetTextConverter()).Data.TypeConverterOptions = new ExcelTypeConverterOptions { CultureInfo = CultureInfo.CurrentCulture };
        // Or even
        Map(m => m.DateTimeOffsetUnspecifiedFromDateTimeAsText).TypeConverter(new ExcelDateTimeOffsetConverter(DateTimeKind.Unspecified)).Data.TypeConverterOptions = new ExcelTypeConverterOptions { ExcelCellFormat = ExcelCellFormats.DateTimeWithHoursMinutesSecondsDefault };
        
        Map(m => m.DateTimeOffsetUtc).TypeConverter(new ExcelDateTimeOffsetConverter(DateTimeKind.Utc)).Data.TypeConverterOptions = new ExcelTypeConverterOptions { ExcelCellFormat = ExcelCellFormats.DateTimeWithHoursMinutesSecondsDefault  };
        Map(x => x.DateTimeOffsetUtcAsText).TypeConverter(new ExcelDateTimeOffsetTextConverter(DateTimeKind.Utc)).Data.TypeConverterOptions = new ExcelTypeConverterOptions { CultureInfo = CultureInfo.CurrentCulture };
        
        Map(x => x.DateTimeOffsetUtcFromDateTimeAsText).TypeConverter(new ExcelDateTimeOffsetTextConverter()).Data.TypeConverterOptions = new ExcelTypeConverterOptions { CultureInfo = CultureInfo.CurrentCulture };

        Map(m => m.DateTimeOffsetLocal).TypeConverter(new ExcelDateTimeOffsetConverter(DateTimeKind.Local)).Data.TypeConverterOptions = new ExcelTypeConverterOptions { ExcelCellFormat = ExcelCellFormats.DateTimeWithHoursMinutesSecondsDefault };
        Map(x => x.DateTimeOffsetLocalAsText).TypeConverter(new ExcelDateTimeOffsetTextConverter(DateTimeKind.Local)).Data.TypeConverterOptions = new ExcelTypeConverterOptions { CultureInfo = CultureInfo.CurrentCulture };
        
        Map(x => x.DateTimeOffsetLocalFromDateTimeAsText).TypeConverter(new ExcelDateTimeOffsetTextConverter()).Data.TypeConverterOptions = new ExcelTypeConverterOptions { CultureInfo = CultureInfo.CurrentCulture };
    }
}
using MemoryStream ExcelStream = new MemoryStream();
using (ExcelDomWriter ExcelWriter = new ExcelDomWriter(ExcelStream, new CsvConfiguration(CultureInfo.CurrentCulture)))
{
    ExcelWriter.Context.RegisterClassMap<FooMap>();
    ExcelWriter.WriteRecords(FooCollection);
}

byte[] Bytes = ExcelStream.ToArray();

File.WriteAllBytes("path/subpath/file.xlsx", Bytes);

ExcelCellFormats enumeration, the following are the details of the defined named constant members:

  1. Default ➡️ Default format
  2. DefaultBold ➡️ Format with Bold text
  3. DefaultBoldCentered ➡️ Format with Bold text with horizontal aligment centered
  4. NumberIntegerDefault ➡️ Format like "0" - Default for "Int32" type
  5. NumberDecimalWithTwoDecimalsDefault ➡️ Format like "0.00" - Default for "Decimal" type
  6. NumberDecimalWithFourDecimals ➡️ Format like "0.0000"
  7. CurrencyGenericWithoutDecimals ➡️ Format like "#,##0"
  8. CurrencyGenericWithTwoDecimals ➡️ Format like "#,##0.00"
  9. CurrencyGenericWithFourDecimals ➡️ Format like "#,##0.0000"
  10. CurrencyEuroITWithTwoDecimals ➡️ Format like "#,##0.00 €"
  11. CurrencyEuroITWithFourDecimals ➡️ Format like "#,##0.0000 €"
  12. CurrencyDollarUSWithTwoDecimals ➡️ Format like "$#,##0.00"
  13. CurrencyDollarUSWithFourDecimals ➡️ Format like "$#,##0.0000"
  14. CurrencyPoundGBWithTwoDecimals ➡️ Format like "£#,##0.00"
  15. CurrencyPoundGBWithFourDecimals ➡️ Format like "£#,##0.0000"
  16. AccountingEuroITWithTwoDecimals ➡️ Format like "#,##0.00 €"
  17. AccountingEuroITWithFourDecimals ➡️ Format like "#,##0.0000 €"
  18. AccountingDollarUSWithTwoDecimals ➡️ Format like "$ #,##0.00"
  19. AccountingDollarUSWithFourDecimals ➡️ Format like "$ #,##0.0000"
  20. AccountingPoundGBWithTwoDecimals ➡️ Format like "£ #,##0.00;"
  21. AccountingPoundGBWithFourDecimals ➡️ Format like "£ #,##0.0000"
  22. DateDefault ➡️ Format like "dd/mm/yyyy" - Default for "DateOnly" type
  23. DateExtended ➡️ Format like "dddd dd mmmm yyyy"
  24. DateWithDash ➡️ Format like "dd-mm-yyyy"
  25. DateTimeWithHoursMinutesSecondsDefault ➡️ Format like "dd/mm/yyyy hh:mm:ss" - Default for "DateTime" type
  26. DateTimeWithHoursMinutes ➡️ Format like "dd/mm/yyyy hh:mm"
  27. DateTime12HourWithHoursMinutesSeconds ➡️ Format like "dd/mm/yyyy h:mm:ss AM/PM"
  28. DateTime12HourWithHoursMinutes ➡️ Format like "dd/mm/yyyy h:mm AM/PM"
  29. TimeWithHoursMinutesSecondsDefault ➡️ Format like "hh:mm:ss" - Default for "TimeOnly" type
  30. TimeWithHoursMinutes ➡️ Format like "hh:mm"
  31. Time12HourWithHoursMinutesSeconds ➡️ Format like "h:mm:ss AM/PM"
  32. Time12HourWithHoursMinutes ➡️ Format like "h:mm AM/PM"
  33. PercentageWithoutDecimals ➡️ Format like "0%"
  34. PercentageWithTwoDecimals ➡️ Format like "0.00%"
  35. ScientificWithTwoDecimalsDefault ➡️ Format like "0.00E+00" - Default for "Double" type
  36. ScientificWithFourDecimals ➡️ Format like "0.0000E+00"
  37. Text ➡️ Format like plain text
  38. SpecialZipCode ➡️ Format like "00000"

For each type (Int32, DateOnly, DateTime, TimeOnly, Double) a default enumerate has been defined, recognizable by "...Default" at the end of the name; in the ClassMap definition, can omit the Excel cell format if intend to apply the default format to that Excel column.

Assuming that the Date property, of Foo, is of type DateOnly, the member Map can also be written in the following way

Map(x => x.Date).TypeConverter<ExcelDateOnlyConverter>();

//Instead of
Map(x => x.Date).TypeConverter<ExcelDateOnlyConverter>()
   .Data.TypeConverterOptions = new ExcelTypeConverterOptions { ExcelCellFormat = ExcelCellFormats.DateDefault };

For the following converters, in addition to a separate discussion, a slightly different approach must also be used with regard to the definition of the ClassMap and as regards the use, we are talking about the converters that allow, for ValueTuple type properties to define the mapping to obtain Excel files having columns with specific Hyperlink cell formats or columns with specific Text cell formats attributable to a ValueTuple.

  • ExcelValueTupleConverter
  • ExcelHyperlinkConverter

Since CsvHelper not have a DefaultTypeConverter for the ValueTuple type, it was necessary to define an ExcelValueTupleConverter, so the specific converter for Hyperlinks, the ExcelHyperlinkConverter, was defined.

public class FooMap : ClassMap<Foo>
{
    public FooMap(CsvContext csvcontext)
    {
        AutoMap(csvcontext);
        Map(x => x.Hyperlink).TypeConverter(new ExcelHyperlinkConverter(ExcelHyperlinkTypes.WebUrl, ExcelHyperlinkResultantValueTypes.TupleStringUri)).Data.TypeConverterOptions = new ExcelTypeConverterOptions { ExcelCellFormat = ExcelCellFormats.Hyperlink };
        Map(x => x.HyperlinkWithText).TypeConverter(new ExcelHyperlinkConverter(ExcelHyperlinkTypes.WebUrl, ExcelHyperlinkResultantValueTypes.TupleStringUri)).Data.TypeConverterOptions = new ExcelTypeConverterOptions { ExcelCellFormat = ExcelCellFormats.Hyperlink };
        Map(x => x.HyperlinkEmail).TypeConverter(new ExcelHyperlinkConverter(ExcelHyperlinkTypes.Email, ExcelHyperlinkResultantValueTypes.TupleStringString)).Data.TypeConverterOptions = new ExcelTypeConverterOptions { ExcelCellFormat = ExcelCellFormats.Hyperlink };
        Map(x => x.HyperlinkToAnotherCell).TypeConverter(new ExcelHyperlinkConverter(ExcelHyperlinkTypes.InternalLink, ExcelHyperlinkResultantValueTypes.TupleStringString)).Data.TypeConverterOptions = new ExcelTypeConverterOptions { ExcelCellFormat = ExcelCellFormats.Hyperlink };

        //Or if you want to obtain a string representation of the ValueTuple in the cell.
        Map(x => x.HyperlinkWithText).TypeConverter(new ExcelValueTupleConverter());
    }
}
using MemoryStream ExcelStream = new MemoryStream();
using (ExcelDomWriter ExcelWriter = new ExcelDomWriter(ExcelStream, new CsvConfiguration(CultureInfo.CurrentCulture)))
{
    //The following AddConverters are required to enable the recognition of the ValueTuple type by CsvHelper which otherwise would not recognize them,
    //also causing the incorrect placement of the specific columns in the Excel file.
    ExcelWriter.Context.TypeConverterCache.AddConverter<ValueTuple<string, Uri>>(new ExcelValueTupleConverter());
    ExcelWriter.Context.TypeConverterCache.AddConverter<ValueTuple<string, string>>(new ExcelValueTupleConverter());

    //This differentiation is necessary to force CsvHelper to maintain context, which otherwise would not be considered by him.
    ExcelWriter.Context.RegisterClassMap(new FooMap(ExcelWriter.Context));
    ExcelWriter.WriteRecords(FooCollection);
}

byte[] Bytes = ExcelStream.ToArray();

File.WriteAllBytes("path/subpath/file.xlsx", Bytes);

The library, also, allows the export of Enumeration types, by default for Enum type properties the named constants are exported (the names to be clear), if you want to export the value you have to proceed by specifying it in the definition of the ClassMap.

Assuming that Foo has a Season property of type Seasons enum

enum Seasons : int
{
   Spring = 0,
   Summer = 1,
   Autumn = 2,
   Winter = 3
}

To export the value of the enum assigned to the Season of Foo property, the Member Map can be written as follows

Map(m => m.Season).TypeConverterOption.Format("D");

To be clear, for Season = Seasons.Summer in the Excel cell would be exported 1

To export the name of the enumeration assigned to the Season of Foo property while still specifying it in the ClassMap definition, the member map can be written as follows

Map(m => m.Season).TypeConverterOption.Format("G");

To be clear, for Season = Seasons.Summer in the Excel cell would be exported Summer

Usage details

If you have two collections of the same type, you do not need to concat them into a single collection to proceed with the export.

using MemoryStream ExcelStream = new MemoryStream();
using (ExcelDomWriter ExcelWriter = new ExcelDomWriter(ExcelStream, new CsvConfiguration(CultureInfo.CurrentCulture)))
{
    ExcelWriter.Context.RegisterClassMap<FooMap>();
    ExcelWriter.WriteRecords(FooCollection);
    
    ExcelWriter.NextRecord();

    ExcelWriter.WriteRecords(AnotherFooCollection);
}

byte[] Bytes = ExcelStream.ToArray();

File.WriteAllBytes("path/subpath/file.xlsx", Bytes);

This puts, the data in the second collection in the same worksheet as the first collection.

If you have two collections of different types and you need to export them both to the same Excel file.

using MemoryStream ExcelStream = new MemoryStream();
using (ExcelDomWriter ExcelWriter = new ExcelDomWriter(ExcelStream, new CsvConfiguration(CultureInfo.CurrentCulture)))
{
    ExcelWriter.Context.RegisterClassMap<FooMap>();
    ExcelWriter.WriteRecords(FooCollection, "SheetFoo");

    ExcelWriter.Context.UnregisterClassMap<FooMap>();

    ExcelWriter.Context.RegisterClassMap<BarMap>();
    ExcelWriter.WriteRecords(BarCollection, "SheetBar");
}

byte[] Bytes = ExcelStream.ToArray();

File.WriteAllBytes("path/subpath/file.xlsx", Bytes);

This puts the data in the second collection in a different worksheet than the first collection.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.