DataExportKit 1.0.0
dotnet add package DataExportKit --version 1.0.0
NuGet\Install-Package DataExportKit -Version 1.0.0
<PackageReference Include="DataExportKit" Version="1.0.0" />
<PackageVersion Include="DataExportKit" Version="1.0.0" />
<PackageReference Include="DataExportKit" />
paket add DataExportKit --version 1.0.0
#r "nuget: DataExportKit, 1.0.0"
#:package DataExportKit@1.0.0
#addin nuget:?package=DataExportKit&version=1.0.0
#tool nuget:?package=DataExportKit&version=1.0.0
DataExportKit
GDPR-compliant personal data export, entity data download (CSV/JSON), and user anonymization for .NET — inspired by spatie/laravel-personal-data-export.
Table of Contents
- Overview
- Features
- Packages
- Installation
- Quick Start
- Core Concepts
- Registration
- Personal Data Export
- Exporting Entity Collections
- User Anonymization
- ASP.NET Core Integration
- Advanced Usage
- Target Frameworks
- License
Overview
DataExportKit provides a clean, infrastructure-agnostic foundation for GDPR personal data export and right-to-be-forgotten workflows in .NET applications.
- Infrastructure-agnostic — no EF Core or Dapper dependency. Implement
IDataCollectoragainst any data source. - Storage-agnostic — no file system or blob storage dependency. Consumers decide where to store or stream the result.
- Pluggable — register any number of collectors and anonymization handlers per application.
- Built-in exporters — CSV and JSON exporters with no external libraries.
- ZIP bundling — multiple exported files are bundled into a single in-memory ZIP archive.
Features
- GDPR personal data export — collect all data for a user across multiple entity types and bundle it into a ZIP archive
- Entity data download — export any in-memory collection to CSV or JSON
- User anonymization — run all registered handlers to fulfill a right-to-be-forgotten request
- CSV export with configurable delimiter, header control, and property exclusion
- JSON export with configurable
System.Text.Jsonserializer options - In-memory ZIP bundling with optional root folder nesting
- ASP.NET Core minimal API endpoint helper
- SourceLink support for debuggable NuGet packages
Packages
| Package | Description | Target Frameworks |
|---|---|---|
DataExportKit |
Core library — interfaces, exporters, bundler, DI registration | net8.0, net9.0, net10.0 |
DataExportKit.AspNetCore |
ASP.NET Core integration — minimal API endpoint helpers | net8.0, net10.0 |
Installation
dotnet add package DataExportKit
For ASP.NET Core minimal API endpoint helpers:
dotnet add package DataExportKit.AspNetCore
Quick Start
1. Register services
builder.Services.AddDataExportKit(options =>
{
options.Register<UserDataCollector>();
options.Register<OrderDataCollector>();
options.RegisterAnonymizer<UserAnonymizer>();
options.RegisterAnonymizer<OrderAnonymizer>();
});
2. Implement a collector
public class UserDataCollector(IUserRepository userRepo) : IDataCollector
{
public string FileName => "user-profile.json";
public async Task<ExportData> CollectAsync(string userId, CancellationToken ct)
{
var user = await userRepo.GetByIdAsync(userId, ct);
return ExportData.Json(FileName, new
{
user.Email,
user.DisplayName,
user.CreatedAt
});
}
}
3. Trigger a personal data export
public class ExportController(IDataExporter exporter) : ControllerBase
{
[HttpGet("export")]
public async Task<IActionResult> ExportMyData(CancellationToken ct)
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)!;
var result = await exporter.CreatePersonalDataExportAsync(userId, ct);
return File(result.ZipContent, "application/zip", result.FileName);
}
}
Core Concepts
IDataCollector
Implement one IDataCollector per entity type to describe how that entity's data should be collected and formatted.
public interface IDataCollector
{
string FileName { get; }
Task<ExportData> CollectAsync(string userId, CancellationToken ct = default);
}
FileName becomes the name of the file inside the ZIP archive. Use descriptive names with the correct extension:
public string FileName => "orders.csv";
public string FileName => "user-profile.json";
IDataExporter
IDataExporter is the main entry point — resolved from DI. It orchestrates all registered collectors and can also export an arbitrary in-memory collection to CSV or JSON.
public interface IDataExporter
{
Task<ExportResult> CreatePersonalDataExportAsync(string userId, CancellationToken ct = default);
Task<ExportData> ExportAsync<T>(
IEnumerable<T> data,
ExportFormat format,
string? fileName = null,
CancellationToken ct = default);
}
IUserAnonymizer and IAnonymizationHandler
IUserAnonymizer orchestrates GDPR right-to-be-forgotten by calling every registered IAnonymizationHandler for the given user.
public interface IUserAnonymizer
{
Task AnonymizeAsync(string userId, CancellationToken ct = default);
}
public interface IAnonymizationHandler
{
Task AnonymizeUserDataAsync(string userId, CancellationToken ct = default);
}
Implement one IAnonymizationHandler per entity type:
public class UserAnonymizer(IUserRepository users) : IAnonymizationHandler
{
public async Task AnonymizeUserDataAsync(string userId, CancellationToken ct)
{
var user = await users.GetByIdAsync(userId, ct);
user.Email = $"deleted-{userId}@anonymized.invalid";
user.DisplayName = "Deleted User";
await users.SaveAsync(user, ct);
}
}
ExportData
Represents a single exported file with its raw bytes and content type. Use the static factory methods for convenience:
// JSON
ExportData.Json("profile.json", myObject);
// CSV
ExportData.Csv("orders.csv", rows);
// Manual
new ExportData
{
FileName = "custom.bin",
Content = bytes,
ContentType = "application/octet-stream"
};
ExportResult
Returned from CreatePersonalDataExportAsync. Contains the ZIP bytes and metadata:
public sealed class ExportResult
{
public string FileName { get; init; } // e.g. "personal-data-export-2026-03-24.zip"
public byte[] ZipContent { get; init; }
public DateTimeOffset CreatedAt { get; init; }
}
CsvExporter
Static utility for CSV generation. Used internally by ExportData.Csv and IDataExporter.ExportAsync.
var bytes = CsvExporter.Export(rows);
var bytes = CsvExporter.Export(rows, new CsvExportOptions
{
Delimiter = ";",
IncludeHeaders = true,
ExcludeProperties = ["InternalId", "PasswordHash"]
});
JsonExporter
Static utility for JSON generation using System.Text.Json. Used internally by ExportData.Json and IDataExporter.ExportAsync.
// Array
var bytes = JsonExporter.Export(rows);
// Single object
var bytes = JsonExporter.ExportSingle(myObject);
// With options
var bytes = JsonExporter.ExportSingle(myObject, new JsonExportOptions
{
SerializerOptions = new JsonSerializerOptions { WriteIndented = false }
});
ZipBundler
Static utility for in-memory ZIP creation. Used internally by CreatePersonalDataExportAsync.
var zipBytes = ZipBundler.Bundle(exportDataFiles);
// With a root folder
var zipBytes = ZipBundler.Bundle(exportDataFiles, rootFolder: "my-export");
Registration
All registration goes through AddDataExportKit:
services.AddDataExportKit(options =>
{
// Register one IDataCollector per entity type
options.Register<UserDataCollector>();
options.Register<OrderDataCollector>();
options.Register<ActivityLogCollector>();
// Register one IAnonymizationHandler per entity type
options.RegisterAnonymizer<UserAnonymizer>();
options.RegisterAnonymizer<OrderAnonymizer>();
});
Collectors and handlers are resolved from the DI container, so they can declare constructor dependencies on any registered service.
Personal Data Export
Call IDataExporter.CreatePersonalDataExportAsync with the user's identifier. All registered IDataCollector implementations are called in parallel, their results are bundled into a ZIP, and an ExportResult is returned.
var result = await dataExporter.CreatePersonalDataExportAsync(userId);
// Stream to browser
return File(result.ZipContent, "application/zip", result.FileName);
// Or save to blob storage
await blobStorage.UploadAsync(result.FileName, result.ZipContent);
The ZIP file name follows the format personal-data-export-{date}.zip, e.g. personal-data-export-2026-03-24.zip.
Exporting Entity Collections
Use IDataExporter.ExportAsync to export any in-memory collection without building a full collector:
// Export to CSV
var csvExport = await dataExporter.ExportAsync(orders, ExportFormat.Csv, "orders.csv");
// Export to JSON
var jsonExport = await dataExporter.ExportAsync(invoices, ExportFormat.Json);
// Use the result bytes
return File(csvExport.Content, csvExport.ContentType, csvExport.FileName);
User Anonymization
Inject IUserAnonymizer and call AnonymizeAsync to run all registered handlers:
public class GdprController(IUserAnonymizer anonymizer) : ControllerBase
{
[HttpDelete("me")]
public async Task<IActionResult> DeleteMyAccount(CancellationToken ct)
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)!;
await anonymizer.AnonymizeAsync(userId, ct);
return NoContent();
}
}
Handlers run sequentially in the order they were registered, so you can control the execution order if handlers have dependencies (e.g. anonymize child records before parent records).
ASP.NET Core Integration
Install DataExportKit.AspNetCore for the minimal API endpoint helper:
app.MapPersonalDataExport("/api/personal-data-export");
The endpoint:
- Requires authorization (
RequireAuthorization()is called internally) - Resolves the user ID from the
subclaim orClaimTypes.NameIdentifier - Returns the ZIP as a file download response
Advanced Usage
CSV Options
var options = new CsvExportOptions
{
Delimiter = ";", // use semicolon instead of comma
IncludeHeaders = false, // omit the header row
ExcludeProperties = ["PasswordHash", "SecurityStamp"]
};
var bytes = CsvExporter.Export(users, options);
Fields containing the delimiter, double quotes, or newlines are automatically quoted and escaped per RFC 4180.
JSON Options
var options = new JsonExportOptions
{
SerializerOptions = new JsonSerializerOptions
{
WriteIndented = false,
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
}
};
var bytes = JsonExporter.Export(items, options);
When no options are provided the defaults apply: camelCase property names, indented output, and null values omitted.
Custom ZIP Root Folder
var zipBytes = ZipBundler.Bundle(files, rootFolder: "alice-export");
// ZIP entries: alice-export/profile.json, alice-export/orders.csv
Target Frameworks
| Package | net8.0 | net9.0 | net10.0 |
|---|---|---|---|
DataExportKit |
Yes | Yes | Yes |
DataExportKit.AspNetCore |
Yes | — | Yes |
License
MIT — see LICENSE for details.
| Product | Versions 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. |
-
net10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.3)
- Microsoft.Extensions.Options (>= 10.0.3)
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.3)
- Microsoft.Extensions.Options (>= 9.0.3)
-
net9.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.3)
- Microsoft.Extensions.Options (>= 9.0.3)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on DataExportKit:
| Package | Downloads |
|---|---|
|
DataExportKit.AspNetCore
ASP.NET Core integration for DataExportKit — endpoint helpers and HttpContext-aware DI extensions. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 129 | 3/26/2026 |