DataImportExportManager 1.0.0

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

DataImportExportManager

A .NET 10 library for deterministic tabular data import/export across CSV, Excel (.xlsx), JSON, and NDJSON (.ndjson/.jsonl) formats.

Local Validation

Run the repository-local validation entry point before creating or updating a pull request:

pwsh ./scripts/validate.ps1 -Pack

This repository follows the class-library promotion model. The validation entry point restores, builds, runs tests when test projects exist, and produces the traceable package output used for promotion.

Features

  • CSV/TSV import/export — RFC 4180–compliant parsing and writing with full support for multi-line quoted fields, configurable delimiter, and configurable encoding.
  • Excel import/export — Reads and writes .xlsx files via the DocumentFormat.OpenXml SDK with configurable sheet selection.
  • JSON import/export — Supports .json payloads using System.Text.Json with deterministic tabular mapping.
  • NDJSON import/export — Supports line-delimited JSON records (.ndjson and .jsonl alias) for pipeline-friendly ingestion/export.
  • XML import/export — Supports deterministic tabular XML using <rows><row><cell>...</cell></row></rows> schema, with optional object-element row mode.
  • Deterministic format routingIDataFormatRouter resolves importers/exporters from explicit format input (for example, UI-selected extension).

JSON Serializer Baseline

This library uses System.Text.Json (Microsoft .NET JSON APIs) and does not depend on Newtonsoft.Json.

  • Extensible — Add new formats by implementing IDataImporter or IDataExporter and registering them with the DI container.
  • Structured logging — Zero-allocation [LoggerMessage] source-generated logging with ILogger<T>. All loggers are optional — the library falls back to NullLogger<T> automatically.
  • Configurable options — Per-component options classes let consumers tune encoding, delimiter, buffer limits, sheet selection, and sanitization behaviour.
  • Security hardening — Opt-in formula-cell sanitization, configurable stream size limits, and resource leak protection.
  • High performanceSearchValues<char> SIMD scanning, ArrayPool<byte/char> buffers, ValueTask with pooling, and pre-allocated collections.

Project Structure

DataImportExportManager/
├── Interfaces/                  # Public contracts
│   ├── IDataImporter.cs         # Stream → tabular data
│   ├── IDataExporter.cs         # Tabular data → stream
│   └── IDataFormatRouter.cs     # Deterministic format selection
├── Contracts/                   # Public data contracts
│   └── TabularImportResult.cs   # Columns + data rows import shape
├── Importers/                   # IDataImporter implementations
│   ├── CsvImporter.cs
│   ├── CsvImporterOptions.cs
│   ├── TsvImporter.cs
│   ├── ExcelImporter.cs
│   ├── ExcelImporterOptions.cs
│   ├── JsonImporter.cs
│   ├── NdjsonImporter.cs
│   ├── XmlImporter.cs
│   ├── XmlImporterOptions.cs
│   └── JsonImporterOptions.cs
├── Exporters/                   # IDataExporter implementations
│   ├── CsvExporter.cs
│   ├── CsvExporterOptions.cs
│   ├── TsvExporter.cs
│   ├── ExcelExporter.cs
│   ├── JsonExporter.cs
│   ├── JsonExporterOptions.cs
│   ├── NdjsonExporter.cs
│   ├── NdjsonExporterOptions.cs
│   ├── XmlExporter.cs
│   └── XmlExporterOptions.cs
├── Services/                    # Routing/orchestration
│   └── DataFormatRouter.cs
└── Extensions/                  # DI registration
    ├── ServiceCollectionExtensions.cs
    └── DataImportSchemaExtensions.cs

DataImportExportManager.Tests/   # xUnit test project
├── CsvImporterTests.cs
├── CsvExporterTests.cs
├── ExcelImporterTests.cs
├── JsonImporterTests.cs
├── JsonExporterTests.cs
├── NdjsonImporterTests.cs
├── NdjsonExporterTests.cs
└── DataFormatRouterTests.cs

Getting Started

Prerequisites

Build

dotnet build

Run Tests

dotnet test

From the repository root, this runs the in-repo test project at DataImportExportManager.Tests/DataImportExportManager.Tests.csproj.

Session Cache Performance Baseline Checks

Run session-cache baseline checks from the test project:

dotnet test --filter "FullyQualifiedName~SessionCachePerformanceTests"

Baseline thresholds are defined in DataImportExportManager.Tests/SessionCacheBenchmarkBaselines.cs and currently target:

  • In-memory session cache store/get/consume loop (1000 iterations) ⇐ 5000ms
  • Distributed session cache store/get/consume loop (1000 iterations, in-memory distributed-cache test double) ⇐ 8000ms

When environment performance characteristics change, update threshold constants in that baseline file and keep this README section in sync.

Branch Governance Flow

Repository governance follows: local branch -> remote branch -> PR to dev -> PR to main.

CI Validation Scope

This repository uses CI for validation automation (restore/build/test and documentation guards).

As a referenced class library, this repository does not require a standalone main-delivery pipeline.

Usage

Register all services in your DI container:

using DataImportExportManager.Extensions;

var builder = Host.CreateApplicationBuilder(args);

// Register importers and exporters with default options
builder.Services.AddDataImportExportManager();

// Or configure individual components
builder.Services.AddDataImportExportManager(
    configureExcelImporter: excel =>
    {
        excel.MaxBufferSize = 25 * 1024 * 1024; // 25 MB
        excel.SheetName = "Data";               // import a named sheet
    },
    configureCsvImporter: csv =>
    {
        csv.Delimiter = ';';                    // semicolon-delimited
    },
    configureCsvExporter: csv =>
    {
        csv.SanitizeFormulaCells = true;        // enable injection protection
    },
    configureXmlExporter: xml =>
    {
        xml.UseObjectElementRows = true;        // first row mapped as XML element names
        xml.StrictObjectElementRowWidth = true; // enforce header/data width contract
    },
    configureXmlImporter: xml =>
    {
        xml.RowSchemaMode = XmlImportRowSchemaMode.ObjectElementsOnly; // strict object-row profile
    });

Resolve and route by explicit extension (recommended for UI-selected formats):

// Inject IDataFormatRouter
var importer = router.GetImporter(".csv");
var exporter = router.GetExporter("xlsx");

await using var source      = File.OpenRead("data.csv");
await using var destination = File.Create("data.xlsx");

var rows = await importer.ImportAsync(source);
await exporter.ExportAsync(rows, destination);

Manual Construction (No DI)

All constructors accept optional parameters — no arguments are required:

var csvImporter  = new CsvImporter();
var excelImporter = new ExcelImporter();
var csvExporter  = new CsvExporter();
var excelExporter = new ExcelExporter();

// With options
var tsvImporter = new CsvImporter(new CsvImporterOptions { Delimiter = '\t' });
var sanitizingExporter = new CsvExporter(new CsvExporterOptions { SanitizeFormulaCells = true });
var secondSheetImporter = new ExcelImporter(new ExcelImporterOptions { SheetIndex = 1 });

Import CSV Data Directly

await using var stream = File.OpenRead("data.csv");
IReadOnlyList<IReadOnlyList<string>> rows = await new CsvImporter().ImportAsync(stream);

Import and Extract Field Names for Dynamic Mapping

When the first row represents field names (for example, Excel header row), use schema extraction to keep field names in the imported data while also exposing a reusable column set:

using DataImportExportManager.Extensions;

await using var stream = File.OpenRead("data.xlsx");
var result = await router.ImportWithSchemaAsync(".xlsx", stream);

IReadOnlyList<string> columns = result.Columns; // header/field names
IReadOnlyList<IReadOnlyList<string>> allRows = result.Rows;      // includes header row
IReadOnlyList<IReadOnlyList<string>> dataRows = result.DataRows; // excludes header row

If tuple-based consumption is preferred:

var (headers, dataRows) = await router.ImportWithSchemaTupleAsync(".xlsx", stream);

If both shapes are needed from one call (object + tuple projection):

var (result, headers, dataRows) = await router.ImportWithSchemaBundleAsync(".xlsx", stream);

Validate imported headers against the current table schema before processing data:

var validation = result.ValidateSchema(["CustomerId", "CustomerName", "Email"]);

if (!validation.IsMatch)
{
    // Existing mapping should be removed when mismatch is detected
    // (validation.ShouldDeleteExistingMapping == true)

    // Present available options to the caller/UI:
    // - SchemaMismatchAction.CorrectSourceFile
    // - SchemaMismatchAction.ContinueWithRemap
}

If you want schema mismatch notifications to flow through the same API event publisher (for example, toast triggers), use the async publisher-aware overload:

var validation = await result.ValidateSchemaAsync(
    ["CustomerId", "CustomerName", "Email"],
    eventPublisher,
    cancellationToken: cancellationToken);

For multi-instance deployments, prefer distributed cache backing:

using Microsoft.Extensions.Caching.StackExchangeRedis;

// Register a distributed cache provider first (example: Redis)
services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost:6379";
    options.InstanceName = "dixmgr:";
});

// Register distributed session cache implementation
services.AddDistributedImportSchemaSessionCache(TimeSpan.FromMinutes(20));

To forward library lifecycle notifications (for example, API → UI toast pipelines), implement IDataImportExportEventPublisher in your API and register it before library registration:

using DataImportExportManager.Contracts;
using DataImportExportManager.Interfaces;

public sealed class ApiEventPublisher : IDataImportExportEventPublisher
{
    public ValueTask PublishAsync(DataImportExportEvent notification, CancellationToken cancellationToken = default)
    {
        // Forward notification to your API event bus / SignalR / queue for UI toast handling.
        return ValueTask.CompletedTask;
    }
}

services.AddSingleton<IDataImportExportEventPublisher, ApiEventPublisher>();
services.AddDataImportExportManager();
services.AddImportSchemaSessionCache();

Event Payload Mapping for API → UI Toast Projection

When forwarding DataImportExportEvent notifications from API to UI, use these fields as the default projection contract:

  • EventName → toast category/key (for example import.completed, schema.validation.mismatch)
  • Message → user-facing toast text (or a localization lookup key in your API)
  • OccurredAtUtc → timeline ordering and UI timestamp display
  • Extension → file-format context for import/export events
  • TenantId, SubjectId → optional scoping/filtering in multi-tenant event streams
  • SessionId → remap/retry flow correlation for schema session events
  • DecisionCode, AvailableActions → schema mismatch UX actions (CorrectSourceFile / ContinueWithRemap)

Suggested API projection shape:

public sealed record UiToastEvent(
    string Type,
    string Message,
    DateTimeOffset OccurredAtUtc,
    string? Extension = null,
    string? SessionId = null,
    string? DecisionCode = null,
    IReadOnlyList<string>? Actions = null);

For this library, payload enrichment (for example correlation IDs) should remain optional and be added only when consumer integration evidence requires additional fields.

SaaS Hosting Configuration Boundary

For SaaS deployments, keep configuration ownership split by responsibility:

  • Hosting API passthrough required (external-service integrations):
    • Distributed cache provider connectivity and credentials (for example Redis endpoint/TLS/auth).
    • Event publishing transport integration (for example queue, bus, SignalR hub, webhook dispatch).
    • Tenant-aware routing and security context propagation for outward service calls.
  • Library self-hosted options (internal behavior):
    • Import/export format options (Csv*Options, Excel*Options, Json*Options, Ndjson*Options, Xml*Options).
    • Session-cache behavior options (defaultTtl, distributed cache KeyPrefix, MaxPayloadBytes).
    • Deterministic schema validation and remap workflow behavior.

For customer self-standup scenarios, document these values in your host API configuration guide:

  1. Which external services are required (or optional) per environment.
  2. Which host-level settings are tenant-specific versus environment-global.
  3. Which library options are safe for tenant-level override.
  4. How event notifications are projected from API to UI toast contracts.

The library intentionally does not include direct configuration-provider bindings for external services; consuming applications supply those settings and pass resolved values into registration callbacks.

Configurable Host Services (Provider-Agnostic)

The library is designed so tenant operators can configure host-selected services without modifying this library.

Integration category Library integration surface Host configuration model Current example Future provider swaps
Logging ILogger<T> (Microsoft.Extensions.Logging) Configure logging providers in host app settings/DI Application Insights Serilog, Dynatrace, others supported by host logging pipeline
Distributed cache IDistributedCache + AddDistributedImportSchemaSessionCache(...) Configure distributed cache provider connectivity/credentials in host Redis via AddStackExchangeRedisCache SQL Server cache, NCache, other IDistributedCache providers
Event notifications IDataImportExportEventPublisher Register host publisher implementation with host service settings/credentials API event bus adapter Queue/topic, SignalR, webhook, or other host-selected transport
Customer self-standup expectation

For self-standup, operators should only need to:

  1. Select supported host providers for logging, cache, and event transport.
  2. Supply provider configuration and credentials in the host application.
  3. Register the host services in DI before library registration.

No library fork or core-library source modification should be required to change provider implementations.

Avoid reuploading on mismatch by caching the imported payload in a short-lived tenant-scoped session:

using DataImportExportManager.Interfaces;

// Register once (for example, during app startup):
// services.AddImportSchemaSessionCache(TimeSpan.FromMinutes(20));

var sessionId = await sessionCache.StoreAsync(
    tenantId: "tenant-001",
    subjectId: "user-123",
    importResult: result);

// Later, after the user chooses ContinueWithRemap:
var cachedSession = await sessionCache.ConsumeAsync("tenant-001", "user-123", sessionId);
if (cachedSession is not null)
{
    var cachedImport = cachedSession.ImportResult;
    // Continue remap flow without asking the user to upload the file again.
    // Session is invalidated after this retrieval.
}

Generate Downloadable Example Import Files

Use the router and extension to generate a format-specific example file (CSV/TSV/JSON/NDJSON/XML/XLSX) from one shared code path:

using DataImportExportManager.Extensions;

await using var destination = File.Create("customer-template.csv");
await router.CreateExampleImportFileAsync(
    ".csv",
    ["CustomerId", "CustomerName", "Email"],
    destination);

You can also pass a custom sample row:

await router.CreateExampleImportFileAsync(
    ".json",
    ["CustomerId", "CustomerName"],
    destination,
    ["1001", "Ada Lovelace"]);

Configuration Reference

CsvImporterOptions

Property Default Description
Encoding UTF-8 Text encoding for reading the stream
Delimiter , Field separator character

CsvExporterOptions

Property Default Description
Encoding UTF-8 (no BOM) Text encoding for writing the stream
Delimiter , Field separator character
SanitizeFormulaCells false Prefix formula-triggering chars (=,+,-,@,\t,\r) with '

ExcelImporterOptions

Property Default Description
MaxBufferSize 100 MB Maximum bytes to buffer for non-seekable streams
SheetName null Import a worksheet by name (case-insensitive); takes precedence over SheetIndex
SheetIndex null Import a worksheet by zero-based index

JsonImporterOptions

Property Default Description
IncludeHeaderRowForObjectRecords true For object-record JSON arrays, includes a synthesized deterministic header row

JsonExporterOptions

Property Default Description
WriteIndented false Writes indented JSON output when enabled

NdjsonImporterOptions

Property Default Description
IgnoreBlankLines true Ignores blank/whitespace NDJSON lines; throws when disabled

NdjsonExporterOptions

Property Default Description
WriteTrailingNewline true Writes a final trailing newline after last NDJSON record

XmlImporterOptions

Property Default Description
EnableObjectElementRows true Enables importing rows like <row><name>...</name></row>
IncludeHeaderRowForObjectElementRows true Includes synthesized header row when object-element rows are imported
RowSchemaMode Auto Auto, CellsOnly, or ObjectElementsOnly strict schema contract

XmlExporterOptions

Property Default Description
UseObjectElementRows false Exports using first row as header element names and subsequent row values as elements
StrictObjectElementRowWidth false When object mode is enabled, enforces each data row width equals header count

Adding a New Format

  1. Create a class implementing IDataImporter and/or IDataExporter in the appropriate folder.
  2. Set the SupportedExtension property (e.g., ".json").
  3. Register the new type in ServiceCollectionExtensions.AddDataImportExportManager().
  4. Add unit tests mirroring existing test class structure.

Library Design Principles

This is a class library intended to be referenced by consuming applications. It follows these principles:

  • Single responsibility — The library does exactly two things: read a document and return tabular data; receive tabular data and write a document. Orchestration and format detection are the consumer's responsibility.
  • Abstractions only — Depends on Microsoft.Extensions.Logging.Abstractions and Microsoft.Extensions.DependencyInjection.Abstractions. The consuming application provides the logging pipeline and DI container.
  • Optional loggers — All constructors accept ILogger<T>?; passing null (or using no-arg construction) silently falls back to NullLogger<T>.
  • No infrastructure opinions — Does not include telemetry exporters, hosting, or configuration providers.
  • ConfigureAwait(false) — All await calls use ConfigureAwait(false) to prevent deadlocks in any consumer SynchronizationContext.
  • Configurable resource limitsExcelImporterOptions.MaxBufferSize lets consumers tune memory limits for their hosting tier.

Contract Diagnostics

When using IDataFormatRouter, contract and routing failures are surfaced with a deterministic diagnostic prefix:

  • Format: [DIXMGR:<extension>:<operation>:<code>] <detail>
  • Example: [DIXMGR:.json:IMPORT:CONTRACT] JSON import requires a root array of arrays or objects.

This makes failure handling and telemetry correlation consistent across all supported formats.

Recent diagnostics parity additions include internal machine-readable codes for:

  • CSV/TSV delimiter configuration validation (INVALID_DELIMITER)
  • Excel import validation (INVALID_WORKBOOK, SHEET_NOT_FOUND, BUFFER_LIMIT_EXCEEDED)
  • Excel export validation (INVALID_SHEET_NAME, ROW_LIMIT_EXCEEDED)

Diagnostics Operation Reference (Current)

Operation Meaning
IMPORT Operation occurred while reading/importing source content
EXPORT Operation occurred while writing/exporting destination content
CONFIG Operation occurred while validating configuration/options

Diagnostics Code Reference (Current)

Code Primary Surface Meaning
ROUTE_NOT_FOUND IDataFormatRouter No importer/exporter is registered for the requested extension
CONTRACT IDataFormatRouter Router wrapped a non-prefixed InvalidOperationException from a handler
DUPLICATE_HANDLER IDataFormatRouter Multiple handlers were registered for the same normalized extension
INVALID_DELIMITER CSV/TSV config Delimiter is reserved (", \r, \n)
INVALID_ROOT_KIND JSON import Root/record kind not allowed for expected payload shape
INVALID_RECORD_KIND JSON/NDJSON import Record kind is not valid for the expected import shape
MALFORMED_JSON JSON/NDJSON import JSON payload or NDJSON record line cannot be parsed
MIXED_RECORD_TYPES JSON/NDJSON import Mixed array/object record kinds in one logical dataset
BLANK_LINE NDJSON import Blank line encountered while strict blank-line handling is enabled
INVALID_ROOT XML import Root element does not match required schema or XML is malformed
MALFORMED_XML XML import XML payload cannot be parsed
INVALID_ROW_ELEMENT XML import Non-row element encountered under rows root
SCHEMA_MODE_VIOLATION XML import Row shape violates configured schema mode
OBJECT_ROWS_DISABLED XML import Object-element row mode encountered while disabled
MIXED_ROW_SCHEMAS XML import Mixed cell and object-element row schemas detected
INVALID_HEADER XML export Header name empty/invalid for XML element export mode
DUPLICATE_HEADER XML export Duplicate object-element header names detected
ROW_WIDTH_MISMATCH XML export Strict row-width mode failed header/data column alignment
INVALID_WORKBOOK Excel import Workbook part missing from Excel document
SHEET_NOT_FOUND Excel import Requested worksheet could not be resolved
BUFFER_LIMIT_EXCEEDED Excel import Non-seekable input exceeded configured buffering limit
INVALID_SHEET_INDEX Excel import Negative sheet index provided
INVALID_SHEET_NAME Excel export config Empty/whitespace sheet name provided
ROW_LIMIT_EXCEEDED Excel export Data row count exceeded Excel workbook limit

Diagnostics conformance is validated by a centralized test matrix (DiagnosticsConformanceTests) that asserts stable diagnostic message shape across direct handlers and router paths. Diagnostics metadata is centralized in an internal catalog (ContractDiagnostics) to keep operation/code values consistent across handlers, router wrapping, and tests. ContractDiagnostics also provides operation-scoped builder helpers (BuildImportMessage, BuildExportMessage, BuildConfigMessage) to reduce call-site drift. Unknown diagnostics operations or codes are rejected during message construction to prevent non-catalog values from leaking into runtime telemetry. Enforcement tests also validate that unknown operation/code values are rejected and that all catalog-defined operations/codes remain recognized.

Architecture

The solution follows SOLID principles:

  • Single Responsibility — Each class has one clear purpose (import or export one specific format).
  • Open/Closed — New formats are added by implementing interfaces, not modifying existing code.
  • Interface Segregation — Import and export concerns are separated into distinct interfaces.
  • Dependency Inversion — Services depend on IDataImporter/IDataExporter abstractions.

Configuration Files

File Purpose
.editorconfig Code style, naming conventions, and analyzer severities
Directory.Build.props Shared MSBuild properties across all projects
global.json Pins the .NET SDK version
.github/copilot-instructions.md GitHub Copilot coding conventions
CONTRIBUTING.md Contribution guidelines
SECURITY.md Vulnerability reporting policy
LICENSE MIT license

License

This project is licensed under the MIT License — see LICENSE for details.

A .NET 10 library for importing and exporting tabular data between CSV and Excel (.xlsx) formats using a clean, extensible architecture.

Local Validation

Run the repository-local validation entry point before creating or updating a pull request:

pwsh ./scripts/validate.ps1 -Pack

This repository follows the class-library promotion model. The validation entry point restores, builds, runs tests when test projects exist, and produces the traceable package output used for promotion.

Features

  • CSV import/export — RFC 4180–compliant parsing and writing with quoted field support.
  • Excel import/export — Reads and writes .xlsx files via the DocumentFormat.OpenXml SDK.
  • Format conversion pipeline — Convert between any registered formats through a single DataPipelineService.ConvertAsync call.
  • Format detection — Detect file extensions and query supported formats via IFileFormatDetector.
  • Extensible — Add new formats by implementing IDataImporter or IDataExporter and registering with the pipeline.
  • Structured logging — Zero-allocation [LoggerMessage] source-generated logging with ILogger<T>. The library depends only on logging abstractions — the consuming application configures its own logging pipeline.
  • Configurable optionsExcelImporterOptions allows consumers to tune buffer size limits for their hosting environment.
  • Security hardening — CSV injection prevention, configurable stream size limits, and resource leak protection.
  • High performanceSearchValues<char> SIMD scanning, ArrayPool<byte> buffering, ValueTask with pooling, and pre-allocated collections.

Project Structure

DataImportExportManager/
├── Interfaces/                  # Public contracts
│   ├── IDataImporter.cs         # Stream → tabular data
│   ├── IDataExporter.cs         # Tabular data → stream
│   └── IFileFormatDetector.cs   # Extension detection and support queries
├── Importers/                   # IDataImporter implementations
│   ├── CsvImporter.cs
│   ├── ExcelImporter.cs
│   └── ExcelImporterOptions.cs
├── Exporters/                   # IDataExporter implementations
│   ├── CsvExporter.cs
│   └── ExcelExporter.cs
├── Extensions/                  # DI registration
│   └── ServiceCollectionExtensions.cs
└── Services/                    # Orchestration
    ├── DataPipelineService.cs   # Format-to-format conversion
    └── FileFormatDetector.cs    # IFileFormatDetector implementation

DataImportExportManager.Tests/   # xUnit test project
├── CsvImporterTests.cs
├── CsvExporterTests.cs
├── ExcelImporterTests.cs
├── FileFormatDetectorTests.cs
└── DataPipelineServiceTests.cs

Getting Started

Prerequisites

Build

dotnet build

Run Tests

dotnet test

Usage

Register all services in your DI container:

using DataImportExportManager.Extensions;

var builder = Host.CreateApplicationBuilder(args);

// Register importers, exporters, pipeline, and detector
builder.Services.AddDataImportExportManager();

// Or configure with custom buffer size for memory-constrained environments
builder.Services.AddDataImportExportManager(excel =>
{
    excel.MaxBufferSize = 25 * 1024 * 1024; // 25 MB for Azure App Service Basic tier
});

var app = builder.Build();

Resolve and use the pipeline:

var pipeline = app.Services.GetRequiredService<DataPipelineService>();

await using var source = File.OpenRead("data.csv");
await using var destination = File.Create("data.xlsx");

await pipeline.ConvertAsync(source, ".csv", destination, ".xlsx");

Manual Construction

If not using DI, pass ILogger<T> instances to each constructor:

using Microsoft.Extensions.Logging.Abstractions;

var csvImporter = new CsvImporter(NullLogger<CsvImporter>.Instance);
var excelImporter = new ExcelImporter(NullLogger<ExcelImporter>.Instance, new ExcelImporterOptions());
var csvExporter = new CsvExporter(NullLogger<CsvExporter>.Instance);
var excelExporter = new ExcelExporter(NullLogger<ExcelExporter>.Instance);

IDataImporter[] importers = [csvImporter, excelImporter];
IDataExporter[] exporters = [csvExporter, excelExporter];

var pipeline = new DataPipelineService(importers, exporters, NullLogger<DataPipelineService>.Instance);

await using var source = File.OpenRead("data.csv");
await using var destination = File.Create("data.xlsx");

await pipeline.ConvertAsync(source, ".csv", destination, ".xlsx");

Import CSV Data Directly

var importer = new CsvImporter(NullLogger<CsvImporter>.Instance);

await using var stream = File.OpenRead("data.csv");
IReadOnlyList<IReadOnlyList<string>> rows = await importer.ImportAsync(stream);

Detect File Format

var detector = new FileFormatDetector(importers, exporters, NullLogger<FileFormatDetector>.Instance);

string extension = detector.DetectExtension("report.xlsx");  // ".xlsx"
bool canImport  = detector.IsImportSupported(extension);      // true

Adding a New Format

  1. Create a class implementing IDataImporter and/or IDataExporter in the appropriate folder.
  2. Set the SupportedExtension property to the target extension (e.g., ".json").
  3. Register the new type in ServiceCollectionExtensions.AddDataImportExportManager().
  4. Add unit tests mirroring existing test class structure.
  5. Update this README if the feature list changes.

Library Design Principles

This is a class library intended to be referenced by consuming applications. It follows these principles:

  • Abstractions only — Depends on Microsoft.Extensions.Logging.Abstractions and Microsoft.Extensions.DependencyInjection.Abstractions, not their implementation packages. The consuming application provides the logging pipeline and DI container.
  • No infrastructure opinions — Does not include telemetry exporters, hosting, or configuration providers. The consumer decides where logs go (console, Application Insights, Seq, etc.).
  • ConfigureAwait(false) — All await calls use ConfigureAwait(false) to prevent deadlocks in any consumer SynchronizationContext.
  • Configurable resource limitsExcelImporterOptions.MaxBufferSize lets consumers tune memory limits for their hosting tier.

Architecture

The solution follows SOLID principles:

  • Single Responsibility — Each class has one clear purpose (import, export, detect, orchestrate).
  • Open/Closed — New formats are added by implementing interfaces, not modifying existing code.
  • Interface Segregation — Import and export concerns are separated into distinct interfaces.
  • Dependency Inversion — Services depend on IDataImporter/IDataExporter abstractions, not concrete implementations.

Configuration Files

File Purpose
.editorconfig Code style, naming conventions, and analyzer severities
Directory.Build.props Shared MSBuild properties across all projects
global.json Pins the .NET SDK version
.github/copilot-instructions.md GitHub Copilot coding conventions
CONTRIBUTING.md Contribution guidelines
SECURITY.md Vulnerability reporting policy
LICENSE MIT license

License

This project is licensed under the MIT License — see LICENSE for details.

Product Compatible and additional computed target framework versions.
.NET 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
1.0.0 105 9/7/2026