DataImportExportManager 1.0.0
dotnet add package DataImportExportManager --version 1.0.0
NuGet\Install-Package DataImportExportManager -Version 1.0.0
<PackageReference Include="DataImportExportManager" Version="1.0.0" />
<PackageVersion Include="DataImportExportManager" Version="1.0.0" />
<PackageReference Include="DataImportExportManager" />
paket add DataImportExportManager --version 1.0.0
#r "nuget: DataImportExportManager, 1.0.0"
#:package DataImportExportManager@1.0.0
#addin nuget:?package=DataImportExportManager&version=1.0.0
#tool nuget:?package=DataImportExportManager&version=1.0.0
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
.xlsxfiles via the DocumentFormat.OpenXml SDK with configurable sheet selection. - JSON import/export — Supports
.jsonpayloads usingSystem.Text.Jsonwith deterministic tabular mapping. - NDJSON import/export — Supports line-delimited JSON records (
.ndjsonand.jsonlalias) 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 routing —
IDataFormatRouterresolves 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
IDataImporterorIDataExporterand registering them with the DI container. - Structured logging — Zero-allocation
[LoggerMessage]source-generated logging withILogger<T>. All loggers are optional — the library falls back toNullLogger<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 performance —
SearchValues<char>SIMD scanning,ArrayPool<byte/char>buffers,ValueTaskwith 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
- .NET 10 SDK (pinned via
global.json)
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 (
1000iterations) ⇐5000ms - Distributed session cache store/get/consume loop (
1000iterations, 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
Dependency Injection (Recommended)
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 exampleimport.completed,schema.validation.mismatch)Message→ user-facing toast text (or a localization lookup key in your API)OccurredAtUtc→ timeline ordering and UI timestamp displayExtension→ file-format context for import/export eventsTenantId,SubjectId→ optional scoping/filtering in multi-tenant event streamsSessionId→ remap/retry flow correlation for schema session eventsDecisionCode,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 cacheKeyPrefix,MaxPayloadBytes). - Deterministic schema validation and remap workflow behavior.
- Import/export format options (
For customer self-standup scenarios, document these values in your host API configuration guide:
- Which external services are required (or optional) per environment.
- Which host-level settings are tenant-specific versus environment-global.
- Which library options are safe for tenant-level override.
- 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:
- Select supported host providers for logging, cache, and event transport.
- Supply provider configuration and credentials in the host application.
- 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
- Create a class implementing
IDataImporterand/orIDataExporterin the appropriate folder. - Set the
SupportedExtensionproperty (e.g.,".json"). - Register the new type in
ServiceCollectionExtensions.AddDataImportExportManager(). - 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.AbstractionsandMicrosoft.Extensions.DependencyInjection.Abstractions. The consuming application provides the logging pipeline and DI container. - Optional loggers — All constructors accept
ILogger<T>?; passingnull(or using no-arg construction) silently falls back toNullLogger<T>. - No infrastructure opinions — Does not include telemetry exporters, hosting, or configuration providers.
- ConfigureAwait(false) — All
awaitcalls useConfigureAwait(false)to prevent deadlocks in any consumerSynchronizationContext. - Configurable resource limits —
ExcelImporterOptions.MaxBufferSizelets 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/IDataExporterabstractions.
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
.xlsxfiles via the DocumentFormat.OpenXml SDK. - Format conversion pipeline — Convert between any registered formats through a single
DataPipelineService.ConvertAsynccall. - Format detection — Detect file extensions and query supported formats via
IFileFormatDetector. - Extensible — Add new formats by implementing
IDataImporterorIDataExporterand registering with the pipeline. - Structured logging — Zero-allocation
[LoggerMessage]source-generated logging withILogger<T>. The library depends only on logging abstractions — the consuming application configures its own logging pipeline. - Configurable options —
ExcelImporterOptionsallows consumers to tune buffer size limits for their hosting environment. - Security hardening — CSV injection prevention, configurable stream size limits, and resource leak protection.
- High performance —
SearchValues<char>SIMD scanning,ArrayPool<byte>buffering,ValueTaskwith 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
- .NET 10 SDK (pinned via
global.json)
Build
dotnet build
Run Tests
dotnet test
Usage
Dependency Injection (Recommended)
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
- Create a class implementing
IDataImporterand/orIDataExporterin the appropriate folder. - Set the
SupportedExtensionproperty to the target extension (e.g.,".json"). - Register the new type in
ServiceCollectionExtensions.AddDataImportExportManager(). - Add unit tests mirroring existing test class structure.
- 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.AbstractionsandMicrosoft.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
awaitcalls useConfigureAwait(false)to prevent deadlocks in any consumerSynchronizationContext. - Configurable resource limits —
ExcelImporterOptions.MaxBufferSizelets 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/IDataExporterabstractions, 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 | Versions 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. |
-
net10.0
- DocumentFormat.OpenXml (>= 3.3.0)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.5)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.5)
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 |