Zongsoft.Externals.ClosedXml 4.5.0

dotnet add package Zongsoft.Externals.ClosedXml --version 4.5.0
                    
NuGet\Install-Package Zongsoft.Externals.ClosedXml -Version 4.5.0
                    
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="Zongsoft.Externals.ClosedXml" Version="4.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Zongsoft.Externals.ClosedXml" Version="4.5.0" />
                    
Directory.Packages.props
<PackageReference Include="Zongsoft.Externals.ClosedXml" />
                    
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 Zongsoft.Externals.ClosedXml --version 4.5.0
                    
#r "nuget: Zongsoft.Externals.ClosedXml, 4.5.0"
                    
#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 Zongsoft.Externals.ClosedXml@4.5.0
                    
#: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=Zongsoft.Externals.ClosedXml&version=4.5.0
                    
Install as a Cake Addin
#tool nuget:?package=Zongsoft.Externals.ClosedXml&version=4.5.0
                    
Install as a Cake Tool

Zongsoft.Externals.ClosedXml Extension Library

License NuGet NuGet Downloads

English | 简体中文


Overview

Zongsoft.Externals.ClosedXml integrates ClosedXML and ClosedXML.Report with the data archiving and template rendering abstractions provided by Zongsoft. It supports:

  • Exporting model data to .xlsx workbooks;
  • Extracting strongly typed records from .xlsx workbooks;
  • Creating enum and Boolean drop-down lists from model property metadata;
  • Rendering Excel report templates with data and parameters;
  • Discovering .xlsx templates from a directory tree;
  • Localized validation and operation errors in English and Simplified Chinese.

The archive format is named Spreadsheet, uses the .xlsx extension, and has the MIME type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.

Installation

Install the NuGet package:

dotnet add package Zongsoft.Externals.ClosedXml

The package targets the same supported frameworks as Zongsoft Framework and currently uses ClosedXML 0.105.1 and ClosedXML.Report 0.2.12.

Workbook Convention

The data boundary is an Excel Table, not a Defined Name or the worksheet's used range. The table name must be the model descriptor's Name.

For a model whose name is User, the workbook therefore contains an Excel Table named User. This convention lets Zongsoft.Data and the Import/ImportAsync endpoints in Zongsoft.Web locate the dataset directly from the current model without requiring a generated internal name or extra configuration.

The worksheet name is only a display or grouping concern. The generator uses model.Title ?? model.Name for it, while the extractor searches all worksheets by default. Set DataArchiveExtractorOptions.Source to a worksheet name only when the search must be restricted to that worksheet; the table inside it must still be named after the model.

The generated layout is:

Row Content
1 Model title
2 Export time and model name
3 Excel Table header
4 and below Data records

For non-empty exports, the generated Table contains exactly the exported records. When no records are exported, it contains one or more blank data rows for manual entry; the extractor ignores those completely empty rows.

Each generated header cell also has a worksheet-scoped Defined Name matching its model field. The extractor uses these field names for stable column mapping and accepts property-name headers as a fallback for manually created tables.

The generated Table keeps its header formatting separate and uses conditional formatting for the data area's alternating gray background and row separators. Excel automatically expands these rules when a user resizes the Table, so newly added rows receive the same data-area appearance without a custom Table theme.

Enum properties receive an Excel data-validation drop-down whose entries are the enum member names. Boolean properties use a TRUE/FALSE drop-down. Nullable enum and Boolean properties additionally contain one selectable empty entry. Lists that require a real blank or typed Boolean values are stored on a VeryHidden internal worksheet and referenced directly as validation ranges; this keeps validation infrastructure out of the editable data sheet and prevents Excel from treating native Boolean cells as invalid text-list values. Dates before 1900-01-01, which Excel's default date system cannot display correctly, are written as readable yyyy-MM-dd text; supported dates remain native Excel date values.

Simplex metadata also drives native Excel input validation. Character fields with a positive Length reject longer input, and non-nullable character fields additionally reject empty input; Byte through UInt32 fields require whole numbers within their type range; Decimal, Currency, VarNumeric, Single, and Double fields require numeric input. DateTime fields use Excel's native date validation and accept dates from 1900-01-01 through 9999-12-31; earlier dates are unsupported. These rules use localized Stop-style error messages and respect nullable metadata. Int64/UInt64, Guid, binary, object, XML, and JSON values intentionally remain import-validated because Excel precision or reliable native validation is insufficient. Excel validation is an early-entry aid and can be bypassed by paste, macros, or external writers, so extraction and model validation remain authoritative.

Generated column widths follow simplex-property DataType and semantic Role. Character columns also use their declared Length, with practical minimum/default widths and a maximum width of 50. Properties whose role is Currency use Excel's locale-aware built-in currency format. Primary-key data columns are centered and use bold Maroon text. Center alignment for keys, enums, dates, Boolean values, identifiers and applicable semantic roles is also stored as the worksheet-column default, so values entered into rows added by resizing the Table retain the same alignment.

Edited workbook recovery

Excel users sometimes append records below a table without expanding it. When the table does not use a totals row, the extractor keeps the table's column boundary but extends the last data row to the last non-empty cell below those columns. Empty rows are ignored. This recovers common edits without treating unrelated columns as model data.

Keep notes and unrelated content outside the table's column band: content below those columns can intentionally be interpreted as an appended record. When a totals row is enabled, only the table's declared data range is extracted.

Files that contain only a model-level Defined Name, an invalid reference, or merely a worksheet named after the model are intentionally rejected. Create an actual Excel Table whose name is the model name.

Exporting Data

SpreadsheetGenerator creates a workbook and its model table in one operation:

using Zongsoft.Data;
using Zongsoft.Externals.ClosedXml;

var model = Model.GetDescriptor<User>();
var users = GetUsers();

await using var output = File.Create("users.xlsx");
await new SpreadsheetGenerator().GenerateAsync(output, model, users);

Use DataArchiveGeneratorOptions to select exported fields:

using Zongsoft.Data.Archiving;

var options = new DataArchiveGeneratorOptions(nameof(User.UserId), nameof(User.Name));
await generator.GenerateAsync(output, model, users, options);

For explicit column presentation, pass DataArchiveField instances. Widths use typographic points (1/72 inch), with zero meaning unspecified; font sizes follow the same zero-as-unspecified convention. Colors are technology-neutral ARGB values from Zongsoft.Components.Color, while a null color means unspecified; and Format is a .NET format specifier rather than an Excel number-format code:

using Zongsoft.Components;
using Zongsoft.Data.Archiving;

var options = new DataArchiveGeneratorOptions(
	new DataArchiveField(nameof(User.UserId))
	{
		Width = 72,
		Alignment = DataArchiveFieldAlignment.Center,
		FontStyle = DataArchiveFontStyle.Bold,
		ForegroundColor = Color.Maroon,
	},
	new DataArchiveField(nameof(User.Balance))
	{
		Width = 90,
		Alignment = DataArchiveFieldAlignment.Right,
		Format = "N2",
	},
	new DataArchiveField(nameof(User.Email))
	{
		Width = 180,
		TextMode = DataArchiveFieldTextMode.Wrap,
	});

Unspecified options continue to use styles inferred from model metadata. None, Wrap, and Shrink map to Excel's native text-display behaviors. An ellipsis mode is intentionally absent because Excel cells cannot display a native trailing ellipsis without changing the stored value.

Because the model name becomes an Excel Table name, it must satisfy Excel's table-name rules. The generator reports a localized validation error when it does not.

Extracting Data

SpreadsheetExtractor obtains the model from the extraction options, locates the table named after that model, and maps its columns back to model properties:

using Zongsoft.Data;
using Zongsoft.Data.Archiving;
using Zongsoft.Externals.ClosedXml;

var model = Model.GetDescriptor<User>();
var options = new DataArchiveExtractorOptions(model);

await using var input = File.OpenRead("users.xlsx");
await foreach(var user in new SpreadsheetExtractor().ExtractAsync<User>(input, options))
	Console.WriteLine($"{user.UserId}: {user.Name}");

To restrict lookup to a specific worksheet:

var options = new DataArchiveExtractorOptions(model)
{
	Source = "Import",
};

The extractor reports a localized error when the worksheet, model table, or required model fields cannot be resolved.

Zongsoft.Web Integration

The generator and extractor are registered as Zongsoft services for IDataArchiveGenerator and IDataArchiveExtractor. Once this extension is loaded by the application, a ServiceController import operation supplies its current model descriptor to the extractor. The default endpoint contract is therefore simple: upload a workbook containing an Excel Table whose name is the current model name.

No private generated table name is required, and a custom worksheet name does not change the model-table convention.

Rendering Templates

SpreadsheetRenderer renders an .xlsx template using ClosedXML.Report variables. SpreadsheetTemplateProvider recursively discovers .xlsx files and indexes each template by its filename without the extension:

using Zongsoft.Externals.ClosedXml;

var provider = new SpreadsheetTemplateProvider("templates");
var template = provider.GetTemplate("invoice")
	?? throw new InvalidOperationException("Template not found.");

var parameters = new Dictionary<string, object>
{
	["GeneratedAt"] = DateTimeOffset.Now,
};

await using var output = File.Create("invoice.xlsx");
await new SpreadsheetRenderer().RenderAsync(output, template, invoice, parameters);

Template variables and expressions follow the ClosedXML.Report syntax.

Localization

English is the neutral resource language, and Simplified Chinese resources are provided for zh-Hans. Error messages follow CultureInfo.CurrentUICulture, so applications should establish the UI culture through their normal request or host localization pipeline.

Sample

The interactive sample project uses Bogus to generate locale-aware fake users, exports data, imports it again, and displays the workbook structure and extracted records for manual verification.

Run it from the repository root:

dotnet run --project externals/closedxml/samples/Zongsoft.Externals.ClosedXml.Samples.csproj -f net10.0

Available commands:

Command Description
export [--count:<number>\|-c:<number>] [--culture:<name>\|-l:<name>] [file] Generate and export fake users, then display the generated worksheet, table, range, columns, and rows. For example, export -c:20 -l:en-US users.xlsx.
import [file] Import a workbook and display its structure and extracted users.
verify [options] [file] Export and immediately import a workbook for an end-to-end check; it accepts the same count and culture options as export.

When export or verify has no file argument, its default name includes the effective culture and record count, such as users.zh-CN(10).xlsx or users.en(0).xlsx. The default generated record count is 10; a count of zero creates the generator's blank entry rows. import still defaults to users.xlsx. out and in are aliases for export and import.

For example, export --count:20 --culture:en-US users.en.xlsx generates English titles, labels, and fake user names, while export -c:20 -l:zh-Hans users.zh-Hans.xlsx generates Simplified Chinese content. The selected culture applies only to that command.

Build and Test

dotnet build externals/closedxml/Zongsoft.Externals.ClosedXml.slnx --no-incremental
dotnet test externals/closedxml/test/Zongsoft.Externals.ClosedXml.Tests.csproj -f net10.0

License

This project is licensed under the GNU Lesser General Public License.

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 is compatible.  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.

Version Downloads Last Updated
4.5.0 91 8/8/2026
4.4.0 87 8/6/2026
4.3.0 135 3/12/2026
4.2.0 118 3/8/2026
4.1.0 120 2/26/2026
4.0.0 238 6/27/2025
3.0.0 217 2/24/2025
Loading failed