PandaTech.FluentImporter 6.0.0

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

Pandatech.FluentImporter

Fluent API for importing CSV and Excel data into .NET 8+ applications with customizable property mapping and validation.

Installation

dotnet add package PandaTech.FluentImporter

Quick Start

Define Your Model

public class FileData
{
    public long Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public DateTime Date { get; set; }
    public string? Comment { get; set; }
    public DateTime CreatedAt { get; init; }
    public string CreatedBy { get; init; }
}

Create Import Rules

public class FileDataImportRule : ImportRule<FileData>
{
    public FileDataImportRule()
    {
        RuleFor(x => x.Name)
            .NotEmpty();

        RuleFor(x => x.Description)
            .ReadFromColumn("Description text")
            .Default("No Description");

        RuleFor(x => x.Date)
            .ReadFromColumn("Date")
            .Convert(DateTime.Parse);

        RuleFor(x => x.Comment)
            .ReadFromColumn("Comment");

        RuleFor(x => x.Id)
            .ReadFromColumn("Id")
            .Convert(s => long.Parse(s));

        RuleFor(x => x.CreatedAt)
            .WriteValue(DateTime.UtcNow);

        RuleFor(x => x.CreatedBy)
            .ReadFromModel(x => x.CreatedBy + " - Modified");
    }
}

Import Data

var importRule = new FileDataImportRule();

// From CSV file
var data = importRule.ReadCsv("path/to/file.csv");

// From CSV stream
using var stream = File.OpenRead("file.csv");
var data = importRule.ReadCsv(stream);

// From Excel file
var data = importRule.ReadXlsx("path/to/file.xlsx");

// From Excel stream
using var stream = File.OpenRead("file.xlsx");
var data = importRule.ReadXlsx(stream);

// From in-memory data
var dict = new List<Dictionary<string, string>>
{
    new() { ["Name"] = "John", ["Date"] = "2024-01-01" }
};
var data = importRule.GetRecords(dict);

Read with error aggregation

ReadCsv / ReadXlsx throw on the first bad cell. To report every bad cell at once, use the Try variants:

var result = importRule.TryReadXlsx(stream, new ImportReadOptions { SheetName = "Data", MaxErrors = 200 });

foreach (var error in result.Errors)
{
    // error.RowNumber, error.ColumnName, error.PropertyName, error.RawValue, error.Message
}

return result.HasErrors ? BadRequest(result.Errors) : Save(result.Records);

Pass SheetName whenever the workbook may contain more than one sheet. Without it the first sheet by tab position is used, which means a legend or instruction sheet placed first would be read as the data sheet.

Generating the template

An import rule already knows every column, so it can emit the file the operator fills in. Generating the template from the rule is the only way to guarantee the download and the parser cannot drift apart.

The result has two sheets: the fillable data sheet first (so it is the tab Excel opens on) and a legend second. Enum columns get an in-cell dropdown sourced from the legend, and the dropdown inserts "0 - Label" which the reader normalizes back to 0 — so the operator picks something meaningful while the column still imports as an integer.

public class ProductImportRule : ImportRule<ProductImportModel>
{
    public ProductImportRule()
    {
        RuleFor(x => x.CategoryId).ReadFromColumn("Category Id").NotEmpty()
            .Describe("Id of the category this product belongs to").WithExample(1);

        RuleFor(x => x.Status).ReadFromColumn("Status").NotEmpty()
            .WithEnumSource<ProductStatus>()        // an int column that carries an enum
            .Describe("Product status").WithExample(0);

        RuleFor(x => x.ReleasedOn).ReadFromColumn("Released On")
            .Convert(ParseDayFirst)
            .WithExpectedFormat("dd/MM/yyyy");      // a converter is opaque; declare what it expects
    }
}

var bytes = new ProductImportRule().BuildTemplate(options: new ImportTemplateOptions
{
    DataSheetName = "Products",
    LegendSheetName = "Legend",
    EnumLabelResolver = (enumType, member, value) => localizer.Label(enumType, member),
    ColumnDescriptionResolver = column => localizer.ColumnDescription(column.ColumnName),
    Texts = new ImportTemplateTexts { LegendTitle = "Legend", RequiredYes = "Yes", /* … */ }
});

The library holds no opinion about language: sheet names, captions, column descriptions and enum labels are all whatever EnumLabelResolver, ColumnDescriptionResolver and Texts return, in any script. The data sheet's header row is the one thing never localized — it is the parsing contract, so it is always the rule's canonical ReadFromColumn text.

Inspecting the columns

ImportRule<T>.Columns exposes every configured column in declaration order as IImportColumn: ColumnName, Aliases, PropertyName, PropertyType, IsRequired, IsNullable, DefaultValue, RegexPattern, HasCustomConverter, Description, ExpectedFormat, Example, EnumSource, AllowedValues, ValueSource, HasValueSet.

API Reference

Property Rules

Method Description
ReadFromColumn(string) Map to a different column name
WithAliases(params string[]) Also accept these header spellings
NotEmpty() Require a non-empty value; the column must also be present
Default(T) Set default value if null/empty
Convert(Func<string, T>) Custom converter function
Convert(Func<string, TModel, T>) Converter with access to model instance
Validate(string) Regex validation pattern
WriteValue(T) Set a constant value; never read from the file, never templated
ReadFromModel(Func<TModel, T>) Compute value from model; never read from the file, never templated
Describe(string) Legend description and header note
WithExample(T) Example value for generated sample rows
WithExpectedFormat(string) Textual format a custom converter expects, e.g. dd/MM/yyyy
WithEnumSource<TEnum>() Declare the enum an int column carries; drives legend and dropdown
WithAllowedValues(...) Declare a closed value set for a non-enum column

Header matching

Case-insensitive, outer whitespace trimmed, and inner whitespace runs collapsed — "Full Name", "FULL NAME", " full name " and "Full Name" all match a rule declaring Full Name. Everything else is exact: spelling, hyphens, and Armenian vs Latin script are load-bearing. Extra unmapped columns are ignored; an optional mapped column may be absent entirely.

Supported Types

  • Primitives: string, int, long, decimal, double, bool, etc.
  • DateTime, DateTimeOffset, Guid
  • Nullable<T> for all value types
  • Enum types (case-insensitive)
  • Custom types via Convert() method

Boolean Parsing

The library automatically handles multiple boolean representations:

  • true/false
  • 1/0
  • yes/no

Error Handling

All import exceptions inherit from ImportException:

Exception Raised when
EmptyFileImportException File contains no data rows
MissingColumnsException Required columns are absent from the header row (checked before any row)
DuplicateColumnException Two header cells normalize to the same column name
SheetNotFoundException A worksheet was requested by name and does not exist
InvalidCellValueException A cell value failed validation or conversion
InvalidColumnValueException A value did not satisfy the rule
InvalidPropertyNameException A rule references a property that does not exist on the model

InvalidCellValueException.Error carries the failure as structured data (ImportError): row number, column name, property name, raw value, target type and message. The Try read variants return these instead of throwing.

License

MIT

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 (2)

Showing the top 2 NuGet packages that depend on PandaTech.FluentImporter:

Package Downloads
Pandatech.SharedKernel

Opinionated ASP.NET Core 10 infrastructure kernel: OpenAPI (Swagger + Scalar), Serilog, MediatR, FluentValidation, CORS, SignalR, OpenTelemetry, health checks, maintenance mode, resilience pipelines, and shared utilities.

Pandatech.ResponseCrafter

Exception-based error handling for ASP.NET Core 8+ with RFC 9457 ProblemDetails, structured logging, typed exceptions, automatic EF Core/Gridify/FluentImporter exception mapping, SignalR support, and flexible naming conventions. Alternative to Result pattern libraries.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
6.0.0 0 7/26/2026
5.0.1 144 7/6/2026
5.0.0 256 2/28/2026
4.0.1 189 1/26/2026
4.0.0 167 12/28/2025
3.0.9 359 9/8/2025
3.0.8 240 9/8/2025
3.0.7 243 9/8/2025
3.0.6 265 9/8/2025
3.0.5 399 8/7/2025
3.0.4 403 6/1/2025
3.0.2 399 3/12/2025
3.0.1 380 2/17/2025
3.0.0 713 11/21/2024
2.0.13 314 9/4/2024
2.0.12 673 5/16/2024
2.0.11 229 5/16/2024
2.0.10 213 5/16/2024
2.0.9 251 5/15/2024
2.0.8 221 5/13/2024
Loading failed

Template generation and reader hardening.

Added: ImportRule<T>.Columns exposes ordered column metadata via the new public IImportColumn; new declarative
rule methods Describe, WithExample, WithExpectedFormat, WithEnumSource, WithAllowedValues, WithAliases;
ImportRule<T>.BuildTemplate generates a two-sheet xlsx (fillable data sheet first, legend second) with styled and
frozen headers, sample rows, localizable legend captions and in-cell dropdowns sourced from the legend;
ReadXlsx(stream, sheetName) selects a worksheet by name; TryReadXlsx/TryReadCsv aggregate every cell error instead
of throwing on the first; new MissingColumnsException, DuplicateColumnException and SheetNotFoundException;
InvalidCellValueException now carries a structured ImportError.

Fixed: header cells are read positionally, so a blank or leading-blank header cell no longer shifts every column
silently; cells are stringified culture-invariantly, so decimals and dates no longer break under a non-invariant
server culture; required columns are validated up front instead of surfacing as a misleading row-2 cell error;
fully blank rows are skipped; enum conversion rejects undefined numeric values; duplicate headers and unsettable
properties throw typed ImportExceptions instead of raw ArgumentException/InvalidOperationException; constant and
computed columns no longer require a header to be present; header matching collapses inner whitespace runs.

Breaking: IPropertyRule (internal) is replaced by the public IImportColumn; the internal ReadFromType enum is
replaced by the public ImportValueSource; WriteValue and ReadFromModel now return the rule for chaining;
RuleFor throws immediately when the property does not exist on the model; an optional mapped column may now be
absent from the file instead of failing every row.